[misc] Add cloud api spec to public open api with rest client (#5222)

This commit is contained in:
Bethuel Mmbaga
2026-02-13 13:08:47 +01:00
committed by GitHub
parent 7ebf37ef20
commit d3eeb6d8ee
27 changed files with 7369 additions and 94 deletions

View File

@@ -0,0 +1,82 @@
package rest
import (
"context"
"github.com/netbirdio/netbird/shared/management/http/api"
)
// BillingAPI APIs for billing and invoices
type BillingAPI struct {
c *Client
}
// GetUsage retrieves current usage statistics for the account
// See more: https://docs.netbird.io/api/resources/billing#get-current-usage
func (a *BillingAPI) GetUsage(ctx context.Context) (*api.UsageStats, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/billing/usage", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.UsageStats](resp)
return &ret, err
}
// GetSubscription retrieves the current subscription details
// See more: https://docs.netbird.io/api/resources/billing#get-current-subscription
func (a *BillingAPI) GetSubscription(ctx context.Context) (*api.Subscription, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/billing/subscription", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.Subscription](resp)
return &ret, err
}
// GetInvoices retrieves the account's paid invoices
// See more: https://docs.netbird.io/api/resources/billing#list-all-invoices
func (a *BillingAPI) GetInvoices(ctx context.Context) ([]api.InvoiceResponse, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/billing/invoices", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.InvoiceResponse](resp)
return ret, err
}
// GetInvoicePDF retrieves the invoice PDF URL
// See more: https://docs.netbird.io/api/resources/billing#get-invoice-pdf
func (a *BillingAPI) GetInvoicePDF(ctx context.Context, invoiceID string) (*api.InvoicePDFResponse, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/billing/invoices/"+invoiceID+"/pdf", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.InvoicePDFResponse](resp)
return &ret, err
}
// GetInvoiceCSV retrieves the invoice CSV content
// See more: https://docs.netbird.io/api/resources/billing#get-invoice-csv
func (a *BillingAPI) GetInvoiceCSV(ctx context.Context, invoiceID string) (string, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/billing/invoices/"+invoiceID+"/csv", nil, nil)
if err != nil {
return "", err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[string](resp)
return ret, err
}

View File

@@ -0,0 +1,194 @@
//go:build integration
package rest_test
import (
"context"
"encoding/json"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/shared/management/client/rest"
"github.com/netbirdio/netbird/shared/management/http/api"
"github.com/netbirdio/netbird/shared/management/http/util"
)
var (
testUsageStats = api.UsageStats{
ActiveUsers: 15,
TotalUsers: 20,
ActivePeers: 10,
TotalPeers: 25,
}
testSubscription = api.Subscription{
Active: true,
PlanTier: "basic",
PriceId: "price_1HhxOp",
Currency: "USD",
Price: 1000,
Provider: "stripe",
UpdatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
}
testInvoice = api.InvoiceResponse{
Id: "inv_123",
PeriodStart: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
PeriodEnd: time.Date(2024, 2, 1, 0, 0, 0, 0, time.UTC),
Type: "invoice",
}
testInvoicePDF = api.InvoicePDFResponse{
Url: "https://example.com/invoice.pdf",
}
)
func TestBilling_GetUsage_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/billing/usage", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal(testUsageStats)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Billing.GetUsage(context.Background())
require.NoError(t, err)
assert.Equal(t, testUsageStats, *ret)
})
}
func TestBilling_GetUsage_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/billing/usage", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Billing.GetUsage(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestBilling_GetSubscription_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/billing/subscription", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal(testSubscription)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Billing.GetSubscription(context.Background())
require.NoError(t, err)
assert.Equal(t, testSubscription, *ret)
})
}
func TestBilling_GetSubscription_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/billing/subscription", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Billing.GetSubscription(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestBilling_GetInvoices_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/billing/invoices", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal([]api.InvoiceResponse{testInvoice})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Billing.GetInvoices(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testInvoice, ret[0])
})
}
func TestBilling_GetInvoices_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/billing/invoices", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Billing.GetInvoices(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestBilling_GetInvoicePDF_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/billing/invoices/inv_123/pdf", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal(testInvoicePDF)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Billing.GetInvoicePDF(context.Background(), "inv_123")
require.NoError(t, err)
assert.Equal(t, testInvoicePDF, *ret)
})
}
func TestBilling_GetInvoicePDF_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/billing/invoices/inv_123/pdf", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Billing.GetInvoicePDF(context.Background(), "inv_123")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Nil(t, ret)
})
}
func TestBilling_GetInvoiceCSV_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/billing/invoices/inv_123/csv", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal("col1,col2\nval1,val2")
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Billing.GetInvoiceCSV(context.Background(), "inv_123")
require.NoError(t, err)
assert.Equal(t, "col1,col2\nval1,val2", ret)
})
}
func TestBilling_GetInvoiceCSV_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/billing/invoices/inv_123/csv", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Billing.GetInvoiceCSV(context.Background(), "inv_123")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Empty(t, ret)
})
}

View File

@@ -73,6 +73,38 @@ type Client struct {
// Events NetBird Events APIs
// see more: https://docs.netbird.io/api/resources/events
Events *EventsAPI
// Billing NetBird Billing APIs for subscriptions, plans, and invoices
// see more: https://docs.netbird.io/api/resources/billing
Billing *BillingAPI
// MSP NetBird MSP tenant management APIs
// see more: https://docs.netbird.io/api/resources/msp
MSP *MSPAPI
// EDR NetBird EDR integration APIs (Intune, SentinelOne, Falcon, Huntress)
// see more: https://docs.netbird.io/api/resources/edr
EDR *EDRAPI
// SCIM NetBird SCIM IDP integration APIs
// see more: https://docs.netbird.io/api/resources/scim
SCIM *SCIMAPI
// EventStreaming NetBird Event Streaming integration APIs
// see more: https://docs.netbird.io/api/resources/event-streaming
EventStreaming *EventStreamingAPI
// IdentityProviders NetBird Identity Providers APIs
// see more: https://docs.netbird.io/api/resources/identity-providers
IdentityProviders *IdentityProvidersAPI
// Ingress NetBird Ingress Peers APIs
// see more: https://docs.netbird.io/api/resources/ingress-ports
Ingress *IngressAPI
// Instance NetBird Instance API
// see more: https://docs.netbird.io/api/resources/instance
Instance *InstanceAPI
}
// New initialize new Client instance using PAT token
@@ -120,6 +152,14 @@ func (c *Client) initialize() {
c.DNSZones = &DNSZonesAPI{c}
c.GeoLocation = &GeoLocationAPI{c}
c.Events = &EventsAPI{c}
c.Billing = &BillingAPI{c}
c.MSP = &MSPAPI{c}
c.EDR = &EDRAPI{c}
c.SCIM = &SCIMAPI{c}
c.EventStreaming = &EventStreamingAPI{c}
c.IdentityProviders = &IdentityProvidersAPI{c}
c.Ingress = &IngressAPI{c}
c.Instance = &InstanceAPI{c}
}
// NewRequest creates and executes new management API request

View File

@@ -0,0 +1,307 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/shared/management/http/api"
)
// EDRAPI APIs for EDR integrations (Intune, SentinelOne, Falcon, Huntress)
type EDRAPI struct {
c *Client
}
// GetIntuneIntegration retrieves the EDR Intune integration
// See more: https://docs.netbird.io/api/resources/edr#get-intune-integration
func (a *EDRAPI) GetIntuneIntegration(ctx context.Context) (*api.EDRIntuneResponse, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/edr/intune", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.EDRIntuneResponse](resp)
return &ret, err
}
// CreateIntuneIntegration creates a new EDR Intune integration
// See more: https://docs.netbird.io/api/resources/edr#create-intune-integration
func (a *EDRAPI) CreateIntuneIntegration(ctx context.Context, request api.EDRIntuneRequest) (*api.EDRIntuneResponse, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/integrations/edr/intune", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.EDRIntuneResponse](resp)
return &ret, err
}
// UpdateIntuneIntegration updates an existing EDR Intune integration
// See more: https://docs.netbird.io/api/resources/edr#update-intune-integration
func (a *EDRAPI) UpdateIntuneIntegration(ctx context.Context, request api.EDRIntuneRequest) (*api.EDRIntuneResponse, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "PUT", "/api/integrations/edr/intune", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.EDRIntuneResponse](resp)
return &ret, err
}
// DeleteIntuneIntegration deletes the EDR Intune integration
// See more: https://docs.netbird.io/api/resources/edr#delete-intune-integration
func (a *EDRAPI) DeleteIntuneIntegration(ctx context.Context) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/integrations/edr/intune", nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}
// GetSentinelOneIntegration retrieves the EDR SentinelOne integration
// See more: https://docs.netbird.io/api/resources/edr#get-sentinelone-integration
func (a *EDRAPI) GetSentinelOneIntegration(ctx context.Context) (*api.EDRSentinelOneResponse, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/edr/sentinelone", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.EDRSentinelOneResponse](resp)
return &ret, err
}
// CreateSentinelOneIntegration creates a new EDR SentinelOne integration
// See more: https://docs.netbird.io/api/resources/edr#create-sentinelone-integration
func (a *EDRAPI) CreateSentinelOneIntegration(ctx context.Context, request api.EDRSentinelOneRequest) (*api.EDRSentinelOneResponse, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/integrations/edr/sentinelone", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.EDRSentinelOneResponse](resp)
return &ret, err
}
// UpdateSentinelOneIntegration updates an existing EDR SentinelOne integration
// See more: https://docs.netbird.io/api/resources/edr#update-sentinelone-integration
func (a *EDRAPI) UpdateSentinelOneIntegration(ctx context.Context, request api.EDRSentinelOneRequest) (*api.EDRSentinelOneResponse, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "PUT", "/api/integrations/edr/sentinelone", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.EDRSentinelOneResponse](resp)
return &ret, err
}
// DeleteSentinelOneIntegration deletes the EDR SentinelOne integration
// See more: https://docs.netbird.io/api/resources/edr#delete-sentinelone-integration
func (a *EDRAPI) DeleteSentinelOneIntegration(ctx context.Context) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/integrations/edr/sentinelone", nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}
// GetFalconIntegration retrieves the EDR Falcon integration
// See more: https://docs.netbird.io/api/resources/edr#get-falcon-integration
func (a *EDRAPI) GetFalconIntegration(ctx context.Context) (*api.EDRFalconResponse, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/edr/falcon", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.EDRFalconResponse](resp)
return &ret, err
}
// CreateFalconIntegration creates a new EDR Falcon integration
// See more: https://docs.netbird.io/api/resources/edr#create-falcon-integration
func (a *EDRAPI) CreateFalconIntegration(ctx context.Context, request api.EDRFalconRequest) (*api.EDRFalconResponse, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/integrations/edr/falcon", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.EDRFalconResponse](resp)
return &ret, err
}
// UpdateFalconIntegration updates an existing EDR Falcon integration
// See more: https://docs.netbird.io/api/resources/edr#update-falcon-integration
func (a *EDRAPI) UpdateFalconIntegration(ctx context.Context, request api.EDRFalconRequest) (*api.EDRFalconResponse, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "PUT", "/api/integrations/edr/falcon", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.EDRFalconResponse](resp)
return &ret, err
}
// DeleteFalconIntegration deletes the EDR Falcon integration
// See more: https://docs.netbird.io/api/resources/edr#delete-falcon-integration
func (a *EDRAPI) DeleteFalconIntegration(ctx context.Context) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/integrations/edr/falcon", nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}
// GetHuntressIntegration retrieves the EDR Huntress integration
// See more: https://docs.netbird.io/api/resources/edr#get-huntress-integration
func (a *EDRAPI) GetHuntressIntegration(ctx context.Context) (*api.EDRHuntressResponse, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/edr/huntress", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.EDRHuntressResponse](resp)
return &ret, err
}
// CreateHuntressIntegration creates a new EDR Huntress integration
// See more: https://docs.netbird.io/api/resources/edr#create-huntress-integration
func (a *EDRAPI) CreateHuntressIntegration(ctx context.Context, request api.EDRHuntressRequest) (*api.EDRHuntressResponse, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/integrations/edr/huntress", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.EDRHuntressResponse](resp)
return &ret, err
}
// UpdateHuntressIntegration updates an existing EDR Huntress integration
// See more: https://docs.netbird.io/api/resources/edr#update-huntress-integration
func (a *EDRAPI) UpdateHuntressIntegration(ctx context.Context, request api.EDRHuntressRequest) (*api.EDRHuntressResponse, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "PUT", "/api/integrations/edr/huntress", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.EDRHuntressResponse](resp)
return &ret, err
}
// DeleteHuntressIntegration deletes the EDR Huntress integration
// See more: https://docs.netbird.io/api/resources/edr#delete-huntress-integration
func (a *EDRAPI) DeleteHuntressIntegration(ctx context.Context) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/integrations/edr/huntress", nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}
// BypassPeerCompliance bypasses compliance for a non-compliant peer
// See more: https://docs.netbird.io/api/resources/edr#bypass-peer-compliance
func (a *EDRAPI) BypassPeerCompliance(ctx context.Context, peerID string) (*api.BypassResponse, error) {
resp, err := a.c.NewRequest(ctx, "POST", "/api/peers/"+peerID+"/edr/bypass", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.BypassResponse](resp)
return &ret, err
}
// RevokePeerBypass revokes the compliance bypass for a peer
// See more: https://docs.netbird.io/api/resources/edr#revoke-peer-bypass
func (a *EDRAPI) RevokePeerBypass(ctx context.Context, peerID string) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/peers/"+peerID+"/edr/bypass", nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}
// ListBypassedPeers returns all peers that have compliance bypassed
// See more: https://docs.netbird.io/api/resources/edr#list-all-bypassed-peers
func (a *EDRAPI) ListBypassedPeers(ctx context.Context) ([]api.BypassResponse, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/peers/edr/bypassed", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.BypassResponse](resp)
return ret, err
}

