Skip to content

Commit

Permalink
Merge pull request #171 from phiHero/feat/conversations
Browse files Browse the repository at this point in the history
Add support for conversations & analytics APIs, allow specification of dirty values behaviors and export option
  • Loading branch information
kishorenc authored Oct 6, 2024
2 parents ee84717 + 77722d7 commit f3e98fa
Show file tree
Hide file tree
Showing 40 changed files with 4,523 additions and 497 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ jobs:
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: v1.59
version: v1.60
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
services:
typesense:
image: typesense/typesense:26.0
image: typesense/typesense:27.0
ports:
- 8108:8108/tcp
volumes:
Expand Down
23 changes: 23 additions & 0 deletions typesense/analytics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package typesense

type AnalyticsInterface interface {
Events() AnalyticsEventsInterface
Rules() AnalyticsRulesInterface
Rule(ruleName string) AnalyticsRuleInterface
}

type analytics struct {
apiClient APIClientInterface
}

func (a *analytics) Events() AnalyticsEventsInterface {
return &analyticsEvents{apiClient: a.apiClient}
}

func (a *analytics) Rules() AnalyticsRulesInterface {
return &analyticsRules{apiClient: a.apiClient}
}

func (a *analytics) Rule(ruleName string) AnalyticsRuleInterface {
return &analyticsRule{apiClient: a.apiClient, ruleName: ruleName}
}
26 changes: 26 additions & 0 deletions typesense/analytics_events.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package typesense

import (
"context"

"github.com/typesense/typesense-go/v2/typesense/api"
)

type AnalyticsEventsInterface interface {
Create(ctx context.Context, eventSchema *api.AnalyticsEventCreateSchema) (*api.AnalyticsEventCreateResponse, error)
}

type analyticsEvents struct {
apiClient APIClientInterface
}

func (a *analyticsEvents) Create(ctx context.Context, eventSchema *api.AnalyticsEventCreateSchema) (*api.AnalyticsEventCreateResponse, error) {
response, err := a.apiClient.CreateAnalyticsEventWithResponse(ctx, api.CreateAnalyticsEventJSONRequestBody(*eventSchema))
if err != nil {
return nil, err
}
if response.JSON201 == nil {
return nil, &HTTPError{Status: response.StatusCode(), Body: response.Body}
}
return response.JSON201, nil
}
55 changes: 55 additions & 0 deletions typesense/analytics_events_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package typesense

import (
"context"
"encoding/json"
"net/http"
"testing"

"github.com/stretchr/testify/assert"
"github.com/typesense/typesense-go/v2/typesense/api"
)

func TestAnalyticsEventsCreate(t *testing.T) {
expectedData := &api.AnalyticsEventCreateSchema{
Name: "products_click_event",
Type: "click",
Data: map[string]interface{}{
"hello": "hi",
},
}

server, client := newTestServerAndClient(func(w http.ResponseWriter, r *http.Request) {
validateRequestMetadata(t, r, "/analytics/events", http.MethodPost)

var reqBody api.AnalyticsEventCreateSchema
err := json.NewDecoder(r.Body).Decode(&reqBody)

assert.NoError(t, err)
assert.Equal(t, *expectedData, reqBody)

data := jsonEncode(t, api.AnalyticsEventCreateResponse{
Ok: true,
})

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
w.Write(data)
})
defer server.Close()

res, err := client.Analytics().Events().Create(context.Background(), expectedData)
assert.NoError(t, err)
assert.True(t, res.Ok)
}

func TestAnalyticsEventsCreateOnHttpStatusErrorCodeReturnsError(t *testing.T) {
server, client := newTestServerAndClient(func(w http.ResponseWriter, r *http.Request) {
validateRequestMetadata(t, r, "/analytics/events", http.MethodPost)
w.WriteHeader(http.StatusConflict)
})
defer server.Close()

_, err := client.Analytics().Events().Create(context.Background(), &api.AnalyticsEventCreateSchema{})
assert.ErrorContains(t, err, "status: 409")
}
39 changes: 39 additions & 0 deletions typesense/analytics_rule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package typesense

import (
"context"

"github.com/typesense/typesense-go/v2/typesense/api"
)

type AnalyticsRuleInterface interface {
Delete(ctx context.Context) (*api.AnalyticsRuleDeleteResponse, error)
Retrieve(ctx context.Context) (*api.AnalyticsRuleSchema, error)
}

type analyticsRule struct {
apiClient APIClientInterface
ruleName string
}

