From cb95dc1371f43a53fce047de02d7161f3598ee8e Mon Sep 17 00:00:00 2001 From: Anatoy Nosov Date: Sun, 8 Aug 2021 13:18:21 +0500 Subject: [PATCH] Filter reworked --- database.go | 29 +++++++++++-- database_test.go | 99 ++++++++++++++++++++++++++++++++++++++++-- filter.go | 111 ++++++++++++++++++++++++++++++++++++++++------- object.go | 12 +++++ search.go | 2 +- sort.go | 4 +- 6 files changed, 231 insertions(+), 26 deletions(-) diff --git a/database.go b/database.go index fcc5a4b..f30de36 100644 --- a/database.go +++ b/database.go @@ -93,10 +93,31 @@ type DatabaseListResponse struct { } type DatabaseQueryRequest struct { - Filter Filter `json:"filter,omitempty"` - Sorts []SortObject `json:"sorts"` - StartCursor Cursor `json:"start_cursor,omitempty"` - PageSize int `json:"page_size,omitempty"` + PropertyFilter *PropertyFilter + CompoundFilter *CompoundFilter + Sorts []SortObject `json:"sorts,omitempty"` + StartCursor Cursor `json:"start_cursor,omitempty"` + PageSize int `json:"page_size,omitempty"` +} + +func (qr *DatabaseQueryRequest) MarshalJSON() ([]byte, error) { + var filter interface{} + if qr.PropertyFilter != nil { + filter = qr.PropertyFilter + } else { + filter = qr.CompoundFilter + } + return json.Marshal(struct { + Sorts []SortObject `json:"sorts,omitempty"` + StartCursor Cursor `json:"start_cursor,omitempty"` + PageSize int `json:"page_size,omitempty"` + Filter interface{} `json:"filter"` + }{ + Sorts: qr.Sorts, + StartCursor: qr.StartCursor, + PageSize: qr.PageSize, + Filter: filter, + }) } type DatabaseQueryResponse struct { diff --git a/database_test.go b/database_test.go index 8caa19f..a8108c8 100644 --- a/database_test.go +++ b/database_test.go @@ -166,10 +166,10 @@ func TestDatabaseClient(t *testing.T) { filePath: "testdata/database_query.json", statusCode: http.StatusOK, request: ¬ionapi.DatabaseQueryRequest{ - Filter: ¬ionapi.PropertyFilter{ + PropertyFilter: ¬ionapi.PropertyFilter{ Property: "Name", - Text: map[notionapi.Condition]string{ - notionapi.ConditionContains: "Hel", + Text: ¬ionapi.TextFilterCondition{ + Contains: "Hel", }, }, }, @@ -213,3 +213,96 @@ func TestDatabaseClient(t *testing.T) { } }) } + +func TestDatabaseQueryRequest_MarshalJSON(t *testing.T) { + timeObj, err := time.Parse(time.RFC3339, "2021-05-10T02:43:42Z") + if err != nil { + t.Error(err) + return + } + dateObj := notionapi.Date(timeObj) + tests := []struct { + name string + req *notionapi.DatabaseQueryRequest + want []byte + wantErr bool + }{ + { + name: "with property filter without sort", + req: ¬ionapi.DatabaseQueryRequest{ + PropertyFilter: ¬ionapi.PropertyFilter{ + Property: "Status", + Select: ¬ionapi.SelectFilterCondition{ + Equals: "Reading", + }, + }, + }, + want: []byte(`{"filter":{"property":"Status","select":{"equals":"Reading"}}}`), + }, + { + name: "with property filter with sort", + req: ¬ionapi.DatabaseQueryRequest{ + PropertyFilter: ¬ionapi.PropertyFilter{ + Property: "Status", + Select: ¬ionapi.SelectFilterCondition{ + Equals: "Reading", + }, + }, + Sorts: []notionapi.SortObject{ + { + Property: "Score /5", + Direction: notionapi.SortOrderASC, + }, + }, + }, + want: []byte(`{"sorts":[{"property":"Score /5","direction":"ascending"}],"filter":{"property":"Status","select":{"equals":"Reading"}}}`), + }, + { + name: "compound filter", + req: ¬ionapi.DatabaseQueryRequest{ + CompoundFilter: ¬ionapi.CompoundFilter{ + notionapi.FilterOperatorOR: []notionapi.PropertyFilter{ + { + Property: "Status", + Select: ¬ionapi.SelectFilterCondition{ + Equals: "Reading", + }, + }, + { + Property: "Publisher", + Select: ¬ionapi.SelectFilterCondition{ + Equals: "NYT", + }, + }, + }, + }, + }, + want: []byte(`{"filter":{"or":[{"property":"Status","select":{"equals":"Reading"}},{"property":"Publisher","select":{"equals":"NYT"}}]}}`), + }, + { + name: "date filter", + req: ¬ionapi.DatabaseQueryRequest{ + PropertyFilter: ¬ionapi.PropertyFilter{ + Property: "created_at", + Date: ¬ionapi.DateFilterCondition{ + Equals: &dateObj, + PastWeek: &struct{}{}, + }, + }, + }, + want: []byte(`{"filter":{"property":"created_at","date":{"equals":"2021-05-10T02:43:42Z","past_week":{}}}}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.req.MarshalJSON() + if (err != nil) != tt.wantErr { + t.Errorf("MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("MarshalJSON() got = %s, want %s", got, tt.want) + } + }) + } +} diff --git a/filter.go b/filter.go index 3ea17ed..80ec00a 100644 --- a/filter.go +++ b/filter.go @@ -1,25 +1,104 @@ package notionapi -import "time" - type FilterOperator string -type Filter interface{} - -type CompoundFilter map[FilterOperator]Filter +type CompoundFilter map[FilterOperator][]PropertyFilter type Condition string type PropertyFilter struct { - Property PropertyType `json:"property"` - Text map[Condition]string `json:"text,omitempty"` - Number map[Condition]float64 `json:"number,omitempty"` - Checkbox map[Condition]bool `json:"checkbox,omitempty"` - Select map[Condition]interface{} `json:"select,omitempty"` - MultiSelect map[Condition]interface{} `json:"multi_select,omitempty"` - Date map[Condition]time.Time `json:"date,omitempty"` - People map[Condition]interface{} `json:"people,omitempty"` - Files map[Condition]bool `json:"files,omitempty"` - Relation map[Condition]interface{} `json:"relation,omitempty"` - Formula map[PropertyType]PropertyFilter `json:"formula,omitempty"` + Property string `json:"property"` + Text *TextFilterCondition `json:"text,omitempty"` + Number *NumberFilterCondition `json:"number,omitempty"` + Checkbox *CheckboxFilterCondition `json:"checkbox,omitempty"` + Select *SelectFilterCondition `json:"select,omitempty"` + MultiSelect *MultiSelectFilterCondition `json:"multi_select,omitempty"` + Date *DateFilterCondition `json:"date,omitempty"` + People *PeopleFilterCondition `json:"people,omitempty"` + Files *FilesFilterCondition `json:"files,omitempty"` + Relation *RelationFilterCondition `json:"relation,omitempty"` + Formula *FormulaFilterCondition `json:"formula,omitempty"` +} + +type TextFilterCondition struct { + Equals string `json:"equals,omitempty"` + DoesNotEqual string `json:"does_not_equal,omitempty"` + Contains string `json:"contains,omitempty"` + DoesNotContain string `json:"does_not_contain,omitempty"` + StartsWith string `json:"starts_with,omitempty"` + EndsWith string `json:"ends_with,omitempty"` + IsEmpty bool `json:"is_empty,omitempty"` + IsNotEmpty bool `json:"is_not_empty,omitempty"` +} + +type NumberFilterCondition struct { + Equals float64 `json:"equals,omitempty"` + DoesNotEqual float64 `json:"does_not_equal,omitempty"` + GreaterThan float64 `json:"greater_than,omitempty"` + LessThan float64 `json:"less_than,omitempty"` + GreaterThanOrEqualTo float64 `json:"greater_than_or_equal_to"` + LessThanOrEqualTo float64 `json:"less_than_or_equal_to"` + IsEmpty bool `json:"is_empty,omitempty"` + IsNotEmpty bool `json:"is_not_empty,omitempty"` +} + +type CheckboxFilterCondition struct { + Equals bool `json:"equals,omitempty"` + DoesNotEqual bool `json:"does_not_equal,omitempty"` +} + +type SelectFilterCondition struct { + Equals string `json:"equals,omitempty"` + DoesNotEqual string `json:"does_not_equal,omitempty"` + IsEmpty bool `json:"is_empty,omitempty"` + IsNotEmpty bool `json:"is_not_empty,omitempty"` +} + +type MultiSelectFilterCondition struct { + Contains string `json:"contains,omitempty"` + DoesNotContain string `json:"does_not_contain,omitempty"` + IsEmpty bool `json:"is_empty,omitempty"` + IsNotEmpty bool `json:"is_not_empty,omitempty"` +} + +type DateFilterCondition struct { + Equals *Date `json:"equals,omitempty"` + Before *Date `json:"before,omitempty"` + After *Date `json:"after,omitempty"` + OnOrBefore *Date `json:"on_or_before,omitempty"` + OnOrAfter *Date `json:"on_or_after,omitempty"` + PastWeek *struct{} `json:"past_week,omitempty"` + PastMonth *struct{} `json:"past_month,omitempty"` + PastYear *struct{} `json:"past_year,omitempty"` + NextWeek *struct{} `json:"next_week,omitempty"` + NextMonth *struct{} `json:"next_month,omitempty"` + NextYear *struct{} `json:"next_year,omitempty"` + IsEmpty bool `json:"is_empty,omitempty"` + IsNotEmpty bool `json:"is_not_empty,omitempty"` +} + +type PeopleFilterCondition struct { + Contains string `json:"contains,omitempty"` + DoesNotContain string `json:"does_not_contain,omitempty"` + IsEmpty bool `json:"is_empty,omitempty"` + IsNotEmpty bool `json:"is_not_empty,omitempty"` +} + +type FilesFilterCondition struct { + IsEmpty bool `json:"is_empty,omitempty"` + IsNotEmpty bool `json:"is_not_empty,omitempty"` +} + +type RelationFilterCondition struct { + Contains string `json:"contains,omitempty"` + DoesNotContain string `json:"does_not_contain,omitempty"` + IsEmpty bool `json:"is_empty,omitempty"` + IsNotEmpty bool `json:"is_not_empty,omitempty"` +} + +type FormulaFilterCondition struct { + Text *TextFilterCondition `json:"text,omitempty"` + Checkbox *CheckboxFilterCondition `json:"checkbox,omitempty"` + Number *NumberFilterCondition `json:"number,omitempty"` + Date *DateFilterCondition `json:"date,omitempty"` } diff --git a/object.go b/object.go index f25d08e..100c399 100644 --- a/object.go +++ b/object.go @@ -1,5 +1,7 @@ package notionapi +import "time" + type ObjectType string func (ot ObjectType) String() string { @@ -66,3 +68,13 @@ type Cursor string func (c Cursor) String() string { return string(c) } + +type Date time.Time + +func (d *Date) String() string { + return time.Time(*d).Format(time.RFC3339) +} + +func (d *Date) MarshalText() ([]byte, error) { + return []byte(d.String()), nil +} diff --git a/search.go b/search.go index ee767a3..71ae413 100644 --- a/search.go +++ b/search.go @@ -34,7 +34,7 @@ func (sc *SearchClient) Do(ctx context.Context, request *SearchRequest) (*Search type SearchRequest struct { Query string `json:"query,omitempty"` Sort *SortObject `json:"sort,omitempty"` - Filter Filter `json:"filter,omitempty"` + Filter interface{} `json:"filter,omitempty"` StartCursor Cursor `json:"start_cursor,omitempty"` PageSize int `json:"page_size"` } diff --git a/sort.go b/sort.go index 5133c5f..1334f8c 100644 --- a/sort.go +++ b/sort.go @@ -6,6 +6,6 @@ type TimestampType string type SortObject struct { Property string `json:"property,omitempty"` - Timestamp TimestampType `json:"timestamp"` - Direction SortOrder `json:"direction"` + Timestamp TimestampType `json:"timestamp,omitempty"` + Direction SortOrder `json:"direction,omitempty"` }