View File

@@ -0,0 +1,422 @@
//go:build integration
package rest_test
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/shared/management/client/rest"
"github.com/netbirdio/netbird/shared/management/http/api"
"github.com/netbirdio/netbird/shared/management/http/util"
)
var (
testIntuneResponse = api.EDRIntuneResponse{
AccountId: "acc-1",
ClientId: "client-1",
TenantId: "tenant-1",
Enabled: true,
Id: 1,
Groups: []api.Group{},
LastSyncedInterval: 24,
CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
LastSyncedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
UpdatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
CreatedBy: "user-1",
}
testSentinelOneResponse = api.EDRSentinelOneResponse{
AccountId: "acc-1",
ApiUrl: "https://sentinelone.example.com",
Enabled: true,
Id: 2,
Groups: []api.Group{},
LastSyncedInterval: 24,
MatchAttributes: api.SentinelOneMatchAttributes{},
CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
LastSyncedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
UpdatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
CreatedBy: "user-1",
}
testFalconResponse = api.EDRFalconResponse{
AccountId: "acc-1",
CloudId: "us-1",
Enabled: true,
Id: 3,
Groups: []api.Group{},
ZtaScoreThreshold: 50,
CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
LastSyncedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
UpdatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
CreatedBy: "user-1",
}
testHuntressResponse = api.EDRHuntressResponse{
AccountId: "acc-1",
Enabled: true,
Id: 4,
Groups: []api.Group{},
LastSyncedInterval: 24,
MatchAttributes: api.HuntressMatchAttributes{},
CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
LastSyncedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
UpdatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
CreatedBy: "user-1",
}
testBypassResponse = api.BypassResponse{
PeerId: "peer-1",
}
)
// Intune tests
func TestEDR_GetIntuneIntegration_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/edr/intune", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal(testIntuneResponse)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EDR.GetIntuneIntegration(context.Background())
require.NoError(t, err)
assert.Equal(t, testIntuneResponse, *ret)
})
}
func TestEDR_GetIntuneIntegration_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/edr/intune", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EDR.GetIntuneIntegration(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestEDR_CreateIntuneIntegration_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/edr/intune", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.EDRIntuneRequest
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "client-1", req.ClientId)
retBytes, _ := json.Marshal(testIntuneResponse)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EDR.CreateIntuneIntegration(context.Background(), api.EDRIntuneRequest{
ClientId: "client-1",
Secret: "secret",
TenantId: "tenant-1",
Groups: []string{"group-1"},
LastSyncedInterval: 24,
})
require.NoError(t, err)
assert.Equal(t, testIntuneResponse, *ret)
})
}
func TestEDR_CreateIntuneIntegration_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/edr/intune", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EDR.CreateIntuneIntegration(context.Background(), api.EDRIntuneRequest{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestEDR_UpdateIntuneIntegration_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/edr/intune", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
retBytes, _ := json.Marshal(testIntuneResponse)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EDR.UpdateIntuneIntegration(context.Background(), api.EDRIntuneRequest{
ClientId: "client-1",
Secret: "new-secret",
TenantId: "tenant-1",
Groups: []string{"group-1"},
})
require.NoError(t, err)
assert.Equal(t, testIntuneResponse, *ret)
})
}
func TestEDR_DeleteIntuneIntegration_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/edr/intune", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.EDR.DeleteIntuneIntegration(context.Background())
require.NoError(t, err)
})
}
func TestEDR_DeleteIntuneIntegration_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/edr/intune", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.EDR.DeleteIntuneIntegration(context.Background())
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
// SentinelOne tests
func TestEDR_GetSentinelOneIntegration_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/edr/sentinelone", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal(testSentinelOneResponse)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EDR.GetSentinelOneIntegration(context.Background())
require.NoError(t, err)
assert.Equal(t, testSentinelOneResponse, *ret)
})
}
func TestEDR_CreateSentinelOneIntegration_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/edr/sentinelone", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
retBytes, _ := json.Marshal(testSentinelOneResponse)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EDR.CreateSentinelOneIntegration(context.Background(), api.EDRSentinelOneRequest{
ApiToken: "token",
ApiUrl: "https://sentinelone.example.com",
Groups: []string{"group-1"},
LastSyncedInterval: 24,
MatchAttributes: api.SentinelOneMatchAttributes{},
})
require.NoError(t, err)
assert.Equal(t, testSentinelOneResponse, *ret)
})
}
func TestEDR_DeleteSentinelOneIntegration_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/edr/sentinelone", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.EDR.DeleteSentinelOneIntegration(context.Background())
require.NoError(t, err)
})
}
// Falcon tests
func TestEDR_GetFalconIntegration_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/edr/falcon", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal(testFalconResponse)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EDR.GetFalconIntegration(context.Background())
require.NoError(t, err)
assert.Equal(t, testFalconResponse, *ret)
})
}
func TestEDR_CreateFalconIntegration_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/edr/falcon", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
retBytes, _ := json.Marshal(testFalconResponse)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EDR.CreateFalconIntegration(context.Background(), api.EDRFalconRequest{
ClientId: "client-1",
Secret: "secret",
CloudId: "us-1",
Groups: []string{"group-1"},
ZtaScoreThreshold: 50,
})
require.NoError(t, err)
assert.Equal(t, testFalconResponse, *ret)
})
}
func TestEDR_DeleteFalconIntegration_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/edr/falcon", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.EDR.DeleteFalconIntegration(context.Background())
require.NoError(t, err)
})
}
// Huntress tests
func TestEDR_GetHuntressIntegration_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/edr/huntress", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal(testHuntressResponse)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EDR.GetHuntressIntegration(context.Background())
require.NoError(t, err)
assert.Equal(t, testHuntressResponse, *ret)
})
}
func TestEDR_CreateHuntressIntegration_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/edr/huntress", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
retBytes, _ := json.Marshal(testHuntressResponse)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EDR.CreateHuntressIntegration(context.Background(), api.EDRHuntressRequest{
ApiKey: "key",
ApiSecret: "secret",
Groups: []string{"group-1"},
LastSyncedInterval: 24,
MatchAttributes: api.HuntressMatchAttributes{},
})
require.NoError(t, err)
assert.Equal(t, testHuntressResponse, *ret)
})
}
func TestEDR_DeleteHuntressIntegration_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/edr/huntress", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.EDR.DeleteHuntressIntegration(context.Background())
require.NoError(t, err)
})
}
// Peer bypass tests
func TestEDR_BypassPeerCompliance_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/peer-1/edr/bypass", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
retBytes, _ := json.Marshal(testBypassResponse)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EDR.BypassPeerCompliance(context.Background(), "peer-1")
require.NoError(t, err)
assert.Equal(t, testBypassResponse, *ret)
})
}
func TestEDR_BypassPeerCompliance_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/peer-1/edr/bypass", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Bad request", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EDR.BypassPeerCompliance(context.Background(), "peer-1")
assert.Error(t, err)
assert.Equal(t, "Bad request", err.Error())
assert.Nil(t, ret)
})
}
func TestEDR_RevokePeerBypass_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/peer-1/edr/bypass", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.EDR.RevokePeerBypass(context.Background(), "peer-1")
require.NoError(t, err)
})
}
func TestEDR_RevokePeerBypass_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/peer-1/edr/bypass", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.EDR.RevokePeerBypass(context.Background(), "peer-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestEDR_ListBypassedPeers_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/edr/bypassed", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal([]api.BypassResponse{testBypassResponse})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EDR.ListBypassedPeers(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testBypassResponse, ret[0])
})
}
func TestEDR_ListBypassedPeers_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/edr/bypassed", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EDR.ListBypassedPeers(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}

View File

@@ -0,0 +1,92 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"strconv"
"github.com/netbirdio/netbird/shared/management/http/api"
)
// EventStreamingAPI APIs for event streaming integrations
type EventStreamingAPI struct {
c *Client
}
// List retrieves all event streaming integrations
// See more: https://docs.netbird.io/api/resources/event-streaming#list-all-event-streaming-integrations
func (a *EventStreamingAPI) List(ctx context.Context) ([]api.IntegrationResponse, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/event-streaming", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.IntegrationResponse](resp)
return ret, err
}
// Get retrieves a specific event streaming integration by ID
// See more: https://docs.netbird.io/api/resources/event-streaming#retrieve-an-event-streaming-integration
func (a *EventStreamingAPI) Get(ctx context.Context, integrationID int) (*api.IntegrationResponse, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/event-streaming/"+strconv.Itoa(integrationID), nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.IntegrationResponse](resp)
return &ret, err
}
// Create creates a new event streaming integration
// See more: https://docs.netbird.io/api/resources/event-streaming#create-an-event-streaming-integration
func (a *EventStreamingAPI) Create(ctx context.Context, request api.CreateIntegrationRequest) (*api.IntegrationResponse, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/event-streaming", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.IntegrationResponse](resp)
return &ret, err
}
// Update updates an existing event streaming integration
// See more: https://docs.netbird.io/api/resources/event-streaming#update-an-event-streaming-integration
func (a *EventStreamingAPI) Update(ctx context.Context, integrationID int, request api.CreateIntegrationRequest) (*api.IntegrationResponse, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "PUT", "/api/event-streaming/"+strconv.Itoa(integrationID), bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.IntegrationResponse](resp)
return &ret, err
}
// Delete deletes an event streaming integration
// See more: https://docs.netbird.io/api/resources/event-streaming#delete-an-event-streaming-integration
func (a *EventStreamingAPI) Delete(ctx context.Context, integrationID int) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/event-streaming/"+strconv.Itoa(integrationID), nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}

View File

@@ -0,0 +1,194 @@
//go:build integration
package rest_test
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/shared/management/client/rest"
"github.com/netbirdio/netbird/shared/management/http/api"
"github.com/netbirdio/netbird/shared/management/http/util"
)
var (
testIntegrationResponse = api.IntegrationResponse{
Id: ptr[int64](1),
AccountId: ptr("acc-1"),
Platform: (*api.IntegrationResponsePlatform)(ptr("datadog")),
Enabled: ptr(true),
Config: &map[string]string{"api_key": "****"},
CreatedAt: ptr(time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)),
UpdatedAt: ptr(time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)),
}
)
func TestEventStreaming_List_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/event-streaming", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal([]api.IntegrationResponse{testIntegrationResponse})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EventStreaming.List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testIntegrationResponse, ret[0])
})
}
func TestEventStreaming_List_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/event-streaming", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EventStreaming.List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestEventStreaming_Get_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/event-streaming/1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal(testIntegrationResponse)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EventStreaming.Get(context.Background(), 1)
require.NoError(t, err)
assert.Equal(t, testIntegrationResponse, *ret)
})
}
func TestEventStreaming_Get_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/event-streaming/1", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EventStreaming.Get(context.Background(), 1)
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Nil(t, ret)
})
}
func TestEventStreaming_Create_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/event-streaming", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.CreateIntegrationRequest
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, api.CreateIntegrationRequestPlatformDatadog, req.Platform)
assert.Equal(t, true, req.Enabled)
retBytes, _ := json.Marshal(testIntegrationResponse)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EventStreaming.Create(context.Background(), api.CreateIntegrationRequest{
Platform: api.CreateIntegrationRequestPlatformDatadog,
Enabled: true,
Config: map[string]string{"api_key": "test-key"},
})
require.NoError(t, err)
assert.Equal(t, testIntegrationResponse, *ret)
})
}
func TestEventStreaming_Create_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/event-streaming", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EventStreaming.Create(context.Background(), api.CreateIntegrationRequest{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestEventStreaming_Update_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/event-streaming/1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.CreateIntegrationRequest
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, false, req.Enabled)
retBytes, _ := json.Marshal(testIntegrationResponse)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EventStreaming.Update(context.Background(), 1, api.CreateIntegrationRequest{
Platform: api.CreateIntegrationRequestPlatformDatadog,
Enabled: false,
Config: map[string]string{"api_key": "updated-key"},
})
require.NoError(t, err)
assert.Equal(t, testIntegrationResponse, *ret)
})
}
func TestEventStreaming_Update_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/event-streaming/1", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.EventStreaming.Update(context.Background(), 1, api.CreateIntegrationRequest{})
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Nil(t, ret)
})
}
func TestEventStreaming_Delete_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/event-streaming/1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.EventStreaming.Delete(context.Background(), 1)
require.NoError(t, err)
})
}
func TestEventStreaming_Delete_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/event-streaming/1", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.EventStreaming.Delete(context.Background(), 1)
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}

View File

