Skip to content
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

Add cancel task api for v0.30.0 #395

Merged
merged 8 commits into from Dec 14, 2022
31 changes: 31 additions & 0 deletions client.go
Expand Up @@ -54,6 +54,7 @@ type ClientInterface interface {
IsHealthy() bool
GetTask(taskUID int64) (resp *Task, err error)
GetTasks(param *TasksQuery) (resp *TaskResult, err error)
CancelTasks(param *CancelTasksQuery) (resp *TaskInfo, err error)
WaitForTask(taskUID int64, options ...WaitParams) (*Task, error)
GenerateTenantToken(APIKeyUID string, searchRules map[string]interface{}, options *TenantTokenOptions) (resp string, err error)
}
Expand Down Expand Up @@ -285,6 +286,36 @@ func (c *Client) GetTasks(param *TasksQuery) (resp *TaskResult, err error) {
return resp, nil
}

func (c *Client) CancelTasks(param *CancelTasksQuery) (resp *TaskInfo, err error) {
resp = &TaskInfo{}
req := internalRequest{
endpoint: "/tasks/cancel",
method: http.MethodPost,
withRequest: nil,
withResponse: &resp,
withQueryParams: map[string]string{},
acceptedStatusCodes: []int{http.StatusOK},
functionName: "CancelTasks",
}
if param != nil {
paramToSend := &TasksQuery{
UIDS: param.UIDS,
IndexUIDS: param.IndexUIDS,
Statuses: param.Statuses,
Types: param.Types,
BeforeEnqueuedAt: param.BeforeEnqueuedAt,
AfterEnqueuedAt: param.AfterEnqueuedAt,
BeforeStartedAt: param.BeforeStartedAt,
AfterStartedAt: param.AfterStartedAt,
}
encodeTasksQuery(paramToSend, &req)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you make this function encodeTasksQuery support multiple sets of params?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really, I could put interface{} as a parameter instead of a typed struct, but I wouldn't have access to the fields easily.

}
if err := c.executeRequest(req); err != nil {
return nil, err
}
return resp, nil
}

// WaitForTask waits for a task to be processed
//
// The function will check by regular interval provided in parameter interval
Expand Down
138 changes: 136 additions & 2 deletions client_test.go
Expand Up @@ -2,6 +2,8 @@ package meilisearch

import (
"context"
"strings"

"sync"
"testing"
"time"
Expand Down Expand Up @@ -655,8 +657,8 @@ func TestClient_GetTask(t *testing.T) {
require.GreaterOrEqual(t, gotResp.UID, tt.args.taskUID)
require.Equal(t, tt.args.UID, gotResp.IndexUID)
require.Equal(t, TaskStatusSucceeded, gotResp.Status)
require.Equal(t, len(tt.args.document), gotResp.Details.ReceivedDocuments)
require.Equal(t, len(tt.args.document), gotResp.Details.IndexedDocuments)
require.Equal(t, int64(len(tt.args.document)), gotResp.Details.ReceivedDocuments)
require.Equal(t, int64(len(tt.args.document)), gotResp.Details.IndexedDocuments)

// Make sure that timestamps are also retrieved
require.NotZero(t, gotResp.EnqueuedAt)
Expand Down Expand Up @@ -827,6 +829,138 @@ func TestClient_GetTasks(t *testing.T) {
}
}

func TestClient_CancelTasks(t *testing.T) {
type args struct {
UID string
client *Client
query *CancelTasksQuery
}
tests := []struct {
name string
args args
want string
}{
{
name: "TestCancelTasksWithNoFilters",
args: args{
UID: "indexUID",
client: defaultClient,
query: nil,
},
want: "",
},
{
name: "TestCancelTasksWithStatutes",
args: args{
UID: "indexUID",
client: defaultClient,
query: &CancelTasksQuery{
Statuses: []string{"succeeded"},
},
},
want: "?statuses=succeeded",
},
{
name: "TestCancelTasksWithIndexUidFilter",
args: args{
UID: "indexUID",
client: defaultClient,
query: &CancelTasksQuery{
IndexUIDS: []string{"0"},
},
},
want: "?indexUids=0",
},
{
name: "TestCancelTasksWithMultipleIndexUidsFilter",
args: args{
UID: "indexUID",
client: defaultClient,
query: &CancelTasksQuery{
IndexUIDS: []string{"0", "1"},
},
},
want: "?indexUids=0%2C1",
},
{
name: "TestCancelTasksWithUidFilter",
args: args{
UID: "indexUID",
client: defaultClient,
query: &CancelTasksQuery{
UIDS: []int64{0},
},
},
want: "?uids=0",
},
{
name: "TestCancelTasksWithMultipleUidsFilter",
args: args{
UID: "indexUID",
client: defaultClient,
query: &CancelTasksQuery{
UIDS: []int64{0, 1},
},
},
want: "?uids=0%2C1",
},
{
name: "TestCancelTasksWithDateFilter",
args: args{
UID: "indexUID",
client: defaultClient,
query: &CancelTasksQuery{
BeforeEnqueuedAt: time.Now(),
},
},
want: strings.NewReplacer(":", "%3A").Replace("?beforeEnqueuedAt=" + time.Now().Format("2006-01-02T15:04:05Z")),
},
{
name: "TestCancelTasksWithParameters",
args: args{
UID: "indexUID",
client: defaultClient,
query: &CancelTasksQuery{
Statuses: []string{"enqueued"},
IndexUIDS: []string{"indexUID"},
UIDS: []int64{1},
AfterEnqueuedAt: time.Now(),
},
},
want: "?afterEnqueuedAt=" + strings.NewReplacer(":", "%3A").Replace(time.Now().Format("2006-01-02T15:04:05Z")) + "&indexUids=indexUID&statuses=enqueued&uids=1",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := tt.args.client
t.Cleanup(cleanup(c))

gotResp, err := c.CancelTasks(tt.args.query)
if tt.args.query == nil {
require.Error(t, err)
require.Equal(t, "missing_task_filters",
err.(*Error).MeilisearchApiError.Code)
} else {
require.NoError(t, err)

_, err = c.WaitForTask(gotResp.TaskUID)
require.NoError(t, err)

gotTask, err := c.GetTask(gotResp.TaskUID)
require.NoError(t, err)

require.NotNil(t, gotResp.Status)
require.NotNil(t, gotResp.Type)
require.NotNil(t, gotResp.TaskUID)
require.NotNil(t, gotResp.EnqueuedAt)
require.Equal(t, "", gotResp.IndexUID)
require.Equal(t, "taskCancelation", gotResp.Type)
require.Equal(t, tt.want, gotTask.Details.OriginalFilter)
}
})
}
}

