Skip to content

Commit

Permalink
Merge pull request #4 from roman-utila/roman/support-topics
Browse files Browse the repository at this point in the history
feat: implement novu topics
  • Loading branch information
unicodeveloper authored Mar 19, 2023
2 parents 62f09f0 + e8d68f6 commit ebe03f7
Show file tree
Hide file tree
Showing 8 changed files with 507 additions and 17 deletions.
2 changes: 1 addition & 1 deletion lib/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (e *EventService) Trigger(ctx context.Context, eventId string, data ITrigge
return resp, err
}

err = e.client.sendRequest(req, &resp)
_, err = e.client.sendRequest(req, &resp)
if err != nil {
return resp, err
}
Expand Down
55 changes: 53 additions & 2 deletions lib/event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import (
"bytes"
"context"
"encoding/json"
"github.com/novuhq/go-novu/lib"
"github.com/stretchr/testify/require"
"io"
"io/ioutil"
"log"
Expand All @@ -15,6 +13,9 @@ import (
"strings"
"testing"

"github.com/novuhq/go-novu/lib"
"github.com/stretchr/testify/require"

"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -78,3 +79,53 @@ func TestEventServiceTrigger_Success(t *testing.T) {

require.Nil(t, err)
}

func TestEventServiceTriggerForTopic_Success(t *testing.T) {
var (
receivedBody lib.ITriggerPayloadOptions
expectedTokenRequest lib.ITriggerPayloadOptions
triggerPayload lib.ITriggerPayloadOptions
)

eventService := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if err := json.NewDecoder(req.Body).Decode(&receivedBody); err != nil {
log.Printf("error in unmarshalling %+v", err)
w.WriteHeader(http.StatusBadRequest)
return
}

t.Run("Header must contain ApiKey", func(t *testing.T) {
authKey := req.Header.Get("Authorization")
assert.True(t, strings.Contains(authKey, novuApiKey))
assert.True(t, strings.HasPrefix(authKey, "ApiKey"))
})

t.Run("URL and request method is as expected", func(t *testing.T) {
expectedURL := "/v1/events/trigger"
assert.Equal(t, http.MethodPost, req.Method)
assert.Equal(t, expectedURL, req.RequestURI)
})

t.Run("Request is as expected", func(t *testing.T) {
fileToStruct(filepath.Join("../testdata", "novu_send_trigger_topic_recipient.json"), &expectedTokenRequest)
assert.Equal(t, expectedTokenRequest, receivedBody)
})

var resp lib.EventResponse
fileToStruct(filepath.Join("../testdata", "novu_send_trigger_response.json"), &resp)

w.WriteHeader(http.StatusOK)
bb, _ := json.Marshal(resp)
w.Write(bb)
}))

defer eventService.Close()

ctx := context.Background()
fileToStruct(filepath.Join("../testdata", "novu_send_trigger_topic_recipient.json"), &triggerPayload)

c := lib.NewAPIClient(novuApiKey, &lib.Config{BackendURL: eventService.URL})
_, err := c.EventApi.Trigger(ctx, novuEventId, triggerPayload)

require.Nil(t, err)
}
49 changes: 45 additions & 4 deletions lib/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ type GeneralError error
const Version = "v1"

const (
HTTPStatusOk = 200
HTTPStatusCreated = 201
HTTPRedirectOk = 300
HTTPStatusOk = 200
HTTPStatusCreated = 201
HTTPStatusNoChange = 204
HTTPRedirectOk = 300
)