@@ -2,6 +2,8 @@ package rest
import (
"context"
"fmt"
"time"
"github.com/netbirdio/netbird/shared/management/http/api"
)
@@ -11,10 +13,79 @@ type EventsAPI struct {
c *Client
}
// List list all events
// See more: https://docs.netbird.io/api/resources/events#list-all-events
func (a *EventsAPI) List(ctx context.Context) ([]api.Event, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/events", nil, nil)
// NetworkTrafficOption options for ListNetworkTrafficEvents API
type NetworkTrafficOption func(query map[string]string)
func NetworkTrafficPage(page int) NetworkTrafficOption {
return func(query map[string]string) {
query["page"] = fmt.Sprintf("%d", page)
}
}
func NetworkTrafficPageSize(pageSize int) NetworkTrafficOption {
return func(query map[string]string) {
query["page_size"] = fmt.Sprintf("%d", pageSize)
}
}
func NetworkTrafficUserID(userID string) NetworkTrafficOption {
return func(query map[string]string) {
query["user_id"] = userID
}
}
func NetworkTrafficReporterID(reporterID string) NetworkTrafficOption {
return func(query map[string]string) {
query["reporter_id"] = reporterID
}
}
func NetworkTrafficProtocol(protocol int) NetworkTrafficOption {
return func(query map[string]string) {
query["protocol"] = fmt.Sprintf("%d", protocol)
}
}
func NetworkTrafficType(t api.GetApiEventsNetworkTrafficParamsType) NetworkTrafficOption {
return func(query map[string]string) {
query["type"] = string(t)
}
}
func NetworkTrafficConnectionType(ct api.GetApiEventsNetworkTrafficParamsConnectionType) NetworkTrafficOption {
return func(query map[string]string) {
query["connection_type"] = string(ct)
}
}
func NetworkTrafficDirection(d api.GetApiEventsNetworkTrafficParamsDirection) NetworkTrafficOption {
return func(query map[string]string) {
query["direction"] = string(d)
}
}
func NetworkTrafficSearch(search string) NetworkTrafficOption {
return func(query map[string]string) {
query["search"] = search
}
}
func NetworkTrafficStartDate(t time.Time) NetworkTrafficOption {
return func(query map[string]string) {
query["start_date"] = t.Format(time.RFC3339)
}
}
func NetworkTrafficEndDate(t time.Time) NetworkTrafficOption {
return func(query map[string]string) {
query["end_date"] = t.Format(time.RFC3339)
}
}
// ListAuditEvents list all audit events
// See more: https://docs.netbird.io/api/resources/events#list-all-audit-events
func (a *EventsAPI) ListAuditEvents(ctx context.Context) ([]api.Event, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/events/audit", nil, nil)
if err != nil {
return nil, err
}
@@ -24,3 +95,21 @@ func (a *EventsAPI) List(ctx context.Context) ([]api.Event, error) {
ret, err := parseResponse[[]api.Event](resp)
return ret, err
}
// ListNetworkTrafficEvents list network traffic events
// See more: https://docs.netbird.io/api/resources/events#list-network-traffic-events
func (a *EventsAPI) ListNetworkTrafficEvents(ctx context.Context, opts ...NetworkTrafficOption) (*api.NetworkTrafficEventsResponse, error) {
query := make(map[string]string)
for _, o := range opts {
o(query)
}
resp, err := a.c.NewRequest(ctx, "GET", "/api/events/network-traffic", nil, query)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.NetworkTrafficEventsResponse](resp)
return &ret, err
}

View File

@@ -21,37 +21,76 @@ var (
Activity: "AccountCreate",
ActivityCode: api.EventActivityCodeAccountCreate,
}
testNetworkTrafficResponse = api.NetworkTrafficEventsResponse{
Data: []api.NetworkTrafficEvent{},
Page: 1,
PageSize: 50,
}
)
func TestEvents_List_200(t *testing.T) {
func TestEvents_ListAuditEvents_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/events", func(w http.ResponseWriter, r *http.Request) {
mux.HandleFunc("/api/events/audit", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.Event{testEvent})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Events.List(context.Background())
ret, err := c.Events.ListAuditEvents(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testEvent, ret[0])
})
}
func TestEvents_List_Err(t *testing.T) {
func TestEvents_ListAuditEvents_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/events", func(w http.ResponseWriter, r *http.Request) {
mux.HandleFunc("/api/events/audit", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Events.List(context.Background())
ret, err := c.Events.ListAuditEvents(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestEvents_ListNetworkTrafficEvents_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/events/network-traffic", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "1", r.URL.Query().Get("page"))
assert.Equal(t, "50", r.URL.Query().Get("page_size"))
retBytes, _ := json.Marshal(testNetworkTrafficResponse)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Events.ListNetworkTrafficEvents(context.Background(),
rest.NetworkTrafficPage(1),
rest.NetworkTrafficPageSize(50),
)
require.NoError(t, err)
assert.Equal(t, testNetworkTrafficResponse, *ret)
})
}
func TestEvents_ListNetworkTrafficEvents_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/events/network-traffic", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Events.ListNetworkTrafficEvents(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestEvents_Integration(t *testing.T) {
withBlackBoxServer(t, func(c *rest.Client) {
// Do something that would trigger any event
@@ -62,7 +101,7 @@ func TestEvents_Integration(t *testing.T) {
})
require.NoError(t, err)
events, err := c.Events.List(context.Background())
events, err := c.Events.ListAuditEvents(context.Background())
require.NoError(t, err)
assert.NotEmpty(t, events)
})

View File

@@ -0,0 +1,92 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/shared/management/http/api"
)
// IdentityProvidersAPI APIs for Identity Providers, do not use directly
type IdentityProvidersAPI struct {
c *Client
}
// List all identity providers
// See more: https://docs.netbird.io/api/resources/identity-providers#list-all-identity-providers
func (a *IdentityProvidersAPI) List(ctx context.Context) ([]api.IdentityProvider, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/identity-providers", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.IdentityProvider](resp)
return ret, err
}
// Get identity provider info
// See more: https://docs.netbird.io/api/resources/identity-providers#retrieve-an-identity-provider
func (a *IdentityProvidersAPI) Get(ctx context.Context, idpID string) (*api.IdentityProvider, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/identity-providers/"+idpID, nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.IdentityProvider](resp)
return &ret, err
}
// Create new identity provider
// See more: https://docs.netbird.io/api/resources/identity-providers#create-an-identity-provider
func (a *IdentityProvidersAPI) Create(ctx context.Context, request api.PostApiIdentityProvidersJSONRequestBody) (*api.IdentityProvider, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/identity-providers", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.IdentityProvider](resp)
return &ret, err
}
// Update update identity provider
// See more: https://docs.netbird.io/api/resources/identity-providers#update-an-identity-provider
func (a *IdentityProvidersAPI) Update(ctx context.Context, idpID string, request api.PutApiIdentityProvidersIdpIdJSONRequestBody) (*api.IdentityProvider, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "PUT", "/api/identity-providers/"+idpID, bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.IdentityProvider](resp)
return &ret, err
}
// Delete delete identity provider
// See more: https://docs.netbird.io/api/resources/identity-providers#delete-an-identity-provider
func (a *IdentityProvidersAPI) Delete(ctx context.Context, idpID string) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/identity-providers/"+idpID, nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}

View File

@@ -0,0 +1,183 @@
//go:build integration
package rest_test
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/shared/management/client/rest"
"github.com/netbirdio/netbird/shared/management/http/api"
"github.com/netbirdio/netbird/shared/management/http/util"
)
var testIdentityProvider = api.IdentityProvider{
ClientId: "test-client-id",
Id: ptr("Test"),
}
func TestIdentityProviders_List_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/identity-providers", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.IdentityProvider{testIdentityProvider})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.IdentityProviders.List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testIdentityProvider, ret[0])
})
}
func TestIdentityProviders_List_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/identity-providers", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.IdentityProviders.List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestIdentityProviders_Get_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/identity-providers/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testIdentityProvider)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.IdentityProviders.Get(context.Background(), "Test")
require.NoError(t, err)
assert.Equal(t, testIdentityProvider, *ret)
})
}
func TestIdentityProviders_Get_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/identity-providers/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.IdentityProviders.Get(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestIdentityProviders_Create_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/identity-providers", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PostApiIdentityProvidersJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "new-client-id", req.ClientId)
retBytes, _ := json.Marshal(testIdentityProvider)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.IdentityProviders.Create(context.Background(), api.PostApiIdentityProvidersJSONRequestBody{
ClientId: "new-client-id",
})
require.NoError(t, err)
assert.Equal(t, testIdentityProvider, *ret)
})
}
func TestIdentityProviders_Create_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/identity-providers", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.IdentityProviders.Create(context.Background(), api.PostApiIdentityProvidersJSONRequestBody{
ClientId: "new-client-id",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestIdentityProviders_Update_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/identity-providers/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PutApiIdentityProvidersIdpIdJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "updated-client-id", req.ClientId)
retBytes, _ := json.Marshal(testIdentityProvider)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.IdentityProviders.Update(context.Background(), "Test", api.PutApiIdentityProvidersIdpIdJSONRequestBody{
ClientId: "updated-client-id",
})
require.NoError(t, err)
assert.Equal(t, testIdentityProvider, *ret)
})
}
func TestIdentityProviders_Update_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/identity-providers/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.IdentityProviders.Update(context.Background(), "Test", api.PutApiIdentityProvidersIdpIdJSONRequestBody{
ClientId: "updated-client-id",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestIdentityProviders_Delete_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/identity-providers/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.IdentityProviders.Delete(context.Background(), "Test")
require.NoError(t, err)
})
}
func TestIdentityProviders_Delete_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/identity-providers/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.IdentityProviders.Delete(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}

View File

@@ -0,0 +1,92 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/shared/management/http/api"
)
// IngressAPI APIs for Ingress Peers, do not use directly
type IngressAPI struct {
c *Client
}
// List all ingress peers
// See more: https://docs.netbird.io/api/resources/ingress#list-all-ingress-peers
func (a *IngressAPI) List(ctx context.Context) ([]api.IngressPeer, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/ingress/peers", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.IngressPeer](resp)
return ret, err
}
// Get ingress peer info
// See more: https://docs.netbird.io/api/resources/ingress#retrieve-an-ingress-peer
func (a *IngressAPI) Get(ctx context.Context, ingressPeerID string) (*api.IngressPeer, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/ingress/peers/"+ingressPeerID, nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.IngressPeer](resp)
return &ret, err
}
// Create new ingress peer
// See more: https://docs.netbird.io/api/resources/ingress#create-an-ingress-peer
func (a *IngressAPI) Create(ctx context.Context, request api.PostApiIngressPeersJSONRequestBody) (*api.IngressPeer, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/ingress/peers", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.IngressPeer](resp)
return &ret, err
}
// Update update ingress peer
// See more: https://docs.netbird.io/api/resources/ingress#update-an-ingress-peer
func (a *IngressAPI) Update(ctx context.Context, ingressPeerID string, request api.PutApiIngressPeersIngressPeerIdJSONRequestBody) (*api.IngressPeer, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "PUT", "/api/ingress/peers/"+ingressPeerID, bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.IngressPeer](resp)
return &ret, err
}
// Delete delete ingress peer
// See more: https://docs.netbird.io/api/resources/ingress#delete-an-ingress-peer
func (a *IngressAPI) Delete(ctx context.Context, ingressPeerID string) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/ingress/peers/"+ingressPeerID, nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}

View File

@@ -0,0 +1,184 @@
//go:build integration
package rest_test
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/shared/management/client/rest"
"github.com/netbirdio/netbird/shared/management/http/api"
"github.com/netbirdio/netbird/shared/management/http/util"
)
var testIngressPeer = api.IngressPeer{
Connected: true,
Enabled: true,
Id: "Test",
}
func TestIngress_List_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/peers", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.IngressPeer{testIngressPeer})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Ingress.List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testIngressPeer, ret[0])
})
}
func TestIngress_List_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/peers", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Ingress.List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestIngress_Get_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/peers/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testIngressPeer)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Ingress.Get(context.Background(), "Test")
require.NoError(t, err)
assert.Equal(t, testIngressPeer, *ret)
})
}
func TestIngress_Get_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/peers/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Ingress.Get(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestIngress_Create_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/peers", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PostApiIngressPeersJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "peer-id", req.PeerId)
retBytes, _ := json.Marshal(testIngressPeer)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Ingress.Create(context.Background(), api.PostApiIngressPeersJSONRequestBody{
PeerId: "peer-id",
})
require.NoError(t, err)
assert.Equal(t, testIngressPeer, *ret)
})
}
func TestIngress_Create_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/peers", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Ingress.Create(context.Background(), api.PostApiIngressPeersJSONRequestBody{
PeerId: "peer-id",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestIngress_Update_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/peers/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PutApiIngressPeersIngressPeerIdJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, true, req.Enabled)
retBytes, _ := json.Marshal(testIngressPeer)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Ingress.Update(context.Background(), "Test", api.PutApiIngressPeersIngressPeerIdJSONRequestBody{
Enabled: true,
})
require.NoError(t, err)
assert.Equal(t, testIngressPeer, *ret)
})
}
func TestIngress_Update_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/peers/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Ingress.Update(context.Background(), "Test", api.PutApiIngressPeersIngressPeerIdJSONRequestBody{
Enabled: true,
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestIngress_Delete_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/peers/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.Ingress.Delete(context.Background(), "Test")
require.NoError(t, err)
})
}
func TestIngress_Delete_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/peers/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.Ingress.Delete(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}

View File

@@ -0,0 +1,46 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/shared/management/http/api"
)
// InstanceAPI APIs for Instance status and version, do not use directly
type InstanceAPI struct {
c *Client
}
// GetStatus get instance status
// See more: https://docs.netbird.io/api/resources/instance#get-instance-status
func (a *InstanceAPI) GetStatus(ctx context.Context) (*api.InstanceStatus, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/instance", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.InstanceStatus](resp)
return &ret, err
}
// Setup perform initial instance setup
// See more: https://docs.netbird.io/api/resources/instance#setup-instance
func (a *InstanceAPI) Setup(ctx context.Context, request api.PostApiSetupJSONRequestBody) (*api.SetupResponse, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/setup", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.SetupResponse](resp)
return &ret, err
}

