Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion plugins/source/hubspot/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func New(_ context.Context, logger zerolog.Logger, s spec.Spec) (schema.ClientMe
Authorizer: hubspot.NewTokenAuthorizer(authToken),
Spec: s,
RateLimiter: rate.NewLimiter(
/* r= */ rate.Limit(*s.MaxRequestsPerSecond),
/* r= */ rate.Limit(s.MaxRequestsPerSecond),
/* b= */ 1,
),
}, nil
Expand Down
20 changes: 9 additions & 11 deletions plugins/source/hubspot/client/spec/schema.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 27 additions & 9 deletions plugins/source/hubspot/client/spec/spec.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
package spec

import _ "embed"
import (
_ "embed"
"fmt"
"os"
)

const (
defaultConcurrency = 1000
)

type Spec struct {
// In order for CloudQuery to sync resources from your HubSpot setup, you will need to authenticate with your HubSpot account. You will need to create a [HubSpot Private App](https://developers.hubspot.com/docs/api/private-apps), and copy the App Token here.
// If not specified `HUBSPOT_APP_TOKEN` environment variable will be used instead.
AppToken string `json:"app_token,omitempty" jsonschema:"minLength=1"`
// Max number of requests per second to perform against the Hubspot API.
MaxRequestsPerSecond *int `yaml:"max_requests_per_second,omitempty" json:"max_requests_per_second,omitempty" jsonschema:"minimum=1,default=5"`
MaxRequestsPerSecond int `json:"max_requests_per_second,omitempty" jsonschema:"minimum=1,default=5"`
// Key-value map of options for each table. The key is the name of the table. The value is an options object.
TableOptions TableOptions `yaml:"table_options,omitempty" json:"table_options,omitempty"`
TableOptions TableOptions `json:"table_options,omitempty"`
// Concurrency setting for the CloudQuery scheduler.
Concurrency int `yaml:"concurrency,omitempty" json:"concurrency,omitempty" jsonschema:"minimum=1,default=1000"`
Concurrency int `json:"concurrency,omitempty" jsonschema:"minimum=1,default=1000"`
}

