diff --git a/README.md b/README.md index 1f154e3..c294107 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,9 @@ Novu's API exposes the entire Novu features via a standardized programmatic interface. Please refer to the full [documentation](https://docs.novu.co/docs/overview/introduction) to learn more. ## Installation & Usage + Install the package to your GoLang project. + ```golang go get github.com/novuhq/go-novu ``` @@ -61,6 +63,7 @@ func main() { fmt.Println(integrations) } ``` + **NOTE** Check the `cmd` directory to see a sample implementation and test files to see sample tests @@ -68,44 +71,52 @@ Check the `cmd` directory to see a sample implementation and test files to see s Class | Method | HTTP request | Description ------------ |----------------------------------------------------------------------------------|-----------------------------------------| ------------- -*EventApi* | [**Trigger**](https://docs.novu.co/api-reference/events/trigger-event) | **Post** /events/trigger | Trigger -*EventApi* | [**TriggerBulk**](https://docs.novu.co/api-reference/events/bulk-trigger-event) | **Post** /v1/events/trigger/bulk | Bulk trigger event -*EventApi* | [**BroadcastToAll**](https://docs.novu.co/api-reference/events/broadcast-event-to-all) | **Post** /v1/events/trigger/broadcast | Broadcast event to all -*EventApi* | [**CancelTrigger**](https://docs.novu.co/api-reference/events/cancel-triggered-event)) | **Delete** /v1/events/trigger/:transactionId | Cancel triggered event -*SubscriberApi* | [**Get**](https://docs.novu.co/api-reference/subscribers/get-subscriber)) | **Get** /subscribers/:subscriberId | Get a subscriber -*SubscriberApi* | [**Identify**](https://docs.novu.co/api-reference/subscribers/create-subscriber) | **Post** /subscribers | Create a subscriber -*SubscriberApi* | [**Update**](https://docs.novu.co/api-reference/subscribers/update-subscriber) | **Put** /subscribers/:subscriberID | Update subscriber data -*SubscriberApi* | [**Delete**](https://docs.novu.co/api-reference/subscribers/delete-subscriber) | **Delete** /subscribers/:subscriberID | Removing a subscriber -*SubscriberApi* | [**Get**](https://docs.novu.co/api-reference/subscribers/get-in-app-notification-feed-for-a-particular-subscriber) | **Get** /subscribers/:subscriberId/notifications/feed | Get a notification feed for a particular subscriber -*SubscriberApi* | [**Get**](https://docs.novu.co/api-reference/subscribers/get-the-unseen-in-app-notifications-count-for-subscribers-feed) | **Get** /subscribers/:subscriberId/notifications/feed | Get the unseen notification count for subscribers feed -*SubscriberApi* | [**Post**](https://docs.novu.co/api-reference/subscribers/mark-a-subscriber-feed-message-as-seen) | **Post** /v1/subscribers/:subscriberId/messages/markAs | Mark a subscriber feed message as seen -*SubscriberApi* | [**Get**](https://docs.novu.co/api-reference/subscribers/get-subscriber-preferences) | **Get** /subscribers/:subscriberId/preferences | Get subscriber preferences -*SubscriberApi* | [**Patch**](https://docs.novu.co/api-reference/subscribers/update-subscriber-preference) | **Patch** /subscribers/:subscriberId/preferences/:templateId | Update subscriber preference -*IntegrationsApi* | [**Create**](https://docs.novu.co/api-reference/integrations/create-integration) | **Post** /integrations | Create an integration -*IntegrationsApi* | [**Update**](https://docs.novu.co/api-reference/integrations/update-integration) | **Put** /integrations/:integrationId | Update an integration -*IntegrationsApi* | [**Delete**](https://docs.novu.co/api-reference/integrations/delete-integration) | **Delete** /integrations/:integrationId | Delete an integration -*IntegrationsApi* | [**Get**](https://docs.novu.co/api-reference/integrations/get-integrations) | **Get** /integrations | Get all integrations -*IntegrationsApi* | [**GetActive**](https://docs.novu.co/api-reference/integrations/get-active-integrations) | **Get** /integrations/active | Get all active integrations -*InboundParserApi* | [**Get**](https://docs.novu.co/api-reference/inbound-parse/validate-the-mx-record-setup-for-the-inbound-parse-functionality) | **Get** /inbound-parse/mx/status | Validate the mx record setup for the inbound parse functionality +*EventApi* | [**Trigger**](https://docs.novu.co/platform/subscribers#removing-a-subscriber) | **Post** /events/trigger | Trigger +*EventApi* | [**TriggerBulk**](https://docs.novu.co/api/trigger-event/) | **Post** /v1/events/trigger/bulk | Bulk trigger event +*EventApi* | [**BroadcastToAll**](https://docs.novu.co/api/broadcast-event-to-all/) | **Post** /v1/events/trigger/broadcast | Broadcast event to all +*EventApi* | [**CancelTrigger**](https://docs.novu.co/api/cancel-triggered-event/) | **Delete** /v1/events/trigger/:transactionId | Cancel triggered event +*SubscriberApi* | [**Get**](https://docs.novu.co/api/get-subscriber/) | **Get** /subscribers/:subscriberId | Get a subscriber +*SubscriberApi* | [**Identify**](https://docs.novu.co/platform/subscribers#creating-a-subscriber) | **Post** /subscribers | Create a subscriber +*SubscriberApi* | [**Update**](https://docs.novu.co/platform/subscribers#updating-subscriber-data) | **Put** /subscribers/:subscriberID | Update subscriber data +*SubscriberApi* | [**Delete**](https://docs.novu.co/platform/subscribers#removing-a-subscriber) | **Delete** /subscribers/:subscriberID | Removing a subscriber +*SubscriberApi* | [**Get**](https://docs.novu.co/api/get-a-notification-feed-for-a-particular-subscriber) | **Get** /subscribers/:subscriberId/notifications/feed | Get a notification feed for a particular subscriber +*SubscriberApi* | [**Get**](https://docs.novu.co/api/get-the-unseen-notification-count-for-subscribers-feed) | **Get** /subscribers/:subscriberId/notifications/feed | Get the unseen notification count for subscribers feed +*SubscriberApi* | [**Post**](https://docs.novu.co/api/mark-a-subscriber-feed-message-as-seen) | **Post** /v1/subscribers/:subscriberId/messages/markAs | Mark a subscriber feed message as seen +*SubscriberApi* | [**Get**](https://docs.novu.co/api/get-subscriber-preferences/) | **Get** /subscribers/:subscriberId/preferences | Get subscriber preferences +*SubscriberApi* | [**Patch**](https://docs.novu.co/api/update-subscriber-preference/) | **Patch** /subscribers/:subscriberId/preferences/:templateId | Update subscriber preference +*TopicsApi* | [**Get**](https://docs.novu.co/api/filter-topics/) | **Get** /topics | Get a list of topics +*TopicsApi* | [**Get**](https://docs.novu.co/api/get-topic/) | **Get** /topics/:topicKey | Get a topic by its topic key +*TopicsApi* | [**Post**](https://docs.novu.co/api/topic-creation/) | **Post** /topics | Create a topic +*TopicsApi* | [**Patch**](https://docs.novu.co/api/rename-a-topic/) | **Patch** /topics/:topicKey | Rename a topic +*TopicsApi* | [**Delete**](https://docs.novu.co/api/delete-topic/) | **Delete** /topics/:topicKey | Delete a topic +*TopicsApi* | [**Post**](https://docs.novu.co/api/subscribers-addition/) | **Post** /topics/:topicKey/subscribers | Add subscribers to a topic by key +*TopicsApi* | [**Post**](https://docs.novu.co/api/subscribers-removal/) | **Post** /topics/:topicKey/subscribers/removal |Remove subscribers from a topic +*IntegrationsApi* | [**Create**](https://docs.novu.co/platform/integrations) | **Post** /integrations | Create an integration +*IntegrationsApi* | [**Update**](https://docs.novu.co/platform/integrations) | **Put** /integrations/:integrationId | Update an integration +*IntegrationsApi* | [**Delete**](https://docs.novu.co/platform/integrations) | **Delete** /integrations/:integrationId | Delete an integration +*IntegrationsApi* | [**Get**](https://docs.novu.co/platform/integrations) | **Get** /integrations | Get all integrations +*IntegrationsApi* | [**GetActive**](https://docs.novu.co/platform/integrations) | **Get** /integrations/active | Get all active integrations +_InboundParserApi_ | [**Get**](https://docs.novu.co/platform/inbound-parse-webhook/) | **Get** /inbound-parse/mx/status | Validate the mx record setup for the inbound parse functionality -## Authorization (api-key) ## Authorization (api-key) - **Type**: API key - **API key parameter name**: ApiKey - **Location**: HTTP header +### For more information about these methods and their parameters, see the [API documentation](https://docs.novu.co/api-reference/overview). + ## Support and Feedback Be sure to visit the Novu official [documentation website](https://docs.novu.co/docs) for additional information about our API. If you find a bug, please post the issue on [Github](https://github.com/novuhq/go-novu/issues). -As always, if you need additional assistance, join our Discord us a note [here](https://discord.gg/TT6TttXjRe). +As always, if you need additional assistance, join our Discord us a note [here](https://discord.gg/novu). ## Contributors -Name | +Name | ------------ | [Oyewole Samuel](https://github.com/samsoft00) | [Dima Grossman](https://github.com/scopsy) | diff --git a/lib/changes.go b/lib/changes.go new file mode 100644 index 0000000..56dedf7 --- /dev/null +++ b/lib/changes.go @@ -0,0 +1,104 @@ +package lib + +import ( + "bytes" + "context" + "encoding/json" + "net/http" + "net/url" + "strconv" +) + +type ChangesService service + +func (c *ChangesService) GetChangesCount(ctx context.Context) (ChangesCountResponse, error) { + var resp ChangesCountResponse + URL := c.client.config.BackendURL.JoinPath("changes", "count") + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, URL.String(), http.NoBody) + if err != nil { + return resp, err + } + + _, err = c.client.sendRequest(req, &resp) + if err != nil { + return resp, err + } + + return resp, nil +} + +func (c *ChangesService) GetChanges(ctx context.Context, q ChangesGetQuery) (ChangesGetResponse, error) { + var resp ChangesGetResponse + URL := c.client.config.BackendURL.JoinPath("changes") + URL.RawQuery = q.BuildQuery() + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, URL.String(), http.NoBody) + if err != nil { + return resp, err + } + + _, err = c.client.sendRequest(req, &resp) + if err != nil { + return resp, err + } + + return resp, nil +} + +func (c *ChangesService) ApplyChange(ctx context.Context, changeId string) (ChangesApplyResponse, error) { + var resp ChangesApplyResponse + URL := c.client.config.BackendURL.JoinPath("changes", changeId, "apply") + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, URL.String(), http.NoBody) + if err != nil { + return resp, err + } + + _, err = c.client.sendRequest(req, &resp) + if err != nil { + return resp, err + } + + return resp, nil +} + +func (c *ChangesService) ApplyBulkChanges(ctx context.Context, payload ChangesBulkApplyPayload) (ChangesApplyResponse, error) { + var resp ChangesApplyResponse + URL := c.client.config.BackendURL.JoinPath("changes", "bulk", "apply") + jsonBody, err := json.Marshal(payload) + if err != nil { + return resp, err + } + req, err := http.NewRequestWithContext(ctx, http.MethodPost, URL.String(), bytes.NewBuffer(jsonBody)) + if err != nil { + return resp, err + } + + _, err = c.client.sendRequest(req, &resp) + if err != nil { + return resp, err + } + + return resp, nil +} + +func (c *ChangesGetQuery) BuildQuery() string { + params := url.Values{} + + if c.Page == 0 { + c.Page = 1 + } + + if c.Limit == 0 { + c.Limit = 10 + } + + if c.Promoted == "" { + c.Promoted = "false" + } + params.Add("page", strconv.Itoa(c.Page)) + params.Add("limit", strconv.Itoa(c.Limit)) + params.Add("promoted", c.Promoted) + return params.Encode() +} diff --git a/lib/changes_test.go b/lib/changes_test.go new file mode 100644 index 0000000..c198183 --- /dev/null +++ b/lib/changes_test.go @@ -0,0 +1,265 @@ +package lib_test + +import ( + "context" + "encoding/json" + "fmt" + "log" + "net/http" + "net/http/httptest" + "strconv" + "strings" + "testing" + + "github.com/novuhq/go-novu/lib" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var bulkApplyPayload = `{ + "changeIds": [ + "string" + ] +}` + +var applyResponse = `{ + "data": [ + { + "_id": "string", + "_creatorId": "string", + "_environmentId": "string", + "_organizationId": "string", + "_entityId": "string", + "enabled": true, + "type": "Feed", + "change": {}, + "createdAt": "string", + "_parentId": "string" + } + ] +}` + +var getResponse = `{ + "totalCount": 0, + "data": [ + { + "_id": "string", + "_creatorId": "string", + "_environmentId": "string", + "_organizationId": "string", + "_entityId": "string", + "enabled": true, + "type": "Feed", + "change": {}, + "createdAt": "string", + "_parentId": "string" + } + ], + "pageSize": 0, + "page": 0 +} +` + +var getCountResponse = `{ + "data": 0 +}` + +func payloadStringToStruct(str string, s interface{}) error { + bb := []byte(str) + err := json.Unmarshal(bb, s) + if err != nil { + return err + } + return nil +} + +func TestChangesService_GetCount_Success(t *testing.T) { + var ( + expectedResponse lib.ChangesCountResponse + ) + + ChangesService := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + 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 := fmt.Sprintf("/v1/changes/count") + assert.Equal(t, http.MethodGet, req.Method) + assert.Equal(t, expectedURL, req.RequestURI) + }) + + var resp lib.ChangesCountResponse + err := payloadStringToStruct(getCountResponse, &resp) + require.Nil(t, err) + + w.WriteHeader(http.StatusOK) + bb, _ := json.Marshal(resp) + w.Write(bb) + })) + + defer ChangesService.Close() + + ctx := context.Background() + + c := lib.NewAPIClient(novuApiKey, &lib.Config{BackendURL: lib.MustParseURL(ChangesService.URL)}) + resp, err := c.ChangesApi.GetChangesCount(ctx) + require.Nil(t, err) + assert.NotNil(t, resp) + + t.Run("Response is as expected", func(t *testing.T) { + err := payloadStringToStruct(getCountResponse, &expectedResponse) + require.Nil(t, err) + assert.Equal(t, expectedResponse, resp) + }) +} + +func TestChangesService_Get_Success(t *testing.T) { + var ( + expectedResponse lib.ChangesGetResponse + ) + promoted := "false" + page := 1 + limit := 10 + + ChangesService := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + 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 := fmt.Sprintf("/v1/changes?limit=%s&page=%s&promoted=%s", strconv.Itoa(limit), strconv.Itoa(page), promoted) + assert.Equal(t, http.MethodGet, req.Method) + assert.Equal(t, expectedURL, req.URL.String()) + }) + + var resp lib.ChangesGetResponse + err := payloadStringToStruct(getResponse, &resp) + require.Nil(t, err) + + w.WriteHeader(http.StatusOK) + bb, _ := json.Marshal(resp) + w.Write(bb) + })) + + defer ChangesService.Close() + + ctx := context.Background() + + c := lib.NewAPIClient(novuApiKey, &lib.Config{BackendURL: lib.MustParseURL(ChangesService.URL)}) + q := lib.ChangesGetQuery{Page: 1, Limit: 10, Promoted: "false"} + resp, err := c.ChangesApi.GetChanges(ctx, q) + require.Nil(t, err) + assert.NotNil(t, resp) + + t.Run("Response is as expected", func(t *testing.T) { + err := payloadStringToStruct(getResponse, &expectedResponse) + require.Nil(t, err) + assert.Equal(t, expectedResponse, resp) + }) +} + +func TestChangesService_Apply_Success(t *testing.T) { + const changeID = "62b51a44da1af31d109f5da7" + var ( + expectedResponse lib.ChangesApplyResponse + ) + + ChangesService := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + 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/changes/" + changeID + "/apply" + assert.Equal(t, http.MethodPost, req.Method) + assert.Equal(t, expectedURL, req.RequestURI) + }) + + var resp lib.ChangesApplyResponse + payloadStringToStruct(applyResponse, &resp) + + w.WriteHeader(http.StatusOK) + bb, _ := json.Marshal(resp) + w.Write(bb) + })) + + defer ChangesService.Close() + + ctx := context.Background() + + c := lib.NewAPIClient(novuApiKey, &lib.Config{BackendURL: lib.MustParseURL(ChangesService.URL)}) + + resp, err := c.ChangesApi.ApplyChange(ctx, changeID) + require.Nil(t, err) + assert.NotNil(t, resp) + + t.Run("Response is as expected", func(t *testing.T) { + // fileToStruct(filepath.Join("../testdata", "changes_apply_response.json"), &expectedResponse) + payloadStringToStruct(applyResponse, &expectedResponse) + assert.Equal(t, expectedResponse, resp) + }) +} + +func TestChangesService_BulkApply_Success(t *testing.T) { + var ( + changesBulkApplyPayload lib.ChangesBulkApplyPayload + receivedBody lib.ChangesBulkApplyPayload + expectedRequest lib.ChangesBulkApplyPayload + expectedResponse lib.ChangesApplyResponse + ) + + ChangesService := 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/changes/bulk/apply" + assert.Equal(t, http.MethodPost, req.Method) + assert.Equal(t, expectedURL, req.RequestURI) + }) + + t.Run("Request is as expected", func(t *testing.T) { + payloadStringToStruct(bulkApplyPayload, &expectedRequest) + assert.Equal(t, expectedRequest, receivedBody) + }) + + var resp lib.ChangesApplyResponse + payloadStringToStruct(applyResponse, &resp) + + w.WriteHeader(http.StatusOK) + bb, _ := json.Marshal(resp) + w.Write(bb) + })) + + defer ChangesService.Close() + + ctx := context.Background() + payloadStringToStruct(bulkApplyPayload, &changesBulkApplyPayload) + + c := lib.NewAPIClient(novuApiKey, &lib.Config{BackendURL: lib.MustParseURL(ChangesService.URL)}) + + resp, err := c.ChangesApi.ApplyBulkChanges(ctx, changesBulkApplyPayload) + require.Nil(t, err) + assert.NotNil(t, resp) + + t.Run("Response is as expected", func(t *testing.T) { + payloadStringToStruct(applyResponse, &expectedResponse) + assert.Equal(t, expectedResponse, resp) + }) +} diff --git a/lib/integration.go b/lib/integration.go index c783b44..2a77d10 100644 --- a/lib/integration.go +++ b/lib/integration.go @@ -14,6 +14,8 @@ type IIntegration interface { GetWebhookSupportStatus(ctx context.Context, providerId string) (bool, error) Update(ctx context.Context, integrationId string, request UpdateIntegrationRequest) (*IntegrationResponse, error) Delete(ctx context.Context, integrationId string) (*IntegrationResponse, error) + SetIntegrationAsPrimary(ctx context.Context, integrationId string) (*SetIntegrationAsPrimaryResponse, error) + GetChannelLimit(ctx context.Context, channelType string) (*IntegrationChannelLimitResponse, error) } type IntegrationService service @@ -148,3 +150,39 @@ func (i IntegrationService) Delete(ctx context.Context, integrationId string) (* return &response, nil } + +func (i IntegrationService) SetIntegrationAsPrimary(ctx context.Context, integrationId string) (*SetIntegrationAsPrimaryResponse, error) { + var response SetIntegrationAsPrimaryResponse + + URL := i.client.config.BackendURL.JoinPath("integrations", integrationId, "set-primary") + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, URL.String(), http.NoBody) + if err != nil { + return nil, err + } + + _, err = i.client.sendRequest(req, &response) + if err != nil { + return nil, err + } + + return &response, nil +} + +func (i IntegrationService) GetChannelLimit(ctx context.Context, channelType string) (*IntegrationChannelLimitResponse, error) { + var response IntegrationChannelLimitResponse + + URL := i.client.config.BackendURL.JoinPath("integrations", channelType, "limit") + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, URL.String(), http.NoBody) + if err != nil { + return nil, err + } + + _, err = i.client.sendRequest(req, &response) + if err != nil { + return nil, err + } + + return &response, nil +} diff --git a/lib/integration_test.go b/lib/integration_test.go index bcf1066..95ea33a 100644 --- a/lib/integration_test.go +++ b/lib/integration_test.go @@ -254,3 +254,55 @@ func TestDeleteActiveIntegration_Success(t *testing.T) { require.NoError(t, err) } + +func TestSetIntegrationAsPrimary_Success(t *testing.T) { + const integrationId = "IntegrationId" + + var response *lib.SetIntegrationAsPrimaryResponse + fileToStruct(filepath.Join("../testdata", "set_integration_as_primary_response.json"), &response) + + httpServer := IntegrationTestServer(t, IntegrationServerOptions[interface{}]{ + ExpectedRequest: IntegrationRequestDetails[interface{}]{ + Url: fmt.Sprintf("/v1/integrations/%s/set-primary", integrationId), + Method: http.MethodPost, + }, + ExpectedResponse: IntegrationResponseDetails{ + StatusCode: http.StatusOK, + Body: response, + }, + }) + + ctx := context.Background() + novuClient := lib.NewAPIClient(novuApiKey, &lib.Config{BackendURL: lib.MustParseURL(httpServer.URL)}) + + res, err := novuClient.IntegrationsApi.SetIntegrationAsPrimary(ctx, integrationId) + + assert.Equal(t, response, res) + require.NoError(t, err) +} + +func TestGetChannelLimit_Success(t *testing.T) { + const channelType = "ChannelType" + + var response *lib.IntegrationChannelLimitResponse + fileToStruct(filepath.Join("../testdata", "integration_channel_limit_response.json"), &response) + + httpServer := IntegrationTestServer(t, IntegrationServerOptions[interface{}]{ + ExpectedRequest: IntegrationRequestDetails[interface{}]{ + Url: fmt.Sprintf("/v1/integrations/%s/limit", channelType), + Method: http.MethodGet, + }, + ExpectedResponse: IntegrationResponseDetails{ + StatusCode: http.StatusOK, + Body: response, + }, + }) + + ctx := context.Background() + novuClient := lib.NewAPIClient(novuApiKey, &lib.Config{BackendURL: lib.MustParseURL(httpServer.URL)}) + + res, err := novuClient.IntegrationsApi.GetChannelLimit(ctx, channelType) + + assert.Equal(t, response, res) + require.NoError(t, err) +} diff --git a/lib/layout.go b/lib/layout.go new file mode 100644 index 0000000..4f6672f --- /dev/null +++ b/lib/layout.go @@ -0,0 +1,133 @@ +package lib + +import ( + "bytes" + "context" + "encoding/json" + "net/http" +) + +type LayoutService service + +func (l *LayoutService) Create(ctx context.Context, request CreateLayoutRequest) (*CreateLayoutResponse, error) { + var resp CreateLayoutResponse + URL := l.client.config.BackendURL.JoinPath("layouts") + + requestBody := CreateLayoutRequest{ + Name: request.Name, + Identifier: request.Identifier, + Description: request.Description, + Content: request.Content, + Variables: request.Variables, + IsDefault: request.IsDefault, + } + + jsonBody, _ := json.Marshal(requestBody) + b := bytes.NewBuffer(jsonBody) + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, URL.String(), b) + if err != nil { + return nil, err + } + _, err = l.client.sendRequest(req, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (l *LayoutService) List(ctx context.Context, options *LayoutRequestOptions) (*LayoutsResponse, error) { + var resp LayoutsResponse + URL := l.client.config.BackendURL.JoinPath("layouts") + if options == nil { + options = &LayoutRequestOptions{} + } + queryParams, _ := json.Marshal(options) + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, URL.String(), bytes.NewBuffer(queryParams)) + if err != nil { + return nil, err + } + + _, err = l.client.sendRequest(req, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (l *LayoutService) Get(ctx context.Context, key string) (*LayoutResponse, error) { + var resp LayoutResponse + URL := l.client.config.BackendURL.JoinPath("layouts", key) + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, URL.String(), bytes.NewBuffer([]byte{})) + if err != nil { + return nil, err + } + + _, err = l.client.sendRequest(req, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (l *LayoutService) Delete(ctx context.Context, key string) error { + var resp interface{} + URL := l.client.config.BackendURL.JoinPath("layouts", key) + + req, err := http.NewRequestWithContext(ctx, http.MethodDelete, URL.String(), http.NoBody) + if err != nil { + return err + } + _, err = l.client.sendRequest(req, &resp) + if err != nil { + return err + } + return nil +} + +func (l *LayoutService) Update(ctx context.Context, key string, request CreateLayoutRequest) (*LayoutResponse, error) { + var resp LayoutResponse + URL := l.client.config.BackendURL.JoinPath("layouts", key) + + requestBody := CreateLayoutRequest{ + Name: request.Name, + Identifier: request.Identifier, + Description: request.Description, + Content: request.Content, + Variables: request.Variables, + IsDefault: request.IsDefault, + } + + jsonBody, _ := json.Marshal(requestBody) + + req, err := http.NewRequestWithContext(ctx, http.MethodPatch, URL.String(), bytes.NewBuffer(jsonBody)) + if err != nil { + return nil, err + } + + _, err = l.client.sendRequest(req, &resp) + if err != nil { + return nil, err + } + + return &resp, nil +} + +func (l *LayoutService) SetDefault(ctx context.Context, key string) error { + var resp interface{} + URL := l.client.config.BackendURL.JoinPath("layouts", key, "default") + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, URL.String(), http.NoBody) + if err != nil { + return err + } + + _, err = l.client.sendRequest(req, &resp) + if err != nil { + return err + } + + return nil +} diff --git a/lib/layout_test.go b/lib/layout_test.go new file mode 100644 index 0000000..9b6e8b7 --- /dev/null +++ b/lib/layout_test.go @@ -0,0 +1,207 @@ +package lib_test + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/novuhq/go-novu/lib" + "github.com/stretchr/testify/require" +) + +const LayoutId = "2222" + +func TestLayoutService_Create_Layout_Success(t *testing.T) { + var createLayoutRequest *lib.CreateLayoutRequest = &lib.CreateLayoutRequest{ + Name: "layoutName", + Identifier: "layoutIdentifier", + Description: "layoutDescription", + Content: "layoutContent", + Variables: []interface{}(nil), + IsDefault: true, + } + res, _ := json.Marshal(createLayoutRequest) + fmt.Println(string(res)) + var expectedResponse *lib.CreateLayoutResponse = &lib.CreateLayoutResponse{ + Data: struct { + Id string `json:"_id"` + }{ + Id: "2222", + }, + } + + httpServer := createTestServer(t, TestServerOptions[lib.CreateLayoutRequest, lib.CreateLayoutResponse]{ + expectedURLPath: "/v1/layouts", + expectedSentBody: *createLayoutRequest, + expectedSentMethod: http.MethodPost, + responseStatusCode: http.StatusCreated, + responseBody: *expectedResponse, + }) + + ctx := context.Background() + c := lib.NewAPIClient(novuApiKey, &lib.Config{BackendURL: lib.MustParseURL(httpServer.URL)}) + resp, err := c.LayoutApi.Create(ctx, *createLayoutRequest) + + require.NoError(t, err) + require.Equal(t, expectedResponse, resp) +} + +func TestLayoutService_List_Layouts_Success(t *testing.T) { + body := map[string]string{} + var expectedResponse *lib.LayoutsResponse = &lib.LayoutsResponse{ + Page: 0, + PageSize: 20, + TotalCount: 1, + Data: []lib.LayoutResponse{{ + Id: "id", + OrganizationId: "orgId", + EnvironmentId: "envId", + CreatorId: "creatorId", + Name: "layoutName", + Identifier: "layoutIdentifier", + Description: "layoutDescription", + Channel: "in_app", + Content: "layoutContent", + ContentType: "layoutContentType", + Variables: []interface{}{}, + IsDefault: true, + IsDeleted: false, + CreatedAt: "createdAt", + UpdatedAt: "updatedAt", + ParentId: "parentId", + }}, + } + httpServer := createTestServer(t, TestServerOptions[map[string]string, lib.LayoutsResponse]{ + expectedURLPath: "/v1/layouts", + expectedSentMethod: http.MethodGet, + expectedSentBody: body, + responseStatusCode: http.StatusCreated, + responseBody: *expectedResponse, + }) + + ctx := context.Background() + c := lib.NewAPIClient(novuApiKey, &lib.Config{BackendURL: lib.MustParseURL(httpServer.URL)}) + resp, err := c.LayoutApi.List(ctx, nil) + + require.NoError(t, err) + require.Equal(t, expectedResponse, resp) +} + +func TestLayoutService_Get_Layout_Success(t *testing.T) { + + var expectedResponse *lib.LayoutResponse = &lib.LayoutResponse{ + Id: "id", + OrganizationId: "orgId", + EnvironmentId: "envId", + CreatorId: "creatorId", + Name: "layoutName", + Identifier: "layoutIdentifier", + Description: "layoutDescription", + Channel: "in_app", + Content: "layoutContent", + ContentType: "layoutContentType", + Variables: []interface{}{}, + IsDefault: true, + IsDeleted: false, + CreatedAt: "createdAt", + UpdatedAt: "updatedAt", + ParentId: "parentId", + } + + httpServer := createTestServer(t, TestServerOptions[map[string]string, lib.LayoutResponse]{ + expectedURLPath: fmt.Sprintf("/v1/layouts/%s", LayoutId), + expectedSentMethod: http.MethodGet, + responseStatusCode: http.StatusOK, + responseBody: *expectedResponse, + }) + + ctx := context.Background() + c := lib.NewAPIClient(novuApiKey, &lib.Config{BackendURL: lib.MustParseURL(httpServer.URL)}) + resp, err := c.LayoutApi.Get(ctx, "2222") + + require.NoError(t, err) + require.Equal(t, expectedResponse, resp) +} + +func TestLayoutService_Delete_Layout_Success(t *testing.T) { + + body := map[string]string{} + + httpServer := createTestServer(t, TestServerOptions[map[string]string, map[string]string]{ + expectedURLPath: fmt.Sprintf("/v1/layouts/%s", LayoutId), + expectedSentMethod: http.MethodDelete, + responseStatusCode: http.StatusOK, + responseBody: body, + }) + + ctx := context.Background() + c := lib.NewAPIClient(novuApiKey, &lib.Config{BackendURL: lib.MustParseURL(httpServer.URL)}) + err := c.LayoutApi.Delete(ctx, "2222") + + require.NoError(t, err) +} + +func TestLayoutService_Update_Layout_Success(t *testing.T) { + + var updateLayoutRequest *lib.CreateLayoutRequest = &lib.CreateLayoutRequest{ + Name: "layoutName", + Identifier: "layoutIdentifier", + Description: "layoutDescription", + Content: "layoutContent", + Variables: []interface{}(nil), + IsDefault: false, + } + res, _ := json.Marshal(updateLayoutRequest) + fmt.Println(string(res)) + var expectedResponse *lib.LayoutResponse = &lib.LayoutResponse{ + Id: "id", + OrganizationId: "orgId", + EnvironmentId: "envId", + CreatorId: "creatorId", + Name: "layoutName", + Identifier: "layoutIdentifier", + Description: "layoutDescription", + Channel: "in_app", + Content: "layoutContent", + ContentType: "layoutContentType", + Variables: []interface{}{}, + IsDefault: true, + IsDeleted: false, + CreatedAt: "createdAt", + UpdatedAt: "updatedAt", + ParentId: "parentId", + } + httpServer := createTestServer[lib.CreateLayoutRequest, lib.LayoutResponse](t, TestServerOptions[lib.CreateLayoutRequest, lib.LayoutResponse]{ + expectedURLPath: fmt.Sprintf("/v1/layouts/%s", LayoutId), + expectedSentBody: *updateLayoutRequest, + expectedSentMethod: http.MethodPatch, + responseStatusCode: http.StatusOK, + responseBody: *expectedResponse, + }) + ctx := context.Background() + c := lib.NewAPIClient(novuApiKey, &lib.Config{BackendURL: lib.MustParseURL(httpServer.URL)}) + resp, err := c.LayoutApi.Update(ctx, "2222", *updateLayoutRequest) + require.NoError(t, err) + require.Equal(t, expectedResponse, resp) +} + +func TestLayoutService_Layout_SetDefault(t *testing.T) { + + body := map[string]string{} + + httpServer := createTestServer(t, TestServerOptions[map[string]string, map[string]string]{ + expectedURLPath: fmt.Sprintf("/v1/layouts/%s/default", LayoutId), + expectedSentBody: body, + expectedSentMethod: http.MethodPost, + responseStatusCode: http.StatusNoContent, + responseBody: body, + }) + + ctx := context.Background() + c := lib.NewAPIClient(novuApiKey, &lib.Config{BackendURL: lib.MustParseURL(httpServer.URL)}) + err := c.LayoutApi.SetDefault(ctx, LayoutId) + + require.NoError(t, err) +} diff --git a/lib/model.go b/lib/model.go index cf03654..cf0f22f 100644 --- a/lib/model.go +++ b/lib/model.go @@ -382,6 +382,30 @@ type GetIntegrationsResponse struct { Data []Integration `json:"data"` } +type IntegrationChannelLimitResponse struct { + Data struct { + Limit int `json:"limit"` + Count int `json:"count"` + } `json:"data"` +} + +type SetIntegrationAsPrimaryResponse struct { + Data struct { + ID string `json:"_id"` + EnvironmentID string `json:"_environmentId"` + OrganizationID string `json:"_organizationId"` + Name string `json:"name"` + Identifier string `json:"identifier"` + ProviderID string `json:"providerId"` + Channel string `json:"channel"` + Credentials IntegrationCredentials `json:"credentials"` + Active bool `json:"active"` + Deleted bool `json:"deleted"` + DeletedAt string `json:"deletedAt"` + DeletedBy string `json:"deletedBy"` + Primary bool `json:"primary"` + } `json:"data"` +} type BulkTriggerOptions struct { Name interface{} `json:"name,omitempty"` To interface{} `json:"to,omitempty"` @@ -409,6 +433,50 @@ type InboundParserResponse struct { Data MxRecordConfiguredStatus `json:"data"` } +type CreateLayoutRequest struct { + Name string `json:"name"` + Identifier string `json:"identifier"` + Description string `json:"description"` + Content string `json:"content"` + Variables []interface{} `json:"variables,omitempty"` + IsDefault bool `json:"isDefault,omitempty"` +} + +type CreateLayoutResponse struct { + Data struct { + Id string `json:"_id"` + } `json:"data"` +} +type LayoutRequestOptions struct { + Page *int `json:"page,omitempty"` + PageSize *int `json:"pageSize,omitempty"` + Key *string `json:"key,omitempty"` + OrderBy *int `json:"orderBy,omitempty"` +} +type LayoutResponse struct { + Id string `json:"_id"` + OrganizationId string `json:"_organizationId"` + EnvironmentId string `json:"_environmentId"` + CreatorId string `json:"_creatorId"` + Name string `json:"name"` + Identifier string `json:"identifier"` + Description string `json:"description"` + Channel string `json:"channel"` + Content string `json:"content"` + ContentType string `json:"contentType"` + Variables []interface{} `json:"variables"` + IsDefault bool `json:"isDefault"` + IsDeleted bool `json:"isDeleted"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` + ParentId string `json:"_parentId"` +} +type LayoutsResponse struct { + TotalCount int `json:"totalCount"` + Data []LayoutResponse `json:"data"` + PageSize int `json:"pageSize"` + Page int `json:"page"` +} type BlueprintByTemplateIdResponse struct { Id string `json:"_id,omitempty"` Name string `json:"name,omitempty"` @@ -437,5 +505,50 @@ type BlueprintByTemplateIdResponse struct { type BlueprintGroupByCategoryResponse struct { General []interface{} `json:"general,omitempty"` - Popular interface{} `json:"popular,omitempty"` + Popular interface{} `json:"popular,omitempty"` +} + +type ChangesGetQuery struct { + Page int `json:"page,omitempty"` + Limit int `json:"limit,omitempty"` + Promoted string `json:"promoted,omitempty"` +} + +type ChangesGetResponseData struct { + Id string `json:"_id,omitempty"` + CreatorId string `json:"_creatorId,omitempty"` + EnvironmentId string `json:"_environmentId,omitempty"` + OrganizationId string `json:"_organizationId,omitempty"` + EntityId string `json:"_entityId,omitempty"` + Enabled bool `json:"enabled,omitempty"` + Type string `json:"type,omitempty"` + Change interface{} `json:"change,omitempty"` + CreatedAt string `json:"createdAt,omitempty"` + ParentId string `json:"_parentId,omitempty"` +} + +type ChangesGetResponse struct { + TotalCount int `json:"totalCount,omitempty"` + Data []ChangesGetResponseData `json:"data"` + PageSize int `json:"pageSize,omitempty"` + Page int `json:"page,omitempty"` +} + +type ChangesCountResponse struct { + Data int `json:"data"` +} + +type ChangesBulkApplyPayload struct { + ChangeIds []string `json:"changeIds"` +} + +type ChangesApplyResponse struct { + Data []ChangesGetResponseData `json:"data,omitempty"` +} + + +type UpdateTenantRequest struct { + Name string `json:"name"` + Data map[string]interface{} `json:"data"` + Identifier string `json:"identifier"` } diff --git a/lib/novu.go b/lib/novu.go index 1ac40c6..5f5925c 100644 --- a/lib/novu.go +++ b/lib/novu.go @@ -41,6 +41,7 @@ type APIClient struct { // Api Service BlueprintApi *BlueprintService + ChangesApi *ChangesService SubscriberApi *SubscriberService EventApi *EventService ExecutionsApi *ExecutionsService @@ -49,6 +50,8 @@ type APIClient struct { TopicsApi *TopicService IntegrationsApi *IntegrationService InboundParserApi *InboundParserService + LayoutApi *LayoutService + TenantApi *TenantService } type service struct { @@ -97,6 +100,7 @@ func NewAPIClient(apiKey string, cfg *Config) *APIClient { c.common.client = c // API Services + c.ChangesApi = (*ChangesService)(&c.common) c.EventApi = (*EventService)(&c.common) c.ExecutionsApi = (*ExecutionsService)(&c.common) c.FeedsApi = (*FeedsService)(&c.common) @@ -105,7 +109,9 @@ func NewAPIClient(apiKey string, cfg *Config) *APIClient { c.TopicsApi = (*TopicService)(&c.common) c.IntegrationsApi = (*IntegrationService)(&c.common) c.InboundParserApi = (*InboundParserService)(&c.common) + c.LayoutApi = (*LayoutService)(&c.common) c.BlueprintApi = (*BlueprintService)(&c.common) + c.TenantApi = (*TenantService)(&c.common) return c } diff --git a/lib/tenants.go b/lib/tenants.go new file mode 100644 index 0000000..dc8f803 --- /dev/null +++ b/lib/tenants.go @@ -0,0 +1,90 @@ +package lib + +import ( + "bytes" + "context" + "encoding/json" + "net/http" +) + +type TenantService service + +func (e *TenantService) CreateTenant(ctx context.Context, name string,identifier string) (JsonResponse, error) { + var resp JsonResponse + URL := e.client.config.BackendURL.JoinPath("tenants") + n := map[string]string{"name": name,"identifier":identifier} + jsonBody, _ := json.Marshal(n) + b := bytes.NewBuffer(jsonBody) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, URL.String(), b) + if err != nil { + return resp, err + } + _, err = e.client.sendRequest(req, &resp) + if err != nil { + return resp, err + } + return resp, nil +} + +func (e *TenantService) GetTenants(ctx context.Context,page string,limit string) (JsonResponse, error) { + var resp JsonResponse + URL := e.client.config.BackendURL.JoinPath("tenants") + v := URL.Query(); + v.Set("page",page) + v.Set("limit",limit) + URL.RawQuery = v.Encode() + req, err := http.NewRequestWithContext(ctx, http.MethodGet, URL.String(), http.NoBody) + if err != nil { + return resp, err + } + _, err = e.client.sendRequest(req, &resp) + if err != nil { + return resp, err + } + return resp, nil +} + +func (e *TenantService) GetTenant(ctx context.Context,identifier string) (JsonResponse, error) { + var resp JsonResponse + URL := e.client.config.BackendURL.JoinPath("tenants",identifier) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, URL.String(), http.NoBody) + if err != nil { + return resp, err + } + _, err = e.client.sendRequest(req, &resp) + if err != nil { + return resp, err + } + return resp, nil +} + +func (e *TenantService) DeleteTenant(ctx context.Context, identifier string) (JsonResponse, error) { + var resp JsonResponse + URL := e.client.config.BackendURL.JoinPath("tenants", identifier) + req, err := http.NewRequestWithContext(ctx, http.MethodDelete, URL.String(), http.NoBody) + if err != nil { + return resp, err + } + _, err = e.client.sendRequest(req, &resp) + if err != nil { + return resp, err + } + return resp, nil +} + + +func (e *TenantService) UpdateTenant(ctx context.Context, identifier string,updateTenantObject *UpdateTenantRequest) (JsonResponse, error) { + var resp JsonResponse + URL := e.client.config.BackendURL.JoinPath("tenants", identifier) + jsonBody, _ := json.Marshal(updateTenantObject) + b := bytes.NewBuffer(jsonBody) + req, err := http.NewRequestWithContext(ctx, http.MethodPatch, URL.String(), b) + if err != nil { + return resp, err + } + _, err = e.client.sendRequest(req, &resp) + if err != nil { + return resp, err + } + return resp, nil +} diff --git a/lib/tenants_test.go b/lib/tenants_test.go new file mode 100644 index 0000000..d664313 --- /dev/null +++ b/lib/tenants_test.go @@ -0,0 +1,136 @@ +package lib_test + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/novuhq/go-novu/lib" +) + +var tenantsApiResponse = `{ + "data": { + "_environmentId": "string", + "_id": "string", + "createdAt": "string", + "data": "object", + "identifier": "string", + "name": "string", + "updatedAt": "string" + } + } + +` + +func TestCreateTenant(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + t.Errorf("Want POST, got %s", r.Method) + } + if r.URL.Path != "/v1/tenants" { + t.Errorf("Want /v1/tenants, got %s", r.URL.Path) + } + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(tenantsApiResponse)) + })) + defer server.Close() + c := lib.NewAPIClient(novuApiKey, &lib.Config{BackendURL: lib.MustParseURL(server.URL)}) + resp, err := c.TenantApi.CreateTenant(context.Background(), "Tenant", "TenantId") + if err != nil { + t.Errorf("Error should be nil, got %v", err) + } + if resp.Data == nil || resp.Data == "" { + t.Error("Expected response, got none") + } +} + +func TestGetTenants(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + t.Errorf("Want GET, got %s", r.Method) + } + if r.URL.Path != "/v1/tenants" { + t.Errorf("Want /v1/tenants, got %s", r.URL.Path) + } + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(tenantsApiResponse)) + })) + defer server.Close() + c := lib.NewAPIClient(novuApiKey, &lib.Config{BackendURL: lib.MustParseURL(server.URL)}) + resp, err := c.TenantApi.GetTenants(context.Background(), "1", "10") + if err != nil { + t.Errorf("Error should be nil, got %v", err) + } + if resp.Data == nil || resp.Data == "" { + t.Error("Expected response, got none") + } +} + +func TestGetTenant(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + t.Errorf("Want GET, got %s", r.Method) + } + if r.URL.Path != "/v1/tenants/TenantId" { + t.Errorf("Want /v1/feeds, got %s", r.URL.Path) + } + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(tenantsApiResponse)) + })) + defer server.Close() + c := lib.NewAPIClient(novuApiKey, &lib.Config{BackendURL: lib.MustParseURL(server.URL)}) + resp, err := c.TenantApi.GetTenant(context.Background(), "TenantId") + if err != nil { + t.Errorf("Error should be nil, got %v", err) + } + if resp.Data == nil || resp.Data == "" { + t.Error("Expected response, got none") + } +} + +func TestDeleteTenant(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodDelete { + t.Errorf("Want DELETE, got %s", r.Method) + } + if r.URL.Path != "/v1/tenants/TenantId" { + t.Errorf("Want /v1/tenants/TenantId, got %s", r.URL.Path) + } + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(tenantsApiResponse)) + })) + defer server.Close() + c := lib.NewAPIClient(novuApiKey, &lib.Config{BackendURL: lib.MustParseURL(server.URL)}) + resp, err := c.TenantApi.DeleteTenant(context.Background(), "TenantId") + if err != nil { + t.Errorf("Error should be nil, got %v", err) + } + if resp.Data == nil || resp.Data == "" { + t.Error("Expected response, got none") + } +} + +func TestUpdateTenant(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPatch { + t.Errorf("Want PATCH, got %s", r.Method) + } + if r.URL.Path != "/v1/tenants/TenantId" { + t.Errorf("Want /v1/tenants/TenantId, got %s", r.URL.Path) + } + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(tenantsApiResponse)) + })) + defer server.Close() + c := lib.NewAPIClient(novuApiKey, &lib.Config{BackendURL: lib.MustParseURL(server.URL)}) + resp, err := c.TenantApi.UpdateTenant(context.Background(), "TenantId", &lib.UpdateTenantRequest{ + Name: "Tenant2", + }) + if err != nil { + t.Errorf("Error should be nil, got %v", err) + } + if resp.Data == nil || resp.Data == "" { + t.Error("Expected response, got none") + } +} diff --git a/testdata/integration_channel_limit_response.json b/testdata/integration_channel_limit_response.json new file mode 100644 index 0000000..7f67af3 --- /dev/null +++ b/testdata/integration_channel_limit_response.json @@ -0,0 +1,6 @@ +{ + "data": { + "limit": 0, + "count": 0 + } +} diff --git a/testdata/set_integration_as_primary_response.json b/testdata/set_integration_as_primary_response.json new file mode 100644 index 0000000..b0eeb0f --- /dev/null +++ b/testdata/set_integration_as_primary_response.json @@ -0,0 +1,43 @@ +{ + "data": { + "_id": "string", + "_environmentId": "string", + "_organizationId": "string", + "name": "string", + "identifier": "string", + "providerId": "string", + "channel": "in_app", + "credentials": { + "apiKey": "string", + "user": "string", + "secretKey": "string", + "domain": "string", + "password": "string", + "host": "string", + "port": "string", + "secure": true, + "region": "string", + "accountSid": "string", + "messageProfileId": "string", + "token": "string", + "from": "string", + "senderName": "string", + "projectName": "string", + "applicationId": "string", + "clientId": "string", + "requireTls": true, + "ignoreTls": true, + "baseUrl": "string", + "webhookUrl": "string", + "redirectUrl": "string", + "hmac": true, + "serviceAccount": "string", + "ipPoolName": "string" + }, + "active": true, + "deleted": true, + "deletedAt": "string", + "deletedBy": "string", + "primary": true + } +}