const (
Expand Down Expand Up @@ -43,6 +44,11 @@ type TriggerRecipientsTypeSingle interface {
string | SubscriberPayload
}

type TriggerTopicRecipientsTypeSingle struct {
TopicKey string `json:"topicKey,omitempty"`
Type string `json:"type,omitempty"`
}

type SubscriberPayload struct {
FirstName string `json:"first_name,omitempty"`
LastName string `json:"last_name,omitempty"`
Expand All @@ -53,7 +59,7 @@ type SubscriberPayload struct {
}

type TriggerRecipientsType interface {
TriggerRecipientsTypeSingle | TriggerRecipientsTypeArray
TriggerRecipientsTypeSingle | TriggerRecipientsTypeArray | TriggerTopicRecipientsTypeSingle | []TriggerTopicRecipientsTypeSingle
}

type ITriggerPayload interface {
Expand Down Expand Up @@ -83,3 +89,38 @@ type EventRequest struct {
type SubscriberResponse struct {
Data interface{} `json:"data"`
}

type ListTopicsResponse struct {
Page int `json:"name"`
PageSize int `json:"pageSize"`
TotalCount int `json:"totalCount"`
Data []GetTopicResponse `json:"data"`
}

type GetTopicResponse struct {
Id string `json:"_id"`
OrganizationId string `json:"_organizationId"`
EnvironmentId string `json:"_environmentId"`
Key string `json:"key"`
Name string `json:"name"`
Subscribers []string `json:"subscribers"`
}

type ListTopicsOptions struct {
Page *int `json:"page,omitempty"`
PageSize *int `json:"pageSize,omitempty"`
Key *string `json:"key,omitempty"`
}

type CreateTopicRequest struct {
Name string `json:"name"`
Key string `json:"key"`
}

type RenameTopicRequest struct {
Name string `json:"name"`
}

type SubscribersTopicRequest struct {
Subscribers []string `json:"subscribers"`
}
20 changes: 14 additions & 6 deletions lib/novu.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package lib
import (
"encoding/json"
"fmt"
"github.com/pkg/errors"
"io"
"net/http"
"strings"
"time"

"github.com/pkg/errors"
)

type Config struct {
Expand All @@ -23,6 +24,7 @@ type APIClient struct {
// Api Service
SubscriberApi *SubscriberService
EventApi *EventService
TopicsApi *TopicService
}

type service struct {
Expand All @@ -43,35 +45,41 @@ func NewAPIClient(apiKey string, cfg *Config) *APIClient {
// API Services
c.EventApi = (*EventService)(&c.common)
c.SubscriberApi = (*SubscriberService)(&c.common)
c.TopicsApi = (*TopicService)(&c.common)

return c
}

func (c APIClient) sendRequest(req *http.Request, resp interface{}) error {
func (c APIClient) sendRequest(req *http.Request, resp interface{}) (*http.Response, error) {
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", fmt.Sprintf("ApiKey %s", c.apiKey))

res, err := c.config.HttpClient.Do(req)
if err != nil {
return errors.Wrap(err, "failed to execute request")
return res, errors.Wrap(err, "failed to execute request")
}

body, _ := io.ReadAll(res.Body)
defer res.Body.Close()

if res.StatusCode >= http.StatusMultipleChoices {
return errors.Errorf(
return res, errors.Errorf(
`request was not successful, status code %d, %s`, res.StatusCode,
string(body),
)
}

if string(body) == "" {
resp = map[string]string{}
return res, nil
}

err = c.decode(&resp, body)
if err != nil {
return errors.Wrap(err, "unable to unmarshal response body")
return res, errors.Wrap(err, "unable to unmarshal response body")
}

return nil
return res, nil
}

func (c APIClient) mergeStruct(target, patch interface{}) (interface{}, error) {
Expand Down
9 changes: 5 additions & 4 deletions lib/subscribers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/pkg/errors"
"net/http"

"github.com/pkg/errors"
)

type ISubscribers interface {
Expand All @@ -33,7 +34,7 @@ func (s *SubscriberService) Identify(ctx context.Context, subscriberID string, d
return resp, err
}

err = s.client.sendRequest(req, &resp)
_, err = s.client.sendRequest(req, &resp)
if err != nil {
return resp, err
}
Expand All @@ -52,7 +53,7 @@ func (s *SubscriberService) Update(ctx context.Context, subscriberID string, dat
return resp, err
}

err = s.client.sendRequest(req, &resp)
_, err = s.client.sendRequest(req, &resp)
if err != nil {
return resp, err
}
Expand All @@ -69,7 +70,7 @@ func (s *SubscriberService) Delete(ctx context.Context, subscriberID string) (Su
return resp, err
}

err = s.client.sendRequest(req, &resp)
_, err = s.client.sendRequest(req, &resp)
if err != nil {
return resp, err
}
Expand Down
Loading

0 comments on commit ebe03f7

Please sign in to comment.