func (spec *Spec) SetDefaults() {
func (s *Spec) SetDefaults() {
if s.AppToken == "" {
Comment thread
disq marked this conversation as resolved.
Outdated
s.AppToken = os.Getenv("HUBSPOT_APP_TOKEN")
}

// https://developers.hubspot.com/docs/api/usage-details#rate-limits
// Hubspot, for Pro and Enterprise, accounts, has rate limits of:
// - 15 requests / second / private-app
Expand All @@ -24,13 +35,20 @@ func (spec *Spec) SetDefaults() {
// subscriptions in case cloudquery is run 24/7).
var defaultRateLimitPerSecond = 5

if spec.MaxRequestsPerSecond == nil || *spec.MaxRequestsPerSecond <= 0 {
spec.MaxRequestsPerSecond = &defaultRateLimitPerSecond
if s.MaxRequestsPerSecond <= 0 {
s.MaxRequestsPerSecond = defaultRateLimitPerSecond
}

if spec.Concurrency == 0 {
spec.Concurrency = defaultConcurrency
if s.Concurrency == 0 {
Comment thread
disq marked this conversation as resolved.
Outdated
s.Concurrency = defaultConcurrency
}
}

func (s Spec) Validate() error {
if s.AppToken == "" {
return fmt.Errorf("app_token is required")
}
return nil
}

//go:embed schema.json
Expand Down
48 changes: 31 additions & 17 deletions plugins/source/hubspot/client/spec/spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

"github.com/cloudquery/codegen/jsonschema"
"github.com/stretchr/testify/require"
)

func TestJSONSchema(t *testing.T) {
Expand All @@ -13,57 +14,70 @@ func TestJSONSchema(t *testing.T) {
Spec: `{}`,
},
{
Name: "null max_requests_per_second",
Spec: `{"max_requests_per_second": null}`,
Name: "app_token",
Spec: `{"app_token": "token"}`,
},
{
Name: "empty app_token",
Spec: `{"app_token": ""}`,
Err: true,
},
{
Name: "max_requests_per_second == -1 is invalid",
Spec: `{"max_requests_per_second": -1}`,
Spec: `{"app_token": "token", "max_requests_per_second": -1}`,
Err: true,
},
{
Name: "max_requests_per_second == 0 is invalid",
Spec: `{"max_requests_per_second": 0}`,
Spec: `{"app_token": "token", "max_requests_per_second": 0}`,
Err: true,
},
{
Name: "max_requests_per_second == 1 is valid",
Spec: `{"max_requests_per_second": 1}`,
Spec: `{"app_token": "token", "max_requests_per_second": 1}`,
},
{
Name: "concurrency == -1 is invalid",
Spec: `{"concurrency": -1}`,
Spec: `{"app_token": "token", "concurrency": -1}`,
Err: true,
},
{
Name: "concurrency == 0 is invalid",
Spec: `{"concurrency": 0}`,
Spec: `{"app_token": "token", "concurrency": 0}`,
Err: true,
},
{
Name: "concurrency == 1 is valid",
Spec: `{"concurrency": 1}`,
Spec: `{"app_token": "token", "concurrency": 1}`,
},
{
Name: "table_options == null is valid",
Spec: `{"table_options": null}`,
Spec: `{"app_token": "token", "table_options": null}`,
},
})
}

func TestTableOptionsJSONSchema(t *testing.T) {
schema, err := jsonschema.Generate(TableOptions{})

require.NoError(t, err)
jsonschema.TestJSONSchema(t, string(schema), []jsonschema.TestCase{
{
Name: "table_options == {} is valid",
Spec: `{"table_options": {}}`,
Name: "empty table options",
Spec: `{}`,
},
{
Name: "spec with table options = null is invalid",
Spec: `{"table_options": {"hubspot_crm_companies": null}}`,
Name: "hubspot_crm_companies = null is invalid",
Spec: `{"hubspot_crm_companies": null}`,
},
{
Name: "spec with table options = [] is invalid",
Spec: `{"table_options": {"hubspot_crm_companies": []}}`,
Name: "hubspot_crm_companiess = [] is invalid",
Spec: `{"hubspot_crm_companies": []}`,
Err: true,
},
{
Name: "spec with table options = {} is valid",
Spec: `{"table_options": {"hubspot_crm_companies": {}}}`,
Name: "hubspot_crm_companies = {} is valid",
Spec: `{"hubspot_crm_companies": {}}`,
},
})
}
4 changes: 2 additions & 2 deletions plugins/source/hubspot/client/spec/table_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ type TableOptions map[string]*TableOptionsSpec
// Table options spec.
type TableOptionsSpec struct {
// List of properties to sync. If empty, everything is synced.
Properties []string `yaml:"properties,omitempty" json:"properties,omitempty" jsonschema:"minLength=1"`
Properties []string `json:"properties,omitempty" jsonschema:"minLength=1"`
// List of associations to sync. If empty, everything is synced.
Associations []string `yaml:"associations,omitempty" json:"associations,omitempty" jsonschema:"minLength=1"`
Associations []string `json:"associations,omitempty" jsonschema:"minLength=1"`
}

func (ts TableOptions) ForTable(name string) *TableOptionsSpec {
Expand Down
5 changes: 3 additions & 2 deletions plugins/source/hubspot/docs/_authentication.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
In Order for CloudQuery to sync resources from your HubSpot setup, you will need to authenticate with your HubSpot account. You will need to create a [HubSpot Private App](https://developers.hubspot.com/docs/api/private-apps), and export the App Token in `HUBSPOT_APP_TOKEN` environment variable.
In Order for CloudQuery to sync resources from your HubSpot setup, you will need to authenticate with your HubSpot account. You will need to create a [HubSpot Private App](https://developers.hubspot.com/docs/api/private-apps), and copy the App Token to the spec.
If not specified `HUBSPOT_APP_TOKEN` environment variable will be used instead.

```bash copy
export HUBSPOT_APP_TOKEN=<your_app_token>
export HUBSPOT_APP_TOKEN=<your_app_token> # optional, if not using spec configuration
```
4 changes: 4 additions & 0 deletions plugins/source/hubspot/docs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ The following example sets up the HubSpot plugin, and connects it to a postgresq

This is the specs that can be used by the HubSpot source Plugin.

- `app_token` (`string`, optional, default: `HUBSPOT_APP_TOKEN` environment variable)
Comment thread
disq marked this conversation as resolved.
Outdated
The HubSpot App Token to use for authentication. This can also be set with the `HUBSPOT_APP_TOKEN` environment variable.

- `concurrency` (int, optional, default: `1000`):

A best effort maximum number of Go routines to use. Lower this number to reduce memory usage.

- `max_requests_per_second` (`int`, optional. Default: `5`)
Expand Down
3 changes: 3 additions & 0 deletions plugins/source/hubspot/resources/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ func newClient(ctx context.Context, logger zerolog.Logger, specBytes []byte, opt
return nil, err
}
s.SetDefaults()
if err := s.Validate(); err != nil {
return nil, err
}
syncClient, err := client.New(ctx, logger, *s)
if err != nil {
return nil, err
Expand Down