View File

@@ -0,0 +1,96 @@
//go:build integration
package rest_test
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/shared/management/client/rest"
"github.com/netbirdio/netbird/shared/management/http/api"
"github.com/netbirdio/netbird/shared/management/http/util"
)
var (
testInstanceStatus = api.InstanceStatus{
SetupRequired: true,
}
testSetupResponse = api.SetupResponse{
Email: "admin@example.com",
UserId: "user-123",
}
)
func TestInstance_GetStatus_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/instance", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testInstanceStatus)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Instance.GetStatus(context.Background())
require.NoError(t, err)
assert.Equal(t, testInstanceStatus, *ret)
})
}
func TestInstance_GetStatus_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/instance", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Instance.GetStatus(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestInstance_Setup_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/setup", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PostApiSetupJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "admin@example.com", req.Email)
retBytes, _ := json.Marshal(testSetupResponse)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Instance.Setup(context.Background(), api.PostApiSetupJSONRequestBody{
Email: "admin@example.com",
})
require.NoError(t, err)
assert.Equal(t, testSetupResponse, *ret)
})
}
func TestInstance_Setup_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/setup", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Instance.Setup(context.Background(), api.PostApiSetupJSONRequestBody{
Email: "admin@example.com",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}

View File

@@ -0,0 +1,122 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/shared/management/http/api"
)
// MSPAPI APIs for MSP tenant management
type MSPAPI struct {
c *Client
}
// ListTenants retrieves all MSP tenants
// See more: https://docs.netbird.io/api/resources/msp#list-all-tenants
func (a *MSPAPI) ListTenants(ctx context.Context) (*api.GetTenantsResponse, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/msp/tenants", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.GetTenantsResponse](resp)
return &ret, err
}
// CreateTenant creates a new MSP tenant
// See more: https://docs.netbird.io/api/resources/msp#create-a-tenant
func (a *MSPAPI) CreateTenant(ctx context.Context, request api.CreateTenantRequest) (*api.TenantResponse, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/integrations/msp/tenants", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.TenantResponse](resp)
return &ret, err
}
// UpdateTenant updates an existing MSP tenant
// See more: https://docs.netbird.io/api/resources/msp#update-a-tenant
func (a *MSPAPI) UpdateTenant(ctx context.Context, tenantID string, request api.UpdateTenantRequest) (*api.TenantResponse, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "PUT", "/api/integrations/msp/tenants/"+tenantID, bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.TenantResponse](resp)
return &ret, err
}
// DeleteTenant deletes an MSP tenant
// See more: https://docs.netbird.io/api/resources/msp#delete-a-tenant
func (a *MSPAPI) DeleteTenant(ctx context.Context, tenantID string) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/integrations/msp/tenants/"+tenantID, nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}
// UnlinkTenant unlinks a tenant from the MSP account
// See more: https://docs.netbird.io/api/resources/msp#unlink-a-tenant
func (a *MSPAPI) UnlinkTenant(ctx context.Context, tenantID, owner string) error {
params := map[string]string{"owner": owner}
requestBytes, err := json.Marshal(params)
if err != nil {
return err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/integrations/msp/tenants/"+tenantID+"/unlink", bytes.NewReader(requestBytes), nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}
// VerifyTenantDNS verifies a tenant domain DNS challenge
// See more: https://docs.netbird.io/api/resources/msp#verify-tenant-dns
func (a *MSPAPI) VerifyTenantDNS(ctx context.Context, tenantID string) error {
resp, err := a.c.NewRequest(ctx, "POST", "/api/integrations/msp/tenants/"+tenantID+"/dns", nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}
// InviteTenant invites an existing account as a tenant to the MSP account
// See more: https://docs.netbird.io/api/resources/msp#invite-a-tenant
func (a *MSPAPI) InviteTenant(ctx context.Context, tenantID string) (*api.TenantResponse, error) {
resp, err := a.c.NewRequest(ctx, "POST", "/api/integrations/msp/tenants/"+tenantID+"/invite", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.TenantResponse](resp)
return &ret, err
}

View File

@@ -0,0 +1,251 @@
//go:build integration
package rest_test
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/shared/management/client/rest"
"github.com/netbirdio/netbird/shared/management/http/api"
"github.com/netbirdio/netbird/shared/management/http/util"
)
var (
testTenant = api.TenantResponse{
Id: "tenant-1",
Name: "Test Tenant",
Domain: "test.example.com",
DnsChallenge: "challenge-123",
Status: "active",
Groups: []api.TenantGroupResponse{},
CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
UpdatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
}
)
func TestMSP_ListTenants_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/msp/tenants", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal([]api.TenantResponse{testTenant})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.MSP.ListTenants(context.Background())
require.NoError(t, err)
assert.Len(t, *ret, 1)
assert.Equal(t, testTenant, (*ret)[0])
})
}
func TestMSP_ListTenants_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/msp/tenants", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.MSP.ListTenants(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestMSP_CreateTenant_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/msp/tenants", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.CreateTenantRequest
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "Test Tenant", req.Name)
assert.Equal(t, "test.example.com", req.Domain)
retBytes, _ := json.Marshal(testTenant)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.MSP.CreateTenant(context.Background(), api.CreateTenantRequest{
Name: "Test Tenant",
Domain: "test.example.com",
Groups: []api.TenantGroupResponse{},
})
require.NoError(t, err)
assert.Equal(t, testTenant, *ret)
})
}
func TestMSP_CreateTenant_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/msp/tenants", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.MSP.CreateTenant(context.Background(), api.CreateTenantRequest{
Name: "Test Tenant",
Domain: "test.example.com",
Groups: []api.TenantGroupResponse{},
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestMSP_UpdateTenant_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/msp/tenants/tenant-1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.UpdateTenantRequest
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "Updated Tenant", req.Name)
retBytes, _ := json.Marshal(testTenant)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.MSP.UpdateTenant(context.Background(), "tenant-1", api.UpdateTenantRequest{
Name: "Updated Tenant",
Groups: []api.TenantGroupResponse{},
})
require.NoError(t, err)
assert.Equal(t, testTenant, *ret)
})
}
func TestMSP_UpdateTenant_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/msp/tenants/tenant-1", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.MSP.UpdateTenant(context.Background(), "tenant-1", api.UpdateTenantRequest{
Name: "Updated Tenant",
Groups: []api.TenantGroupResponse{},
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestMSP_DeleteTenant_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/msp/tenants/tenant-1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.MSP.DeleteTenant(context.Background(), "tenant-1")
require.NoError(t, err)
})
}
func TestMSP_DeleteTenant_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/msp/tenants/tenant-1", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.MSP.DeleteTenant(context.Background(), "tenant-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestMSP_UnlinkTenant_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/msp/tenants/tenant-1/unlink", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
w.WriteHeader(200)
})
err := c.MSP.UnlinkTenant(context.Background(), "tenant-1", "owner-1")
require.NoError(t, err)
})
}
func TestMSP_UnlinkTenant_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/msp/tenants/tenant-1/unlink", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.MSP.UnlinkTenant(context.Background(), "tenant-1", "owner-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestMSP_VerifyTenantDNS_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/msp/tenants/tenant-1/dns", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
w.WriteHeader(200)
})
err := c.MSP.VerifyTenantDNS(context.Background(), "tenant-1")
require.NoError(t, err)
})
}
func TestMSP_VerifyTenantDNS_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/msp/tenants/tenant-1/dns", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Failed", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.MSP.VerifyTenantDNS(context.Background(), "tenant-1")
assert.Error(t, err)
assert.Equal(t, "Failed", err.Error())
})
}
func TestMSP_InviteTenant_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/msp/tenants/tenant-1/invite", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
retBytes, _ := json.Marshal(testTenant)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.MSP.InviteTenant(context.Background(), "tenant-1")
require.NoError(t, err)
assert.Equal(t, testTenant, *ret)
})
}
func TestMSP_InviteTenant_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/msp/tenants/tenant-1/invite", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.MSP.InviteTenant(context.Background(), "tenant-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Nil(t, ret)
})
}

View File

@@ -91,6 +91,20 @@ func (a *NetworksAPI) Delete(ctx context.Context, networkID string) error {
return nil
}
// ListAllRouters list all routers across all networks
// See more: https://docs.netbird.io/api/resources/networks#list-all-network-routers
func (a *NetworksAPI) ListAllRouters(ctx context.Context) ([]api.NetworkRouter, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/networks/routers", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.NetworkRouter](resp)
return ret, err
}
// NetworkResourcesAPI APIs for Network Resources, do not use directly
type NetworkResourcesAPI struct {
c *Client

View File

@@ -219,6 +219,35 @@ func TestNetworks_Integration(t *testing.T) {
})
}
func TestNetworks_ListAllRouters_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/routers", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.NetworkRouter{testNetworkRouter})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Networks.ListAllRouters(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testNetworkRouter, ret[0])
})
}
func TestNetworks_ListAllRouters_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/routers", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Networks.ListAllRouters(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestNetworkResources_List_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/resources", func(w http.ResponseWriter, r *http.Request) {

View File

@@ -106,3 +106,173 @@ func (a *PeersAPI) ListAccessiblePeers(ctx context.Context, peerID string) ([]ap
ret, err := parseResponse[[]api.Peer](resp)
return ret, err
}
// CreateTemporaryAccess create temporary access for a peer
// See more: https://docs.netbird.io/api/resources/peers#create-temporary-access
func (a *PeersAPI) CreateTemporaryAccess(ctx context.Context, peerID string, request api.PostApiPeersPeerIdTemporaryAccessJSONRequestBody) (*api.PeerTemporaryAccessResponse, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/peers/"+peerID+"/temporary-access", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.PeerTemporaryAccessResponse](resp)
return &ret, err
}
// PeerIngressPortsAPI APIs for Peer Ingress Ports, do not use directly
type PeerIngressPortsAPI struct {
c *Client
peerID string
}
// IngressPorts APIs for peer ingress ports
func (a *PeersAPI) IngressPorts(peerID string) *PeerIngressPortsAPI {
return &PeerIngressPortsAPI{
c: a.c,
peerID: peerID,
}
}
// List list all ingress port allocations for a peer
// See more: https://docs.netbird.io/api/resources/peers#list-all-ingress-port-allocations
func (a *PeerIngressPortsAPI) List(ctx context.Context) ([]api.IngressPortAllocation, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/peers/"+a.peerID+"/ingress/ports", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.IngressPortAllocation](resp)
return ret, err
}
// Get get ingress port allocation info
// See more: https://docs.netbird.io/api/resources/peers#retrieve-an-ingress-port-allocation
func (a *PeerIngressPortsAPI) Get(ctx context.Context, allocationID string) (*api.IngressPortAllocation, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/peers/"+a.peerID+"/ingress/ports/"+allocationID, nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.IngressPortAllocation](resp)
return &ret, err
}
// Create create new ingress port allocation
// See more: https://docs.netbird.io/api/resources/peers#create-an-ingress-port-allocation
func (a *PeerIngressPortsAPI) Create(ctx context.Context, request api.PostApiPeersPeerIdIngressPortsJSONRequestBody) (*api.IngressPortAllocation, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/peers/"+a.peerID+"/ingress/ports", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.IngressPortAllocation](resp)
return &ret, err
}
// Update update ingress port allocation
// See more: https://docs.netbird.io/api/resources/peers#update-an-ingress-port-allocation
func (a *PeerIngressPortsAPI) Update(ctx context.Context, allocationID string, request api.PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody) (*api.IngressPortAllocation, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "PUT", "/api/peers/"+a.peerID+"/ingress/ports/"+allocationID, bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.IngressPortAllocation](resp)
return &ret, err
}
// Delete delete ingress port allocation
// See more: https://docs.netbird.io/api/resources/peers#delete-an-ingress-port-allocation
func (a *PeerIngressPortsAPI) Delete(ctx context.Context, allocationID string) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/peers/"+a.peerID+"/ingress/ports/"+allocationID, nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}
// PeerJobsAPI APIs for Peer Jobs, do not use directly
type PeerJobsAPI struct {
c *Client
peerID string
}
// Jobs APIs for peer jobs
func (a *PeersAPI) Jobs(peerID string) *PeerJobsAPI {
return &PeerJobsAPI{
c: a.c,
peerID: peerID,
}
}
// List list all jobs for a peer
// See more: https://docs.netbird.io/api/resources/peers#list-all-peer-jobs
func (a *PeerJobsAPI) List(ctx context.Context) ([]api.JobResponse, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/peers/"+a.peerID+"/jobs", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.JobResponse](resp)
return ret, err
}
// Get get job info
// See more: https://docs.netbird.io/api/resources/peers#retrieve-a-peer-job
func (a *PeerJobsAPI) Get(ctx context.Context, jobID string) (*api.JobResponse, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/peers/"+a.peerID+"/jobs/"+jobID, nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.JobResponse](resp)
return &ret, err
}
// Create create new job for a peer
// See more: https://docs.netbird.io/api/resources/peers#create-a-peer-job
func (a *PeerJobsAPI) Create(ctx context.Context, request api.PostApiPeersPeerIdJobsJSONRequestBody) (*api.JobResponse, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/peers/"+a.peerID+"/jobs", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.JobResponse](resp)
return &ret, err
}

View File