func (a *analyticsRule) Delete(ctx context.Context) (*api.AnalyticsRuleDeleteResponse, error) {
response, err := a.apiClient.DeleteAnalyticsRuleWithResponse(ctx, a.ruleName)
if err != nil {
return nil, err
}
if response.JSON200 == nil {
return nil, &HTTPError{Status: response.StatusCode(), Body: response.Body}
}
return response.JSON200, nil
}

func (a *analyticsRule) Retrieve(ctx context.Context) (*api.AnalyticsRuleSchema, error) {
response, err := a.apiClient.RetrieveAnalyticsRuleWithResponse(ctx, a.ruleName)
if err != nil {
return nil, err
}
if response.JSON200 == nil {
return nil, &HTTPError{Status: response.StatusCode(), Body: response.Body}
}
return response.JSON200, nil
}
75 changes: 75 additions & 0 deletions typesense/analytics_rule_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package typesense

import (
"context"
"net/http"
"testing"

"github.com/stretchr/testify/assert"
"github.com/typesense/typesense-go/v2/typesense/api"
"github.com/typesense/typesense-go/v2/typesense/api/pointer"
)

func TestAnalyticsRuleRetrieve(t *testing.T) {
expectedData := &api.AnalyticsRuleSchema{
Name: "test_rule",
Type: "test_type",
Params: api.AnalyticsRuleParameters{
Limit: pointer.Int(10),
},
}

server, client := newTestServerAndClient(func(w http.ResponseWriter, r *http.Request) {
validateRequestMetadata(t, r, "/analytics/rules/test_rule", http.MethodGet)
data := jsonEncode(t, expectedData)

w.Header().Set("Content-Type", "application/json")
w.Write(data)
})
defer server.Close()

res, err := client.Analytics().Rule(expectedData.Name).Retrieve(context.Background())
assert.NoError(t, err)
assert.Equal(t, expectedData, res)
}

func TestAnalyticsRuleRetrieveOnHttpStatusErrorCodeReturnsError(t *testing.T) {
server, client := newTestServerAndClient(func(w http.ResponseWriter, r *http.Request) {
validateRequestMetadata(t, r, "/analytics/rules/test_rule", http.MethodGet)
w.WriteHeader(http.StatusConflict)
})
defer server.Close()

_, err := client.Analytics().Rule("test_rule").Retrieve(context.Background())
assert.ErrorContains(t, err, "status: 409")
}

func TestAnalyticsRuleDelete(t *testing.T) {
expectedData := &api.AnalyticsRuleDeleteResponse{
Name: "test_rule",
}

server, client := newTestServerAndClient(func(w http.ResponseWriter, r *http.Request) {
validateRequestMetadata(t, r, "/analytics/rules/test_rule", http.MethodDelete)
data := jsonEncode(t, expectedData)

w.Header().Set("Content-Type", "application/json")
w.Write(data)
})
defer server.Close()

res, err := client.Analytics().Rule("test_rule").Delete(context.Background())
assert.NoError(t, err)
assert.Equal(t, expectedData, res)
}

func TestAnalyticsRuleUpsertOnHttpStatusErrorCodeReturnsError(t *testing.T) {
server, client := newTestServerAndClient(func(w http.ResponseWriter, r *http.Request) {
validateRequestMetadata(t, r, "/analytics/rules/test_rule", http.MethodDelete)
w.WriteHeader(http.StatusConflict)
})
defer server.Close()

_, err := client.Analytics().Rule("test_rule").Delete(context.Background())
assert.ErrorContains(t, err, "status: 409")
}
39 changes: 39 additions & 0 deletions typesense/analytics_rules.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package typesense

import (
"context"

"github.com/typesense/typesense-go/v2/typesense/api"
)

type AnalyticsRulesInterface interface {
Upsert(ctx context.Context, ruleName string, ruleSchema *api.AnalyticsRuleUpsertSchema) (*api.AnalyticsRuleSchema, error)
Retrieve(ctx context.Context) ([]*api.AnalyticsRuleSchema, error)
}

type analyticsRules struct {
apiClient APIClientInterface
}

func (a *analyticsRules) Upsert(ctx context.Context, ruleName string, ruleSchema *api.AnalyticsRuleUpsertSchema) (*api.AnalyticsRuleSchema, error) {
response, err := a.apiClient.UpsertAnalyticsRuleWithResponse(ctx,
ruleName, api.UpsertAnalyticsRuleJSONRequestBody(*ruleSchema))
if err != nil {
return nil, err
}
if response.JSON200 == nil {
return nil, &HTTPError{Status: response.StatusCode(), Body: response.Body}
}
return response.JSON200, nil
}