func TestClient_DefaultWaitForTask(t *testing.T) {
type args struct {
UID string
Expand Down
47 changes: 33 additions & 14 deletions types.go
Expand Up @@ -130,25 +130,21 @@ type Task struct {
StartedAt time.Time `json:"startedAt,omitempty"`
FinishedAt time.Time `json:"finishedAt,omitempty"`
Details Details `json:"details,omitempty"`
CanceledBy int64 `json:"canceledBy,omitempty"`
}

// TaskInfo indicates information regarding a task returned by an asynchronous method
//
// Documentation: https://docs.meilisearch.com/reference/api/tasks.html#tasks
type TaskInfo struct {
Status TaskStatus `json:"status"`
TaskUID int64 `json:"taskUid,omitempty"`
IndexUID string `json:"indexUid"`
Type string `json:"type"`
Error meilisearchApiError `json:"error,omitempty"`
Duration string `json:"duration,omitempty"`
EnqueuedAt time.Time `json:"enqueuedAt"`
StartedAt time.Time `json:"startedAt,omitempty"`
FinishedAt time.Time `json:"finishedAt,omitempty"`
Details Details `json:"details,omitempty"`
Status TaskStatus `json:"status"`
TaskUID int64 `json:"taskUid"`
IndexUID string `json:"indexUid"`
Type string `json:"type"`
EnqueuedAt time.Time `json:"enqueuedAt"`
}

// TasksQuery is the request body for list documents method
// TasksQuery is a list of filter available to send as query parameters
type TasksQuery struct {
UIDS []int64
Limit int64
Expand All @@ -165,16 +161,39 @@ type TasksQuery struct {
AfterFinishedAt time.Time
}

// CancelTasksQuery is a list of filter available to send as query parameters
type CancelTasksQuery struct {
UIDS []int64
IndexUIDS []string
Statuses []string
Types []string
BeforeEnqueuedAt time.Time
AfterEnqueuedAt time.Time
BeforeStartedAt time.Time
AfterStartedAt time.Time
}
Comment on lines +165 to +174
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
type CancelTasksQuery struct {
UIDS []int64
IndexUIDS []string
Statuses []string
Types []string
BeforeEnqueuedAt time.Time
AfterEnqueuedAt time.Time
BeforeStartedAt time.Time
AfterStartedAt time.Time
}
type CancelTasksQuery struct {
UIDS []int64
IndexUIDS []string
Statuses []string
Types []string
BeforeEnqueuedAt time.Time
AfterEnqueuedAt time.Time
BeforeStartedAt time.Time
AfterStartedAt time.Time
BeforeFinishedAt time.Time
AfterFinishedAt time.Time
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't add those two filters because they didn't make sense, and they are not in the parameters available in the documentation. Are you sure it's a good think to add it?


type Details struct {
ReceivedDocuments int `json:"receivedDocuments,omitempty"`
IndexedDocuments int `json:"indexedDocuments,omitempty"`
DeletedDocuments int `json:"deletedDocuments,omitempty"`
ReceivedDocuments int64 `json:"receivedDocuments,omitempty"`
IndexedDocuments int64 `json:"indexedDocuments,omitempty"`
DeletedDocuments int64 `json:"deletedDocuments,omitempty"`
PrimaryKey string `json:"primaryKey,omitempty"`
ProvidedIds int64 `json:"providedIds,omitempty"`
RankingRules []string `json:"rankingRules,omitempty"`
DistinctAttribute *string `json:"distinctAttribute,omitempty"`
SearchableAttributes []string `json:"searchableAttributes,omitempty"`
DisplayedAttributes []string `json:"displayedAttributes,omitempty"`
StopWords []string `json:"stopWords,omitempty"`
Synonyms map[string][]string `json:"synonyms,omitempty"`
FilterableAttributes []string `json:"filterableAttributes,omitempty"`
SortableAttributes []string `json:"sortableAttributes,omitempty"`
TypoTolerance *TypoTolerance `json:"typoTolerance,omitempty"`
Pagination *Pagination `json:"pagination,omitempty"`
Faceting *Faceting `json:"faceting,omitempty"`
MatchedTasks int64 `json:"matchedTasks,omitempty"`
CanceledTasks int64 `json:"canceledTasks,omitempty"`
DeletedTasks int64 `json:"deletedTasks,omitempty"`
OriginalFilter string `json:"originalFilter,omitempty"`
}

// Return of multiple tasks is wrap in a TaskResult
Expand Down