@@ -25,6 +25,21 @@ var (
DnsLabel: "test",
Id: "Test",
}
testPeerTemporaryAccess = api.PeerTemporaryAccessResponse{
Id: "Test",
Name: "test-peer",
}
testIngressPortAllocation = api.IngressPortAllocation{
Enabled: true,
Id: "alloc-1",
}
testJobResponse = api.JobResponse{
Id: "job-1",
Status: "pending",
}
)
func TestPeers_List_200(t *testing.T) {
@@ -177,6 +192,264 @@ func TestPeers_ListAccessiblePeers_Err(t *testing.T) {
})
}
func TestPeers_CreateTemporaryAccess_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/temporary-access", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
retBytes, _ := json.Marshal(testPeerTemporaryAccess)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.CreateTemporaryAccess(context.Background(), "Test", api.PostApiPeersPeerIdTemporaryAccessJSONRequestBody{})
require.NoError(t, err)
assert.Equal(t, testPeerTemporaryAccess, *ret)
})
}
func TestPeers_CreateTemporaryAccess_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/temporary-access", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.CreateTemporaryAccess(context.Background(), "Test", api.PostApiPeersPeerIdTemporaryAccessJSONRequestBody{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestPeerIngressPorts_List_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.IngressPortAllocation{testIngressPortAllocation})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.IngressPorts("Test").List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testIngressPortAllocation, ret[0])
})
}
func TestPeerIngressPorts_List_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.IngressPorts("Test").List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestPeerIngressPorts_Get_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports/alloc-1", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testIngressPortAllocation)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.IngressPorts("Test").Get(context.Background(), "alloc-1")
require.NoError(t, err)
assert.Equal(t, testIngressPortAllocation, *ret)
})
}
func TestPeerIngressPorts_Get_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports/alloc-1", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.IngressPorts("Test").Get(context.Background(), "alloc-1")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestPeerIngressPorts_Create_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
retBytes, _ := json.Marshal(testIngressPortAllocation)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.IngressPorts("Test").Create(context.Background(), api.PostApiPeersPeerIdIngressPortsJSONRequestBody{})
require.NoError(t, err)
assert.Equal(t, testIngressPortAllocation, *ret)
})
}
func TestPeerIngressPorts_Create_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.IngressPorts("Test").Create(context.Background(), api.PostApiPeersPeerIdIngressPortsJSONRequestBody{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestPeerIngressPorts_Update_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports/alloc-1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
retBytes, _ := json.Marshal(testIngressPortAllocation)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.IngressPorts("Test").Update(context.Background(), "alloc-1", api.PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody{})
require.NoError(t, err)
assert.Equal(t, testIngressPortAllocation, *ret)
})
}
func TestPeerIngressPorts_Update_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports/alloc-1", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.IngressPorts("Test").Update(context.Background(), "alloc-1", api.PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestPeerIngressPorts_Delete_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports/alloc-1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.Peers.IngressPorts("Test").Delete(context.Background(), "alloc-1")
require.NoError(t, err)
})
}
func TestPeerIngressPorts_Delete_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports/alloc-1", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.Peers.IngressPorts("Test").Delete(context.Background(), "alloc-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestPeerJobs_List_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/jobs", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.JobResponse{testJobResponse})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.Jobs("Test").List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testJobResponse.Id, ret[0].Id)
assert.Equal(t, testJobResponse.Status, ret[0].Status)
})
}
func TestPeerJobs_List_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/jobs", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.Jobs("Test").List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestPeerJobs_Get_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/jobs/job-1", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testJobResponse)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.Jobs("Test").Get(context.Background(), "job-1")
require.NoError(t, err)
assert.Equal(t, testJobResponse.Id, ret.Id)
assert.Equal(t, testJobResponse.Status, ret.Status)
})
}
func TestPeerJobs_Get_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/jobs/job-1", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.Jobs("Test").Get(context.Background(), "job-1")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestPeerJobs_Create_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/jobs", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
retBytes, _ := json.Marshal(testJobResponse)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.Jobs("Test").Create(context.Background(), api.PostApiPeersPeerIdJobsJSONRequestBody{})
require.NoError(t, err)
assert.Equal(t, testJobResponse.Id, ret.Id)
assert.Equal(t, testJobResponse.Status, ret.Status)
})
}
func TestPeerJobs_Create_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/jobs", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.Jobs("Test").Create(context.Background(), api.PostApiPeersPeerIdJobsJSONRequestBody{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestPeers_Integration(t *testing.T) {
withBlackBoxServer(t, func(c *rest.Client) {
peers, err := c.Peers.List(context.Background())

View File

@@ -0,0 +1,119 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/shared/management/http/api"
)
// SCIMAPI APIs for SCIM IDP integrations
type SCIMAPI struct {
c *Client
}
// List retrieves all SCIM IDP integrations
// See more: https://docs.netbird.io/api/resources/scim#list-all-scim-integrations
func (a *SCIMAPI) List(ctx context.Context) ([]api.ScimIntegration, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/scim-idp", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.ScimIntegration](resp)
return ret, err
}
// Get retrieves a specific SCIM IDP integration by ID
// See more: https://docs.netbird.io/api/resources/scim#retrieve-a-scim-integration
func (a *SCIMAPI) Get(ctx context.Context, integrationID string) (*api.ScimIntegration, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/scim-idp/"+integrationID, nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.ScimIntegration](resp)
return &ret, err
}
// Create creates a new SCIM IDP integration
// See more: https://docs.netbird.io/api/resources/scim#create-a-scim-integration
func (a *SCIMAPI) Create(ctx context.Context, request api.CreateScimIntegrationRequest) (*api.ScimIntegration, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/integrations/scim-idp", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.ScimIntegration](resp)
return &ret, err
}
// Update updates an existing SCIM IDP integration
// See more: https://docs.netbird.io/api/resources/scim#update-a-scim-integration
func (a *SCIMAPI) Update(ctx context.Context, integrationID string, request api.UpdateScimIntegrationRequest) (*api.ScimIntegration, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "PUT", "/api/integrations/scim-idp/"+integrationID, bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.ScimIntegration](resp)
return &ret, err
}
// Delete deletes a SCIM IDP integration
// See more: https://docs.netbird.io/api/resources/scim#delete-a-scim-integration
func (a *SCIMAPI) Delete(ctx context.Context, integrationID string) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/integrations/scim-idp/"+integrationID, nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}
// RegenerateToken regenerates the SCIM API token for an integration
// See more: https://docs.netbird.io/api/resources/scim#regenerate-scim-token
func (a *SCIMAPI) RegenerateToken(ctx context.Context, integrationID string) (*api.ScimTokenResponse, error) {
resp, err := a.c.NewRequest(ctx, "POST", "/api/integrations/scim-idp/"+integrationID+"/token", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.ScimTokenResponse](resp)
return &ret, err
}
// GetLogs retrieves synchronization logs for an SCIM IDP integration
// See more: https://docs.netbird.io/api/resources/scim#get-scim-sync-logs
func (a *SCIMAPI) GetLogs(ctx context.Context, integrationID string) ([]api.IdpIntegrationSyncLog, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/scim-idp/"+integrationID+"/logs", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.IdpIntegrationSyncLog](resp)
return ret, err
}

View File

@@ -0,0 +1,262 @@
//go:build integration
package rest_test
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/shared/management/client/rest"
"github.com/netbirdio/netbird/shared/management/http/api"
"github.com/netbirdio/netbird/shared/management/http/util"
)
var (
testScimIntegration = api.ScimIntegration{
Id: 1,
AuthToken: "****",
Enabled: true,
GroupPrefixes: []string{"eng-"},
UserGroupPrefixes: []string{"dev-"},
Provider: "okta",
LastSyncedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
}
testScimToken = api.ScimTokenResponse{
AuthToken: "new-token-123",
}
testSyncLog = api.IdpIntegrationSyncLog{
Id: 1,
Level: "info",
Message: "Sync completed",
Timestamp: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
}
)
func TestSCIM_List_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/scim-idp", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal([]api.ScimIntegration{testScimIntegration})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.SCIM.List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testScimIntegration, ret[0])
})
}
func TestSCIM_List_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/scim-idp", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.SCIM.List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestSCIM_Get_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/scim-idp/int-1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal(testScimIntegration)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.SCIM.Get(context.Background(), "int-1")
require.NoError(t, err)
assert.Equal(t, testScimIntegration, *ret)
})
}
func TestSCIM_Get_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/scim-idp/int-1", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.SCIM.Get(context.Background(), "int-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Nil(t, ret)
})
}
func TestSCIM_Create_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/scim-idp", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.CreateScimIntegrationRequest
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "okta", req.Provider)
assert.Equal(t, "scim-", req.Prefix)
retBytes, _ := json.Marshal(testScimIntegration)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.SCIM.Create(context.Background(), api.CreateScimIntegrationRequest{
Provider: "okta",
Prefix: "scim-",
GroupPrefixes: &[]string{"eng-"},
})
require.NoError(t, err)
assert.Equal(t, testScimIntegration, *ret)
})
}
func TestSCIM_Create_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/scim-idp", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.SCIM.Create(context.Background(), api.CreateScimIntegrationRequest{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestSCIM_Update_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/scim-idp/int-1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.UpdateScimIntegrationRequest
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, true, *req.Enabled)
retBytes, _ := json.Marshal(testScimIntegration)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.SCIM.Update(context.Background(), "int-1", api.UpdateScimIntegrationRequest{
Enabled: ptr(true),
})
require.NoError(t, err)
assert.Equal(t, testScimIntegration, *ret)
})
}
func TestSCIM_Update_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/scim-idp/int-1", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.SCIM.Update(context.Background(), "int-1", api.UpdateScimIntegrationRequest{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestSCIM_Delete_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/scim-idp/int-1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.SCIM.Delete(context.Background(), "int-1")
require.NoError(t, err)
})
}
func TestSCIM_Delete_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/scim-idp/int-1", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.SCIM.Delete(context.Background(), "int-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestSCIM_RegenerateToken_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/scim-idp/int-1/token", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
retBytes, _ := json.Marshal(testScimToken)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.SCIM.RegenerateToken(context.Background(), "int-1")
require.NoError(t, err)
assert.Equal(t, testScimToken, *ret)
})
}
func TestSCIM_RegenerateToken_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/scim-idp/int-1/token", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.SCIM.RegenerateToken(context.Background(), "int-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Nil(t, ret)
})
}
func TestSCIM_GetLogs_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/scim-idp/int-1/logs", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal([]api.IdpIntegrationSyncLog{testSyncLog})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.SCIM.GetLogs(context.Background(), "int-1")
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testSyncLog, ret[0])
})
}
func TestSCIM_GetLogs_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/scim-idp/int-1/logs", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.SCIM.GetLogs(context.Background(), "int-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Empty(t, ret)
})
}

View File

