Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading