-
Notifications
You must be signed in to change notification settings - Fork 81
add http end points related to tasks #690
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,4 +36,5 @@ type Client interface { | |
ClientAdmin | ||
ClientAsyncJob | ||
ClientFoxx | ||
ClientTasks | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// DISCLAIMER | ||
// | ||
// # Copyright 2024 ArangoDB GmbH, Cologne, Germany | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
// Copyright holder is ArangoDB GmbH, Cologne, Germany | ||
|
||
package arangodb | ||
|
||
import ( | ||
"context" | ||
) | ||
|
||
// ClientTasks defines the interface for managing tasks in ArangoDB. | ||
type ClientTasks interface { | ||
// Task retrieves an existing task by its ID. | ||
// If no task with the given ID exists, a NotFoundError is returned. | ||
Task(ctx context.Context, id string) (Task, error) | ||
|
||
// Tasks returns a list of all tasks on the server. | ||
Tasks(ctx context.Context) ([]Task, error) | ||
|
||
// CreateTask creates a new task with the specified options. | ||
CreateTask(ctx context.Context, options *TaskOptions) (Task, error) | ||
|
||
// If a task with the given ID already exists, a Conflict error is returned. | ||
CreateTaskWithID(ctx context.Context, id string, options *TaskOptions) (Task, error) | ||
|
||
// RemoveTask deletes an existing task by its ID. | ||
RemoveTask(ctx context.Context, id string) error | ||
} | ||
|
||
// TaskOptions contains options for creating a new task. | ||
type TaskOptions struct { | ||
// ID is an optional identifier for the task. | ||
ID string `json:"id,omitempty"` | ||
// Name is an optional name for the task. | ||
Name string `json:"name,omitempty"` | ||
|
||
// Command is the JavaScript code to be executed. | ||
Command string `json:"command"` | ||
|
||
// Params are optional parameters passed to the command. | ||
Params interface{} `json:"params,omitempty"` | ||
|
||
// Period is the interval (in seconds) at which the task runs periodically. | ||
// If zero, the task runs once after the offset. | ||
Period int64 `json:"period,omitempty"` | ||
|
||
// Offset is the delay (in milliseconds) before the task is first executed. | ||
Offset float64 `json:"offset,omitempty"` | ||
} | ||
|
||
// Task provides access to a single task on the server. | ||
type Task interface { | ||
// ID returns the ID of the task. | ||
ID() string | ||
|
||
// Name returns the name of the task. | ||
Name() string | ||
|
||
// Command returns the JavaScript code of the task. | ||
Command() string | ||
|
||
// Params returns the parameters of the task. | ||
Params(result interface{}) error | ||
|
||
// Period returns the period (in seconds) of the task. | ||
Period() int64 | ||
|
||
// Offset returns the offset (in milliseconds) of the task. | ||
Offset() float64 | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,227 @@ | ||||||||||||||
// DISCLAIMER | ||||||||||||||
// | ||||||||||||||
// Copyright 2024 ArangoDB GmbH, Cologne, Germany | ||||||||||||||
// | ||||||||||||||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||||||||||||||
// you may not use this file except in compliance with the License. | ||||||||||||||
// You may obtain a copy of the License at | ||||||||||||||
// | ||||||||||||||
// http://www.apache.org/licenses/LICENSE-2.0 | ||||||||||||||
// | ||||||||||||||
// Unless required by applicable law or agreed to in writing, software | ||||||||||||||
// distributed under the License is distributed on an "AS IS" BASIS, | ||||||||||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||||||||||
// See the License for the specific language governing permissions and | ||||||||||||||
// limitations under the License. | ||||||||||||||
// | ||||||||||||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany | ||||||||||||||
// | ||||||||||||||
|
||||||||||||||
package arangodb | ||||||||||||||
|
||||||||||||||
import ( | ||||||||||||||
"context" | ||||||||||||||
"encoding/json" | ||||||||||||||
"fmt" | ||||||||||||||
"net/http" | ||||||||||||||
"net/url" | ||||||||||||||
|
||||||||||||||
"github.com/pkg/errors" | ||||||||||||||
|
||||||||||||||
"github.com/arangodb/go-driver/v2/arangodb/shared" | ||||||||||||||
"github.com/arangodb/go-driver/v2/connection" | ||||||||||||||
) | ||||||||||||||
|
||||||||||||||
// newClientTask initializes a new task client with the given database name. | ||||||||||||||
func newClientTask(client *client) *clientTask { | ||||||||||||||
return &clientTask{ | ||||||||||||||
client: client, | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
// will check all methods in ClientTasks are implemented with the clientTask struct. | ||||||||||||||
var _ ClientTasks = &clientTask{} | ||||||||||||||
|
||||||||||||||
type clientTask struct { | ||||||||||||||
client *client | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
type taskResponse struct { | ||||||||||||||
ID string `json:"id,omitempty"` | ||||||||||||||
Name string `json:"name,omitempty"` | ||||||||||||||
Command string `json:"command,omitempty"` | ||||||||||||||
Params json.RawMessage `json:"params,omitempty"` | ||||||||||||||
Period int64 `json:"period,omitempty"` | ||||||||||||||
Offset float64 `json:"offset,omitempty"` | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
func newTask(client *client, resp *taskResponse) Task { | ||||||||||||||
return &task{ | ||||||||||||||
client: client, | ||||||||||||||
id: resp.ID, | ||||||||||||||
name: resp.Name, | ||||||||||||||
command: resp.Command, | ||||||||||||||
params: resp.Params, | ||||||||||||||
period: resp.Period, | ||||||||||||||
offset: resp.Offset, | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
type task struct { | ||||||||||||||
client *client | ||||||||||||||
id string | ||||||||||||||
name string | ||||||||||||||
command string | ||||||||||||||
params json.RawMessage | ||||||||||||||
period int64 | ||||||||||||||
offset float64 | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
func (t *task) ID() string { | ||||||||||||||
return t.id | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
func (t *task) Name() string { | ||||||||||||||
return t.name | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
func (t *task) Command() string { | ||||||||||||||
return t.command | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
func (t *task) Params(result interface{}) error { | ||||||||||||||
if t.params == nil { | ||||||||||||||
return nil | ||||||||||||||
} | ||||||||||||||
return json.Unmarshal(t.params, result) | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
func (t *task) Period() int64 { | ||||||||||||||
return t.period | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
func (t *task) Offset() float64 { | ||||||||||||||
return t.offset | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
func (c clientTask) Tasks(ctx context.Context) ([]Task, error) { | ||||||||||||||
urlEndpoint := connection.NewUrl("_api", "tasks") // Note: This should include database context, see below | ||||||||||||||
response := make([]taskResponse, 0) // Direct array response | ||||||||||||||
resp, err := connection.CallGet(ctx, c.client.connection, urlEndpoint, &response) | ||||||||||||||
if err != nil { | ||||||||||||||
return nil, errors.WithStack(err) | ||||||||||||||
} | ||||||||||||||
switch code := resp.Code(); code { | ||||||||||||||
case http.StatusOK: | ||||||||||||||
result := make([]Task, len(response)) | ||||||||||||||
for i, task := range response { | ||||||||||||||
fmt.Printf("Task %d: %+v\n", i, task) | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nitpick] This debug Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||
result[i] = newTask(c.client, &task) | ||||||||||||||
} | ||||||||||||||
return result, nil | ||||||||||||||
default: | ||||||||||||||
// Attempt to get error details from response headers or body | ||||||||||||||
return nil, shared.NewResponseStruct().AsArangoErrorWithCode(code) | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
func (c clientTask) Task(ctx context.Context, id string) (Task, error) { | ||||||||||||||
urlEndpoint := connection.NewUrl("_api", "tasks", url.PathEscape(id)) | ||||||||||||||
|
||||||||||||||
response := struct { | ||||||||||||||
taskResponse `json:",inline"` | ||||||||||||||
shared.ResponseStruct `json:",inline"` | ||||||||||||||
}{} | ||||||||||||||
|
||||||||||||||
resp, err := connection.CallGet(ctx, c.client.connection, urlEndpoint, &response) | ||||||||||||||
if err != nil { | ||||||||||||||
return nil, errors.WithStack(err) | ||||||||||||||
} | ||||||||||||||
switch code := resp.Code(); code { | ||||||||||||||
case http.StatusOK: | ||||||||||||||
return newTask(c.client, &response.taskResponse), nil | ||||||||||||||
default: | ||||||||||||||
return nil, response.AsArangoError() | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
func (c clientTask) CreateTask(ctx context.Context, options *TaskOptions) (Task, error) { | ||||||||||||||
var urlEndpoint string | ||||||||||||||
if options.ID != "" { | ||||||||||||||
urlEndpoint = connection.NewUrl("_api", "tasks", url.PathEscape(options.ID)) | ||||||||||||||
} else { | ||||||||||||||
urlEndpoint = connection.NewUrl("_api", "tasks") | ||||||||||||||
} | ||||||||||||||
// Prepare the request body | ||||||||||||||
createRequest := struct { | ||||||||||||||
ID string `json:"id,omitempty"` | ||||||||||||||
Name string `json:"name,omitempty"` | ||||||||||||||
Command string `json:"command,omitempty"` | ||||||||||||||
Params json.RawMessage `json:"params,omitempty"` | ||||||||||||||
Period int64 `json:"period,omitempty"` | ||||||||||||||
Offset float64 `json:"offset,omitempty"` | ||||||||||||||
}{ | ||||||||||||||
ID: options.ID, | ||||||||||||||
Name: options.Name, | ||||||||||||||
Command: options.Command, | ||||||||||||||
Period: options.Period, | ||||||||||||||
Offset: options.Offset, | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
if options.Params != nil { | ||||||||||||||
raw, err := json.Marshal(options.Params) | ||||||||||||||
if err != nil { | ||||||||||||||
return nil, errors.WithStack(err) | ||||||||||||||
} | ||||||||||||||
createRequest.Params = raw | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
response := struct { | ||||||||||||||
shared.ResponseStruct `json:",inline"` | ||||||||||||||
taskResponse `json:",inline"` | ||||||||||||||
}{} | ||||||||||||||
|
||||||||||||||
resp, err := connection.CallPost(ctx, c.client.connection, urlEndpoint, &response, &createRequest) | ||||||||||||||
if err != nil { | ||||||||||||||
return nil, errors.WithStack(err) | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
switch code := resp.Code(); code { | ||||||||||||||
case http.StatusCreated, http.StatusOK: | ||||||||||||||
return newTask(c.client, &response.taskResponse), nil | ||||||||||||||
default: | ||||||||||||||
return nil, response.AsArangoError() | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
func (c clientTask) RemoveTask(ctx context.Context, id string) error { | ||||||||||||||
urlEndpoint := connection.NewUrl("_api", "tasks", url.PathEscape(id)) | ||||||||||||||
|
||||||||||||||
resp, err := connection.CallDelete(ctx, c.client.connection, urlEndpoint, nil) | ||||||||||||||
if err != nil { | ||||||||||||||
return err | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
switch code := resp.Code(); code { | ||||||||||||||
case http.StatusAccepted, http.StatusOK: | ||||||||||||||
return nil | ||||||||||||||
default: | ||||||||||||||
return shared.NewResponseStruct().AsArangoErrorWithCode(code) | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
func (c clientTask) CreateTaskWithID(ctx context.Context, id string, options *TaskOptions) (Task, error) { | ||||||||||||||
// Check if task already exists | ||||||||||||||
existingTask, err := c.Task(ctx, id) | ||||||||||||||
fmt.Printf("Checking existing task with ID: %s, existingTask: %v, Error:%v", id, existingTask, err) | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nitpick] This debug
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||
if err == nil && existingTask != nil { | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When checking for an existing task, only a NotFound error should be ignored. Currently any error lets creation proceed, potentially hiding real failures. Explicitly detect NotFound and propagate other errors.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||
return nil, &shared.ArangoError{ | ||||||||||||||
Code: http.StatusConflict, | ||||||||||||||
ErrorMessage: fmt.Sprintf("Task with ID %s already exists", id), | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
// Set the ID and call CreateTask | ||||||||||||||
options.ID = id | ||||||||||||||
return c.CreateTask(ctx, options) | ||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The endpoint URL lacks the database context, so operations may target the system database. Use a helper to include the current database in the path (e.g.,
_db/{dbName}/_api/tasks
).Copilot uses AI. Check for mistakes.