func (a *analyticsRules) Retrieve(ctx context.Context) ([]*api.AnalyticsRuleSchema, error) {
response, err := a.apiClient.RetrieveAnalyticsRulesWithResponse(ctx)
if err != nil {
return nil, err
}
if response.JSON200 == nil {
return nil, &HTTPError{Status: response.StatusCode(), Body: response.Body}
}
return *response.JSON200.Rules, nil
}
98 changes: 98 additions & 0 deletions typesense/analytics_rules_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package typesense

import (
"context"
"encoding/json"
"net/http"
"testing"

"github.com/stretchr/testify/assert"
"github.com/typesense/typesense-go/v2/typesense/api"
"github.com/typesense/typesense-go/v2/typesense/api/pointer"
)

func TestAnalyticsRulesRetrieve(t *testing.T) {
expectedData := []*api.AnalyticsRuleSchema{
{
Name: "test_name",
Type: "test_type",
Params: api.AnalyticsRuleParameters{
Limit: pointer.Int(10),
},
},
}

server, client := newTestServerAndClient(func(w http.ResponseWriter, r *http.Request) {
validateRequestMetadata(t, r, "/analytics/rules", http.MethodGet)
data := jsonEncode(t, api.AnalyticsRulesRetrieveSchema{
Rules: &expectedData,
})
w.Header().Set("Content-Type", "application/json")
w.Write(data)
})
defer server.Close()

res, err := client.Analytics().Rules().Retrieve(context.Background())
assert.NoError(t, err)
assert.Equal(t, expectedData, res)
}

func TestAnalyticsRulesRetrieveOnHttpStatusErrorCodeReturnsError(t *testing.T) {
server, client := newTestServerAndClient(func(w http.ResponseWriter, r *http.Request) {
validateRequestMetadata(t, r, "/analytics/rules", http.MethodGet)
w.WriteHeader(http.StatusConflict)
})
defer server.Close()

_, err := client.Analytics().Rules().Retrieve(context.Background())
assert.ErrorContains(t, err, "status: 409")
}

func TestAnalyticsRulesUpsert(t *testing.T) {
upsertData := &api.AnalyticsRuleUpsertSchema{
Type: api.AnalyticsRuleUpsertSchemaTypeCounter,
Params: api.AnalyticsRuleParameters{
Limit: pointer.Int(100),
},
}
expectedData := &api.AnalyticsRuleSchema{
Name: "test-rule",
Type: api.AnalyticsRuleSchemaType(upsertData.Type),
Params: upsertData.Params,
}

server, client := newTestServerAndClient(func(w http.ResponseWriter, r *http.Request) {
validateRequestMetadata(t, r, "/analytics/rules/test-rule", http.MethodPut)

var reqBody api.AnalyticsRuleUpsertSchema
err := json.NewDecoder(r.Body).Decode(&reqBody)

assert.NoError(t, err)
assert.Equal(t, *upsertData, reqBody)

data := jsonEncode(t, api.AnalyticsRuleSchema{
Name: expectedData.Name,
Type: api.AnalyticsRuleSchemaType(upsertData.Type),
Params: upsertData.Params,
})

w.Header().Set("Content-Type", "application/json")
w.Write(data)
})
defer server.Close()

res, err := client.Analytics().Rules().Upsert(context.Background(), expectedData.Name, upsertData)
assert.NoError(t, err)
assert.Equal(t, expectedData, res)
}

func TestAnalyticsRulesUpsertOnHttpStatusErrorCodeReturnsError(t *testing.T) {
server, client := newTestServerAndClient(func(w http.ResponseWriter, r *http.Request) {
validateRequestMetadata(t, r, "/analytics/rules/test-rule", http.MethodPut)
w.WriteHeader(http.StatusConflict)
})
defer server.Close()

_, err := client.Analytics().Rules().Upsert(context.Background(), "test-rule", &api.AnalyticsRuleUpsertSchema{})
assert.ErrorContains(t, err, "status: 409")
}
6 changes: 6 additions & 0 deletions typesense/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,9 @@ func WithAPIKey(apiKey string) ClientOption {
return nil
}
}

// Manually defining this unreferenced schema here instead of disabling oapi-codegen schema pruning

type DocumentIndexParameters struct {
DirtyValues *DirtyValues `json:"dirty_values,omitempty"`
}
Loading

0 comments on commit f3e98fa

Please sign in to comment.