@@ -105,3 +105,145 @@ func (a *UsersAPI) Current(ctx context.Context) (*api.User, error) {
ret, err := parseResponse[api.User](resp)
return &ret, err
}
// ListInvites list all user invites
// See more: https://docs.netbird.io/api/resources/users#list-all-user-invites
func (a *UsersAPI) ListInvites(ctx context.Context) ([]api.UserInvite, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/users/invites", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.UserInvite](resp)
return ret, err
}
// CreateInvite create a user invite
// See more: https://docs.netbird.io/api/resources/users#create-a-user-invite
func (a *UsersAPI) CreateInvite(ctx context.Context, request api.PostApiUsersInvitesJSONRequestBody) (*api.UserInvite, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/users/invites", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.UserInvite](resp)
return &ret, err
}
// DeleteInvite delete a user invite
// See more: https://docs.netbird.io/api/resources/users#delete-a-user-invite
func (a *UsersAPI) DeleteInvite(ctx context.Context, inviteID string) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/users/invites/"+inviteID, nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}
// RegenerateInvite regenerate a user invite token
// See more: https://docs.netbird.io/api/resources/users#regenerate-a-user-invite
func (a *UsersAPI) RegenerateInvite(ctx context.Context, inviteID string, request api.PostApiUsersInvitesInviteIdRegenerateJSONRequestBody) (*api.UserInviteRegenerateResponse, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/users/invites/"+inviteID+"/regenerate", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.UserInviteRegenerateResponse](resp)
return &ret, err
}
// GetInviteByToken get a user invite by token
// See more: https://docs.netbird.io/api/resources/users#get-a-user-invite-by-token
func (a *UsersAPI) GetInviteByToken(ctx context.Context, token string) (*api.UserInviteInfo, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/users/invites/"+token, nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.UserInviteInfo](resp)
return &ret, err
}
// AcceptInvite accept a user invite
// See more: https://docs.netbird.io/api/resources/users#accept-a-user-invite
func (a *UsersAPI) AcceptInvite(ctx context.Context, token string, request api.PostApiUsersInvitesTokenAcceptJSONRequestBody) (*api.UserInviteAcceptResponse, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/users/invites/"+token+"/accept", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.UserInviteAcceptResponse](resp)
return &ret, err
}
// Approve approve a pending user
// See more: https://docs.netbird.io/api/resources/users#approve-a-user
func (a *UsersAPI) Approve(ctx context.Context, userID string) (*api.User, error) {
resp, err := a.c.NewRequest(ctx, "POST", "/api/users/"+userID+"/approve", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.User](resp)
return &ret, err
}
// ChangePassword change a user's password
// See more: https://docs.netbird.io/api/resources/users#change-user-password
func (a *UsersAPI) ChangePassword(ctx context.Context, userID string, request api.PutApiUsersUserIdPasswordJSONRequestBody) error {
requestBytes, err := json.Marshal(request)
if err != nil {
return err
}
resp, err := a.c.NewRequest(ctx, "PUT", "/api/users/"+userID+"/password", bytes.NewReader(requestBytes), nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}
// Reject reject a pending user
// See more: https://docs.netbird.io/api/resources/users#reject-a-user
func (a *UsersAPI) Reject(ctx context.Context, userID string) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/users/"+userID+"/reject", nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}

View File

@@ -32,6 +32,23 @@ var (
Role: "user",
Status: api.UserStatusActive,
}
testUserInvite = api.UserInvite{
AutoGroups: []string{"group1"},
Id: "invite-1",
}
testUserInviteInfo = api.UserInviteInfo{
Email: "invite@test.com",
}
testUserInviteAcceptResponse = api.UserInviteAcceptResponse{
Success: true,
}
testUserInviteRegenerateResponse = api.UserInviteRegenerateResponse{
InviteToken: "new-token",
}
)
func TestUsers_List_200(t *testing.T) {
@@ -220,6 +237,269 @@ func TestUsers_Current_Err(t *testing.T) {
})
}
func TestUsers_ListInvites_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/invites", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.UserInvite{testUserInvite})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Users.ListInvites(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testUserInvite, ret[0])
})
}
func TestUsers_ListInvites_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/invites", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Users.ListInvites(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestUsers_CreateInvite_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/invites", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PostApiUsersInvitesJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "invite@test.com", req.Email)
retBytes, _ := json.Marshal(testUserInvite)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Users.CreateInvite(context.Background(), api.PostApiUsersInvitesJSONRequestBody{
Email: "invite@test.com",
})
require.NoError(t, err)
assert.Equal(t, testUserInvite, *ret)
})
}
func TestUsers_CreateInvite_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/invites", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Users.CreateInvite(context.Background(), api.PostApiUsersInvitesJSONRequestBody{
Email: "invite@test.com",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestUsers_DeleteInvite_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/invites/invite-1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.Users.DeleteInvite(context.Background(), "invite-1")
require.NoError(t, err)
})
}
func TestUsers_DeleteInvite_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/invites/invite-1", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.Users.DeleteInvite(context.Background(), "invite-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestUsers_RegenerateInvite_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/invites/invite-1/regenerate", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
retBytes, _ := json.Marshal(testUserInviteRegenerateResponse)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Users.RegenerateInvite(context.Background(), "invite-1", api.PostApiUsersInvitesInviteIdRegenerateJSONRequestBody{})
require.NoError(t, err)
assert.Equal(t, testUserInviteRegenerateResponse, *ret)
})
}
func TestUsers_RegenerateInvite_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/invites/invite-1/regenerate", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Users.RegenerateInvite(context.Background(), "invite-1", api.PostApiUsersInvitesInviteIdRegenerateJSONRequestBody{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestUsers_GetInviteByToken_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/invites/some-token", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testUserInviteInfo)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Users.GetInviteByToken(context.Background(), "some-token")
require.NoError(t, err)
assert.Equal(t, testUserInviteInfo, *ret)
})
}
func TestUsers_GetInviteByToken_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/invites/some-token", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Users.GetInviteByToken(context.Background(), "some-token")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestUsers_AcceptInvite_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/invites/some-token/accept", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
retBytes, _ := json.Marshal(testUserInviteAcceptResponse)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Users.AcceptInvite(context.Background(), "some-token", api.PostApiUsersInvitesTokenAcceptJSONRequestBody{})
require.NoError(t, err)
assert.Equal(t, testUserInviteAcceptResponse, *ret)
})
}
func TestUsers_AcceptInvite_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/invites/some-token/accept", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Users.AcceptInvite(context.Background(), "some-token", api.PostApiUsersInvitesTokenAcceptJSONRequestBody{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestUsers_Approve_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/Test/approve", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
retBytes, _ := json.Marshal(testUser)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Users.Approve(context.Background(), "Test")
require.NoError(t, err)
assert.Equal(t, testUser, *ret)
})
}
func TestUsers_Approve_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/Test/approve", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Users.Approve(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestUsers_ChangePassword_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/Test/password", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PutApiUsersUserIdPasswordJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
w.WriteHeader(200)
})
err := c.Users.ChangePassword(context.Background(), "Test", api.PutApiUsersUserIdPasswordJSONRequestBody{})
require.NoError(t, err)
})
}
func TestUsers_ChangePassword_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/Test/password", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.Users.ChangePassword(context.Background(), "Test", api.PutApiUsersUserIdPasswordJSONRequestBody{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
})
}
func TestUsers_Reject_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/Test/reject", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.Users.Reject(context.Background(), "Test")
require.NoError(t, err)
})
}
func TestUsers_Reject_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/Test/reject", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.Users.Reject(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
})
}
func TestUsers_Integration(t *testing.T) {
withBlackBoxServer(t, func(c *rest.Client) {
// rest client PAT is owner's

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,14 @@ const (
TokenAuthScopes = "TokenAuth.Scopes"
)
// Defines values for CreateIntegrationRequestPlatform.
const (
CreateIntegrationRequestPlatformDatadog CreateIntegrationRequestPlatform = "datadog"
CreateIntegrationRequestPlatformFirehose CreateIntegrationRequestPlatform = "firehose"
CreateIntegrationRequestPlatformGenericHttp CreateIntegrationRequestPlatform = "generic_http"
CreateIntegrationRequestPlatformS3 CreateIntegrationRequestPlatform = "s3"
)
// Defines values for DNSRecordType.
const (
DNSRecordTypeA DNSRecordType = "A"
@@ -188,6 +196,20 @@ const (
IngressPortAllocationRequestPortRangeProtocolUdp IngressPortAllocationRequestPortRangeProtocol = "udp"
)
// Defines values for IntegrationResponsePlatform.
const (
IntegrationResponsePlatformDatadog IntegrationResponsePlatform = "datadog"
IntegrationResponsePlatformFirehose IntegrationResponsePlatform = "firehose"
IntegrationResponsePlatformGenericHttp IntegrationResponsePlatform = "generic_http"
IntegrationResponsePlatformS3 IntegrationResponsePlatform = "s3"
)
// Defines values for InvoiceResponseType.
const (
InvoiceResponseTypeAccount InvoiceResponseType = "account"
InvoiceResponseTypeTenants InvoiceResponseType = "tenants"
)
// Defines values for JobResponseStatus.
const (
JobResponseStatusFailed JobResponseStatus = "failed"
@@ -266,6 +288,21 @@ const (
ResourceTypeSubnet ResourceType = "subnet"
)
// Defines values for SentinelOneMatchAttributesNetworkStatus.
const (
SentinelOneMatchAttributesNetworkStatusConnected SentinelOneMatchAttributesNetworkStatus = "connected"
SentinelOneMatchAttributesNetworkStatusDisconnected SentinelOneMatchAttributesNetworkStatus = "disconnected"
SentinelOneMatchAttributesNetworkStatusQuarantined SentinelOneMatchAttributesNetworkStatus = "quarantined"
)
// Defines values for TenantResponseStatus.
const (
TenantResponseStatusActive TenantResponseStatus = "active"
TenantResponseStatusExisting TenantResponseStatus = "existing"
TenantResponseStatusInvited TenantResponseStatus = "invited"
TenantResponseStatusPending TenantResponseStatus = "pending"
)
// Defines values for UserStatus.
const (
UserStatusActive UserStatus = "active"
@@ -299,6 +336,12 @@ const (
GetApiEventsNetworkTrafficParamsDirectionINGRESS GetApiEventsNetworkTrafficParamsDirection = "INGRESS"
)
// Defines values for PutApiIntegrationsMspTenantsIdInviteJSONBodyValue.
const (
PutApiIntegrationsMspTenantsIdInviteJSONBodyValueAccept PutApiIntegrationsMspTenantsIdInviteJSONBodyValue = "accept"
PutApiIntegrationsMspTenantsIdInviteJSONBodyValueDecline PutApiIntegrationsMspTenantsIdInviteJSONBodyValue = "decline"
)
// AccessiblePeer defines model for AccessiblePeer.
type AccessiblePeer struct {
// CityName Commonly used English name of the city
@@ -490,6 +533,21 @@ type BundleWorkloadResponse struct {
Type WorkloadType `json:"type"`
}
// BypassResponse Response for bypassed peer operations.
type BypassResponse struct {
// PeerId The ID of the bypassed peer.
PeerId string `json:"peer_id"`
}
// CheckoutResponse defines model for CheckoutResponse.
type CheckoutResponse struct {
// SessionId The unique identifier for the checkout session.
SessionId string `json:"session_id"`
// Url URL to redirect the user to the checkout session.
Url string `json:"url"`
}
// Checks List of objects that perform the actual checks
type Checks struct {
// GeoLocationCheck Posture check for geo location
@@ -532,6 +590,36 @@ type Country struct {
// CountryCode 2-letter ISO 3166-1 alpha-2 code that represents the country
type CountryCode = string
// CreateIntegrationRequest Request payload for creating a new event streaming integration. Also used as the structure for the PUT request body, but not all fields are applicable for updates (see PUT operation description).
type CreateIntegrationRequest struct {
// Config Platform-specific configuration as key-value pairs. For creation, all necessary credentials and settings must be provided. For updates, provide the fields to change or the entire new configuration.
Config map[string]string `json:"config"`
// Enabled Specifies whether the integration is enabled. During creation (POST), this value is sent by the client, but the provided backend manager function `CreateIntegration` does not appear to use it directly, so its effect on creation should be verified. During updates (PUT), this field is used to enable or disable the integration.
Enabled bool `json:"enabled"`
// Platform The event streaming platform to integrate with (e.g., "datadog", "s3", "firehose"). This field is used for creation. For updates (PUT), this field, if sent, is ignored by the backend.
Platform CreateIntegrationRequestPlatform `json:"platform"`
}
// CreateIntegrationRequestPlatform The event streaming platform to integrate with (e.g., "datadog", "s3", "firehose"). This field is used for creation. For updates (PUT), this field, if sent, is ignored by the backend.
type CreateIntegrationRequestPlatform string
// CreateScimIntegrationRequest Request payload for creating an SCIM IDP integration
type CreateScimIntegrationRequest struct {
// GroupPrefixes List of start_with string patterns for groups to sync
GroupPrefixes *[]string `json:"group_prefixes,omitempty"`
// Prefix The connection prefix used for the SCIM provider
Prefix string `json:"prefix"`
// Provider Name of the SCIM identity provider
Provider string `json:"provider"`
// UserGroupPrefixes List of start_with string patterns for groups which users to sync
UserGroupPrefixes *[]string `json:"user_group_prefixes,omitempty"`
}
// CreateSetupKeyRequest defines model for CreateSetupKeyRequest.
type CreateSetupKeyRequest struct {
// AllowExtraDnsLabels Allow extra DNS labels to be added to the peer
@@ -556,6 +644,24 @@ type CreateSetupKeyRequest struct {
UsageLimit int `json:"usage_limit"`
}
// CreateTenantRequest defines model for CreateTenantRequest.
type CreateTenantRequest struct {
// Domain The name for the MSP tenant
Domain string `json:"domain"`
// Groups MSP users Groups that can access the Tenant and Roles to assume
Groups []TenantGroupResponse `json:"groups"`
// Name The name for the MSP tenant
Name string `json:"name"`
}
// DNSChallengeResponse defines model for DNSChallengeResponse.
type DNSChallengeResponse struct {
// DnsChallenge The DNS challenge to set in a TXT record
DnsChallenge string `json:"dns_challenge"`
}
// DNSRecord defines model for DNSRecord.
type DNSRecord struct {
// Content DNS record content (IP address for A/AAAA, domain for CNAME)
@@ -598,6 +704,234 @@ type DNSSettings struct {
DisabledManagementGroups []string `json:"disabled_management_groups"`
}
// EDRFalconRequest Request payload for creating or updating a EDR Falcon integration
type EDRFalconRequest struct {
// ClientId CrowdStrike API client ID
ClientId string `json:"client_id"`
// CloudId CrowdStrike cloud identifier (e.g., "us-1", "us-2", "eu-1")
CloudId string `json:"cloud_id"`
// Enabled Indicates whether the integration is enabled
Enabled *bool `json:"enabled,omitempty"`
// Groups The Groups this integration applies to
Groups []string `json:"groups"`
// Secret CrowdStrike API client secret
Secret string `json:"secret"`
// ZtaScoreThreshold The minimum Zero Trust Assessment score required for agent approval (0-100)
ZtaScoreThreshold int `json:"zta_score_threshold"`
}
// EDRFalconResponse Represents a Falcon EDR integration
type EDRFalconResponse struct {
// AccountId The identifier of the account this integration belongs to.
AccountId string `json:"account_id"`
// CloudId CrowdStrike cloud identifier
CloudId string `json:"cloud_id"`
// CreatedAt Timestamp of when the integration was created.
CreatedAt time.Time `json:"created_at"`
// CreatedBy The user id that created the integration
CreatedBy string `json:"created_by"`
// Enabled Indicates whether the integration is enabled
Enabled bool `json:"enabled"`
// Groups List of groups
Groups []Group `json:"groups"`
// Id The unique numeric identifier for the integration.
Id int64 `json:"id"`
// LastSyncedAt Timestamp of when the integration was last synced.
LastSyncedAt time.Time `json:"last_synced_at"`
// UpdatedAt Timestamp of when the integration was last updated.
UpdatedAt time.Time `json:"updated_at"`
// ZtaScoreThreshold The minimum Zero Trust Assessment score required for agent approval (0-100)
ZtaScoreThreshold int `json:"zta_score_threshold"`
}
// EDRHuntressRequest Request payload for creating or updating a EDR Huntress integration
type EDRHuntressRequest struct {
// ApiKey Huntress API key
ApiKey string `json:"api_key"`
// ApiSecret Huntress API secret
ApiSecret string `json:"api_secret"`
// Enabled Indicates whether the integration is enabled
Enabled *bool `json:"enabled,omitempty"`
// Groups The Groups this integrations applies to
Groups []string `json:"groups"`
// LastSyncedInterval The devices last sync requirement interval in hours. Minimum value is 24 hours
LastSyncedInterval int `json:"last_synced_interval"`
// MatchAttributes Attribute conditions to match when approving agents
MatchAttributes HuntressMatchAttributes `json:"match_attributes"`
}
// EDRHuntressResponse Represents a Huntress EDR integration configuration
type EDRHuntressResponse struct {
// AccountId The identifier of the account this integration belongs to.
AccountId string `json:"account_id"`
// CreatedAt Timestamp of when the integration was created.
CreatedAt time.Time `json:"created_at"`
// CreatedBy The user id that created the integration
CreatedBy string `json:"created_by"`
// Enabled Indicates whether the integration is enabled
Enabled bool `json:"enabled"`
// Groups List of groups
Groups []Group `json:"groups"`
// Id The unique numeric identifier for the integration.
Id int64 `json:"id"`
// LastSyncedAt Timestamp of when the integration was last synced.
LastSyncedAt time.Time `json:"last_synced_at"`
// LastSyncedInterval The devices last sync requirement interval in hours.
LastSyncedInterval int `json:"last_synced_interval"`
// MatchAttributes Attribute conditions to match when approving agents
MatchAttributes HuntressMatchAttributes `json:"match_attributes"`
// UpdatedAt Timestamp of when the integration was last updated.
UpdatedAt time.Time `json:"updated_at"`
}
// EDRIntuneRequest Request payload for creating or updating a EDR Intune integration.
type EDRIntuneRequest struct {
// ClientId The Azure application client id
ClientId string `json:"client_id"`
// Enabled Indicates whether the integration is enabled
Enabled *bool `json:"enabled,omitempty"`
// Groups The Groups this integrations applies to
Groups []string `json:"groups"`
// LastSyncedInterval The devices last sync requirement interval in hours. Minimum value is 24 hours.
LastSyncedInterval int `json:"last_synced_interval"`
// Secret The Azure application client secret
Secret string `json:"secret"`
// TenantId The Azure tenant id
TenantId string `json:"tenant_id"`
}
// EDRIntuneResponse Represents a Intune EDR integration configuration
type EDRIntuneResponse struct {
// AccountId The identifier of the account this integration belongs to.
AccountId string `json:"account_id"`
// ClientId The Azure application client id
ClientId string `json:"client_id"`
// CreatedAt Timestamp of when the integration was created.
CreatedAt time.Time `json:"created_at"`
// CreatedBy The user id that created the integration
CreatedBy string `json:"created_by"`
// Enabled Indicates whether the integration is enabled
Enabled bool `json:"enabled"`
// Groups List of groups
Groups []Group `json:"groups"`
// Id The unique numeric identifier for the integration.
Id int64 `json:"id"`
// LastSyncedAt Timestamp of when the integration was last synced.
LastSyncedAt time.Time `json:"last_synced_at"`
// LastSyncedInterval The devices last sync requirement interval in hours.
LastSyncedInterval int `json:"last_synced_interval"`
// TenantId The Azure tenant id
TenantId string `json:"tenant_id"`
// UpdatedAt Timestamp of when the integration was last updated.
UpdatedAt time.Time `json:"updated_at"`
}
// EDRSentinelOneRequest Request payload for creating or updating a EDR SentinelOne integration
type EDRSentinelOneRequest struct {
// ApiToken SentinelOne API token
ApiToken string `json:"api_token"`
// ApiUrl The Base URL of SentinelOne API
ApiUrl string `json:"api_url"`
// Enabled Indicates whether the integration is enabled
Enabled *bool `json:"enabled,omitempty"`
// Groups The Groups this integrations applies to
Groups []string `json:"groups"`
// LastSyncedInterval The devices last sync requirement interval in hours. Minimum value is 24 hours.
LastSyncedInterval int `json:"last_synced_interval"`
// MatchAttributes Attribute conditions to match when approving agents
MatchAttributes SentinelOneMatchAttributes `json:"match_attributes"`
}
// EDRSentinelOneResponse Represents a SentinelOne EDR integration configuration
type EDRSentinelOneResponse struct {
// AccountId The identifier of the account this integration belongs to.
AccountId string `json:"account_id"`
// ApiUrl The Base URL of SentinelOne API
ApiUrl string `json:"api_url"`
// CreatedAt Timestamp of when the integration was created.
CreatedAt time.Time `json:"created_at"`
// CreatedBy The user id that created the integration
CreatedBy string `json:"created_by"`
// Enabled Indicates whether the integration is enabled
Enabled bool `json:"enabled"`
// Groups List of groups
Groups []Group `json:"groups"`
// Id The unique numeric identifier for the integration.
Id int64 `json:"id"`
// LastSyncedAt Timestamp of when the integration was last synced.
LastSyncedAt time.Time `json:"last_synced_at"`
// LastSyncedInterval The devices last sync requirement interval in hours.
LastSyncedInterval int `json:"last_synced_interval"`
// MatchAttributes Attribute conditions to match when approving agents
MatchAttributes SentinelOneMatchAttributes `json:"match_attributes"`
// UpdatedAt Timestamp of when the integration was last updated.
UpdatedAt time.Time `json:"updated_at"`
}
// ErrorResponse Standard error response. Note: The exact structure of this error response is inferred from `util.WriteErrorResponse` and `util.WriteError` usage in the provided Go code, as a specific Go struct for errors was not provided.
type ErrorResponse struct {
// Message A human-readable error message.
Message *string `json:"message,omitempty"`
}
// Event defines model for Event.
type Event struct {
// Activity The activity that occurred during the event
@@ -643,6 +977,9 @@ type GeoLocationCheck struct {
// GeoLocationCheckAction Action to take upon policy match
type GeoLocationCheckAction string
// GetTenantsResponse defines model for GetTenantsResponse.
type GetTenantsResponse = []TenantResponse
// Group defines model for Group.
type Group struct {
// Id Group ID
@@ -699,6 +1036,21 @@ type GroupRequest struct {
Resources *[]Resource `json:"resources,omitempty"`
}
// HuntressMatchAttributes Attribute conditions to match when approving agents
type HuntressMatchAttributes struct {
// DefenderPolicyStatus Policy status of Defender AV for Managed Antivirus.
DefenderPolicyStatus *string `json:"defender_policy_status,omitempty"`
// DefenderStatus Status of Defender AV Managed Antivirus.
DefenderStatus *string `json:"defender_status,omitempty"`
// DefenderSubstatus Sub-status of Defender AV Managed Antivirus.
DefenderSubstatus *string `json:"defender_substatus,omitempty"`
// FirewallStatus Status of agent firewall. Can be one of Disabled, Enabled, Pending Isolation, Isolated, Pending Release.
FirewallStatus *string `json:"firewall_status,omitempty"`
}
// IdentityProvider defines model for IdentityProvider.
type IdentityProvider struct {
// ClientId OAuth2 client ID
@@ -738,6 +1090,21 @@ type IdentityProviderRequest struct {
// IdentityProviderType Type of identity provider
type IdentityProviderType string
// IdpIntegrationSyncLog Represents a synchronization log entry for an integration
type IdpIntegrationSyncLog struct {
// Id The unique identifier for the sync log
Id int64 `json:"id"`
// Level The log level
Level string `json:"level"`
// Message Log message
Message string `json:"message"`
// Timestamp Timestamp of when the log was created
Timestamp time.Time `json:"timestamp"`
}
// IngressPeer defines model for IngressPeer.
type IngressPeer struct {
AvailablePorts AvailablePorts `json:"available_ports"`
@@ -892,6 +1259,57 @@ type InstanceVersionInfo struct {
ManagementUpdateAvailable bool `json:"management_update_available"`
}
// IntegrationResponse Represents an event streaming integration.
type IntegrationResponse struct {
// AccountId The identifier of the account this integration belongs to.
AccountId *string `json:"account_id,omitempty"`
// Config Configuration for the integration. Sensitive keys (like API keys, secret keys) are masked with '****' in responses, as indicated by the GetIntegration handler logic.
Config *map[string]string `json:"config,omitempty"`
// CreatedAt Timestamp of when the integration was created.
CreatedAt *time.Time `json:"created_at,omitempty"`
// Enabled Whether the integration is currently active.
Enabled *bool `json:"enabled,omitempty"`
// Id The unique numeric identifier for the integration.
Id *int64 `json:"id,omitempty"`
// Platform The event streaming platform.
Platform *IntegrationResponsePlatform `json:"platform,omitempty"`
// UpdatedAt Timestamp of when the integration was last updated.
UpdatedAt *time.Time `json:"updated_at,omitempty"`
}
// IntegrationResponsePlatform The event streaming platform.
type IntegrationResponsePlatform string
// InvoicePDFResponse defines model for InvoicePDFResponse.
type InvoicePDFResponse struct {
// Url URL to redirect the user to invoice.
Url string `json:"url"`
}
// InvoiceResponse defines model for InvoiceResponse.
type InvoiceResponse struct {
// Id The Stripe invoice id
Id string `json:"id"`
// PeriodEnd The end date of the invoice period.
PeriodEnd time.Time `json:"period_end"`
// PeriodStart The start date of the invoice period.
PeriodStart time.Time `json:"period_start"`
// Type The invoice type
Type InvoiceResponseType `json:"type"`
}
// InvoiceResponseType The invoice type
type InvoiceResponseType string
// JobRequest defines model for JobRequest.
type JobRequest struct {
Workload WorkloadRequest `json:"workload"`
@@ -1797,6 +2215,15 @@ type PolicyUpdate struct {
SourcePostureChecks *[]string `json:"source_posture_checks,omitempty"`
}
// PortalResponse defines model for PortalResponse.
type PortalResponse struct {
// SessionId The unique identifier for the customer portal session.
SessionId string `json:"session_id"`
// Url URL to redirect the user to the customer portal.
Url string `json:"url"`
}
// PostureCheck defines model for PostureCheck.
type PostureCheck struct {
// Checks List of objects that perform the actual checks
@@ -1824,6 +2251,21 @@ type PostureCheckUpdate struct {
Name string `json:"name"`
}
// Price defines model for Price.
type Price struct {
// Currency Currency code for this price.
Currency string `json:"currency"`
// Price Price amount in minor units (e.g., cents).
Price int `json:"price"`
// PriceId Unique identifier for the price.
PriceId string `json:"price_id"`
// Unit Unit of measurement for this price (e.g., per user).
Unit string `json:"unit"`
}
// Process Describes the operational activity within a peer's system.
type Process struct {
// LinuxPath Path to the process executable file in a Linux operating system
@@ -1841,6 +2283,24 @@ type ProcessCheck struct {
Processes []Process `json:"processes"`
}
// Product defines model for Product.
type Product struct {
// Description Detailed description of the product.
Description string `json:"description"`
// Features List of features provided by the product.
Features []string `json:"features"`
// Free Indicates whether the product is free or not.
Free bool `json:"free"`
// Name Name of the product.
Name string `json:"name"`
// Prices List of prices for the product in different currencies
Prices []Price `json:"prices"`
}
// Resource defines model for Resource.
type Resource struct {
// Id ID of the resource
@@ -1950,6 +2410,66 @@ type RulePortRange struct {
Start int `json:"start"`
}
// ScimIntegration Represents a SCIM IDP integration
type ScimIntegration struct {
// AuthToken SCIM API token (full on creation, masked otherwise)
AuthToken string `json:"auth_token"`
// Enabled Indicates whether the integration is enabled
Enabled bool `json:"enabled"`
// GroupPrefixes List of start_with string patterns for groups to sync
GroupPrefixes []string `json:"group_prefixes"`
// Id The unique identifier for the integration
Id int64 `json:"id"`
// LastSyncedAt Timestamp of when the integration was last synced
LastSyncedAt time.Time `json:"last_synced_at"`
// Provider Name of the SCIM identity provider
Provider string `json:"provider"`
// UserGroupPrefixes List of start_with string patterns for groups which users to sync
UserGroupPrefixes []string `json:"user_group_prefixes"`
}
// ScimTokenResponse Response containing the regenerated SCIM token
type ScimTokenResponse struct {
// AuthToken The newly generated SCIM API token
AuthToken string `json:"auth_token"`
}
// SentinelOneMatchAttributes Attribute conditions to match when approving agents
type SentinelOneMatchAttributes struct {
// ActiveThreats The maximum allowed number of active threats on the agent
ActiveThreats *int `json:"active_threats,omitempty"`
// EncryptedApplications Whether disk encryption is enabled on the agent
EncryptedApplications *bool `json:"encrypted_applications,omitempty"`
// FirewallEnabled Whether the agent firewall is enabled
FirewallEnabled *bool `json:"firewall_enabled,omitempty"`
// Infected Whether the agent is currently flagged as infected
Infected *bool `json:"infected,omitempty"`
// IsActive Whether the agent has been recently active and reporting
IsActive *bool `json:"is_active,omitempty"`
// IsUpToDate Whether the agent is running the latest available version
IsUpToDate *bool `json:"is_up_to_date,omitempty"`
// NetworkStatus The current network connectivity status of the device
NetworkStatus *SentinelOneMatchAttributesNetworkStatus `json:"network_status,omitempty"`
// OperationalState The current operational state of the agent
OperationalState *string `json:"operational_state,omitempty"`
}
// SentinelOneMatchAttributesNetworkStatus The current network connectivity status of the device
type SentinelOneMatchAttributesNetworkStatus string
// SetupKey defines model for SetupKey.
type SetupKey struct {
// AllowExtraDnsLabels Allow extra DNS labels to be added to the peer
@@ -2121,6 +2641,117 @@ type SetupResponse struct {
UserId string `json:"user_id"`
}
// Subscription defines model for Subscription.
type Subscription struct {
// Active Indicates whether the subscription is active or not.
Active bool `json:"active"`
// Currency Currency code of the subscription.
Currency string `json:"currency"`
// Features List of features included in the subscription.
Features *[]string `json:"features,omitempty"`
// PlanTier The tier of the plan for the subscription.
PlanTier string `json:"plan_tier"`
// Price Price amount in minor units (e.g., cents).
Price int `json:"price"`
// PriceId Unique identifier for the price of the subscription.
PriceId string `json:"price_id"`
// Provider The provider of the subscription.
Provider string `json:"provider"`
// RemainingTrial The remaining time for the trial period, in seconds.
RemainingTrial *int `json:"remaining_trial,omitempty"`
// UpdatedAt The date and time when the subscription was last updated.
UpdatedAt time.Time `json:"updated_at"`
}
// TenantGroupResponse defines model for TenantGroupResponse.
type TenantGroupResponse struct {
// Id The Group ID
Id string `json:"id"`
// Role The Role name
Role string `json:"role"`
}
// TenantResponse defines model for TenantResponse.
type TenantResponse struct {
// ActivatedAt The date and time when the tenant was activated.
ActivatedAt *time.Time `json:"activated_at,omitempty"`
// CreatedAt The date and time when the tenant was created.
CreatedAt time.Time `json:"created_at"`
// DnsChallenge The DNS challenge to set in a TXT record
DnsChallenge string `json:"dns_challenge"`
// Domain The tenant account domain
Domain string `json:"domain"`
// Groups MSP users Groups that can access the Tenant and Roles to assume
Groups []TenantGroupResponse `json:"groups"`
// Id The updated MSP tenant account ID
Id string `json:"id"`
// InvitedAt The date and time when the existing tenant was invited.
InvitedAt *time.Time `json:"invited_at,omitempty"`
// Name The name for the MSP tenant
Name string `json:"name"`
// Status The status of the tenant
Status TenantResponseStatus `json:"status"`
// UpdatedAt The date and time when the tenant was last updated.
UpdatedAt time.Time `json:"updated_at"`
}
// TenantResponseStatus The status of the tenant
type TenantResponseStatus string
// UpdateScimIntegrationRequest Request payload for updating an SCIM IDP integration
type UpdateScimIntegrationRequest struct {
// Enabled Indicates whether the integration is enabled
Enabled *bool `json:"enabled,omitempty"`
// GroupPrefixes List of start_with string patterns for groups to sync
GroupPrefixes *[]string `json:"group_prefixes,omitempty"`
// UserGroupPrefixes List of start_with string patterns for groups which users to sync
UserGroupPrefixes *[]string `json:"user_group_prefixes,omitempty"`
}
// UpdateTenantRequest defines model for UpdateTenantRequest.
type UpdateTenantRequest struct {
// Groups MSP users Groups that can access the Tenant and Roles to assume
Groups []TenantGroupResponse `json:"groups"`
// Name The name for the MSP tenant
Name string `json:"name"`
}
// UsageStats defines model for UsageStats.
type UsageStats struct {
// ActivePeers Number of active peers.
ActivePeers int64 `json:"active_peers"`
// ActiveUsers Number of active users.
ActiveUsers int64 `json:"active_users"`
// TotalPeers Total number of peers.
TotalPeers int64 `json:"total_peers"`
// TotalUsers Total number of users.
TotalUsers int64 `json:"total_users"`
}
// User defines model for User.
type User struct {
// AutoGroups Group IDs to auto-assign to peers registered by this user
@@ -2407,6 +3038,66 @@ type GetApiGroupsParams struct {
Name *string `form:"name,omitempty" json:"name,omitempty"`
}
// PostApiIntegrationsBillingAwsMarketplaceActivateJSONBody defines parameters for PostApiIntegrationsBillingAwsMarketplaceActivate.
type PostApiIntegrationsBillingAwsMarketplaceActivateJSONBody struct {
// PlanTier The plan tier to activate the subscription for.
PlanTier string `json:"plan_tier"`
}
// PostApiIntegrationsBillingAwsMarketplaceEnrichJSONBody defines parameters for PostApiIntegrationsBillingAwsMarketplaceEnrich.
type PostApiIntegrationsBillingAwsMarketplaceEnrichJSONBody struct {
// AwsUserId The AWS user ID.
AwsUserId string `json:"aws_user_id"`
}
// PostApiIntegrationsBillingCheckoutJSONBody defines parameters for PostApiIntegrationsBillingCheckout.
type PostApiIntegrationsBillingCheckoutJSONBody struct {
// BaseURL The base URL for the redirect after checkout.
BaseURL string `json:"baseURL"`
// EnableTrial Enables a 14-day trial for the account.
EnableTrial *bool `json:"enableTrial,omitempty"`
// PriceID The Price ID for checkout.
PriceID string `json:"priceID"`
}
// GetApiIntegrationsBillingPortalParams defines parameters for GetApiIntegrationsBillingPortal.
type GetApiIntegrationsBillingPortalParams struct {
// BaseURL The base URL for the redirect after accessing the portal.
BaseURL string `form:"baseURL" json:"baseURL"`
}
// PutApiIntegrationsBillingSubscriptionJSONBody defines parameters for PutApiIntegrationsBillingSubscription.
type PutApiIntegrationsBillingSubscriptionJSONBody struct {
// PlanTier The plan tier to change the subscription to.
PlanTier *string `json:"plan_tier,omitempty"`
// PriceID The Price ID to change the subscription to.
PriceID *string `json:"priceID,omitempty"`
}
// PutApiIntegrationsMspTenantsIdInviteJSONBody defines parameters for PutApiIntegrationsMspTenantsIdInvite.
type PutApiIntegrationsMspTenantsIdInviteJSONBody struct {
// Value Accept or decline the invitation.
Value PutApiIntegrationsMspTenantsIdInviteJSONBodyValue `json:"value"`
}
// PutApiIntegrationsMspTenantsIdInviteJSONBodyValue defines parameters for PutApiIntegrationsMspTenantsIdInvite.
type PutApiIntegrationsMspTenantsIdInviteJSONBodyValue string
// PostApiIntegrationsMspTenantsIdSubscriptionJSONBody defines parameters for PostApiIntegrationsMspTenantsIdSubscription.
type PostApiIntegrationsMspTenantsIdSubscriptionJSONBody struct {
// PriceID The Price ID to change the subscription to.
PriceID string `json:"priceID"`
}
// PostApiIntegrationsMspTenantsIdUnlinkJSONBody defines parameters for PostApiIntegrationsMspTenantsIdUnlink.
type PostApiIntegrationsMspTenantsIdUnlinkJSONBody struct {
// Owner The new owners user ID.
Owner string `json:"owner"`
}
// GetApiPeersParams defines parameters for GetApiPeers.
type GetApiPeersParams struct {
// Name Filter peers by name
@@ -2452,6 +3143,12 @@ type PostApiDnsZonesZoneIdRecordsJSONRequestBody = DNSRecordRequest
// PutApiDnsZonesZoneIdRecordsRecordIdJSONRequestBody defines body for PutApiDnsZonesZoneIdRecordsRecordId for application/json ContentType.
type PutApiDnsZonesZoneIdRecordsRecordIdJSONRequestBody = DNSRecordRequest
// CreateIntegrationJSONRequestBody defines body for CreateIntegration for application/json ContentType.
type CreateIntegrationJSONRequestBody = CreateIntegrationRequest
// UpdateIntegrationJSONRequestBody defines body for UpdateIntegration for application/json ContentType.
type UpdateIntegrationJSONRequestBody = CreateIntegrationRequest
// PostApiGroupsJSONRequestBody defines body for PostApiGroups for application/json ContentType.
type PostApiGroupsJSONRequestBody = GroupRequest
@@ -2470,6 +3167,63 @@ type PostApiIngressPeersJSONRequestBody = IngressPeerCreateRequest
// PutApiIngressPeersIngressPeerIdJSONRequestBody defines body for PutApiIngressPeersIngressPeerId for application/json ContentType.
type PutApiIngressPeersIngressPeerIdJSONRequestBody = IngressPeerUpdateRequest
// PostApiIntegrationsBillingAwsMarketplaceActivateJSONRequestBody defines body for PostApiIntegrationsBillingAwsMarketplaceActivate for application/json ContentType.
type PostApiIntegrationsBillingAwsMarketplaceActivateJSONRequestBody PostApiIntegrationsBillingAwsMarketplaceActivateJSONBody
// PostApiIntegrationsBillingAwsMarketplaceEnrichJSONRequestBody defines body for PostApiIntegrationsBillingAwsMarketplaceEnrich for application/json ContentType.
type PostApiIntegrationsBillingAwsMarketplaceEnrichJSONRequestBody PostApiIntegrationsBillingAwsMarketplaceEnrichJSONBody
// PostApiIntegrationsBillingCheckoutJSONRequestBody defines body for PostApiIntegrationsBillingCheckout for application/json ContentType.
type PostApiIntegrationsBillingCheckoutJSONRequestBody PostApiIntegrationsBillingCheckoutJSONBody
// PutApiIntegrationsBillingSubscriptionJSONRequestBody defines body for PutApiIntegrationsBillingSubscription for application/json ContentType.
type PutApiIntegrationsBillingSubscriptionJSONRequestBody PutApiIntegrationsBillingSubscriptionJSONBody
// CreateFalconEDRIntegrationJSONRequestBody defines body for CreateFalconEDRIntegration for application/json ContentType.
type CreateFalconEDRIntegrationJSONRequestBody = EDRFalconRequest
// UpdateFalconEDRIntegrationJSONRequestBody defines body for UpdateFalconEDRIntegration for application/json ContentType.
type UpdateFalconEDRIntegrationJSONRequestBody = EDRFalconRequest
// CreateHuntressEDRIntegrationJSONRequestBody defines body for CreateHuntressEDRIntegration for application/json ContentType.
type CreateHuntressEDRIntegrationJSONRequestBody = EDRHuntressRequest
// UpdateHuntressEDRIntegrationJSONRequestBody defines body for UpdateHuntressEDRIntegration for application/json ContentType.
type UpdateHuntressEDRIntegrationJSONRequestBody = EDRHuntressRequest
// CreateEDRIntegrationJSONRequestBody defines body for CreateEDRIntegration for application/json ContentType.
type CreateEDRIntegrationJSONRequestBody = EDRIntuneRequest
// UpdateEDRIntegrationJSONRequestBody defines body for UpdateEDRIntegration for application/json ContentType.
type UpdateEDRIntegrationJSONRequestBody = EDRIntuneRequest
// CreateSentinelOneEDRIntegrationJSONRequestBody defines body for CreateSentinelOneEDRIntegration for application/json ContentType.
type CreateSentinelOneEDRIntegrationJSONRequestBody = EDRSentinelOneRequest
// UpdateSentinelOneEDRIntegrationJSONRequestBody defines body for UpdateSentinelOneEDRIntegration for application/json ContentType.
type UpdateSentinelOneEDRIntegrationJSONRequestBody = EDRSentinelOneRequest
// PostApiIntegrationsMspTenantsJSONRequestBody defines body for PostApiIntegrationsMspTenants for application/json ContentType.
type PostApiIntegrationsMspTenantsJSONRequestBody = CreateTenantRequest
// PutApiIntegrationsMspTenantsIdJSONRequestBody defines body for PutApiIntegrationsMspTenantsId for application/json ContentType.
type PutApiIntegrationsMspTenantsIdJSONRequestBody = UpdateTenantRequest
// PutApiIntegrationsMspTenantsIdInviteJSONRequestBody defines body for PutApiIntegrationsMspTenantsIdInvite for application/json ContentType.
type PutApiIntegrationsMspTenantsIdInviteJSONRequestBody PutApiIntegrationsMspTenantsIdInviteJSONBody
// PostApiIntegrationsMspTenantsIdSubscriptionJSONRequestBody defines body for PostApiIntegrationsMspTenantsIdSubscription for application/json ContentType.
type PostApiIntegrationsMspTenantsIdSubscriptionJSONRequestBody PostApiIntegrationsMspTenantsIdSubscriptionJSONBody
// PostApiIntegrationsMspTenantsIdUnlinkJSONRequestBody defines body for PostApiIntegrationsMspTenantsIdUnlink for application/json ContentType.
type PostApiIntegrationsMspTenantsIdUnlinkJSONRequestBody PostApiIntegrationsMspTenantsIdUnlinkJSONBody
// CreateSCIMIntegrationJSONRequestBody defines body for CreateSCIMIntegration for application/json ContentType.
type CreateSCIMIntegrationJSONRequestBody = CreateScimIntegrationRequest
// UpdateSCIMIntegrationJSONRequestBody defines body for UpdateSCIMIntegration for application/json ContentType.
type UpdateSCIMIntegrationJSONRequestBody = UpdateScimIntegrationRequest
// PostApiNetworksJSONRequestBody defines body for PostApiNetworks for application/json ContentType.
type PostApiNetworksJSONRequestBody = NetworkRequest