package goslide
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
)
type Account struct {
AccountID string `json:"account_id"`
AccountName string `json:"account_name"`
AlertEmails []string `json:"alert_emails"`
BillingAddress BillingAddress `json:"billing_address"`
PrimaryContact string `json:"primary_contact"`
PrimaryEmail string `json:"primary_email"`
PrimaryPhone string `json:"primary_phone"`
}
type BillingAddress struct {
City string `json:"City"`
Country string `json:"Country"`
Line1 string `json:"Line1"`
Line2 string `json:"Line2"`
PostalCode string `json:"PostalCode"`
State string `json:"State"`
}
type AccountService struct {
baseEndpoint string
requestClient *requestClient
}
// https://docs.slide.tech/api/#tag/accounts/GET/v1/account
func (a AccountService) List(
ctx context.Context,
pageHandler func(response ListResponse[Account]) error,
) error {
return a.ListWithQueryParameters(ctx, pageHandler)
}
// https://docs.slide.tech/api/#tag/accounts/GET/v1/account
func (a AccountService) ListWithQueryParameters(
ctx context.Context,
pageHandler func(response ListResponse[Account]) error,
options ...paginationQueryParam,
) error {
queryParams := url.Values{}
for _, option := range options {
option(queryParams)
}
for {
target := ListResponse[Account]{}
endpoint := a.baseEndpoint
if len(queryParams) > 0 {
endpoint = endpoint + "?"
}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
fmt.Sprintf("%s%s", endpoint, queryParams.Encode()),
http.NoBody,
)
if err != nil {
return err
}
if err := a.requestClient.SlideRequest(request, &target); err != nil {
return err
}
if err := pageHandler(target); err != nil {
return err
}
// No next offset marks the end of the paginated results
if target.Pagination.NextOffset == nil {
break
}
queryParams.Set(
"offset",
strconv.FormatUint(
uint64(*target.Pagination.NextOffset),
10,
),
)
}
return nil
}
// https://docs.slide.tech/api/#tag/accounts/GET/v1/account/{account_id}
func (a AccountService) Get(ctx context.Context, accountID string) (Account, error) {
target := Account{}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
a.baseEndpoint+"/"+accountID,
http.NoBody,
)
if err != nil {
return Account{}, err
}
if err := a.requestClient.SlideRequest(request, &target); err != nil {
return Account{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/accounts/PATCH/v1/account/{account_id}
func (a AccountService) Update(
ctx context.Context,
accountID string,
alertEmails []string,
) (Account, error) {
type accountPayload struct {
AlertEmails []string `json:"alert_emails"`
}
payloadBytes, err := json.Marshal(accountPayload{
AlertEmails: alertEmails,
})
if err != nil {
return Account{}, err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPatch,
a.baseEndpoint+"/"+accountID,
requestBody,
)
if err != nil {
return Account{}, err
}
target := Account{}
if err := a.requestClient.SlideRequest(request, &target); err != nil {
return Account{}, err
}
return target, nil
}
package goslide
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"time"
)
type Agent struct {
Addresses []Address `json:"addresses"`
AgentID string `json:"agent_id"`
AgentVersion string `json:"agent_version"`
BootedAt time.Time `json:"booted_at"`
ClientID string `json:"client_id"`
DeviceID string `json:"device_id"`
DisplayName string `json:"display_name"`
EncryptionAlgorithm string `json:"encryption_algorithm"`
FirmwareType string `json:"firmware_type"`
Hostname string `json:"hostname"`
LastSeenAt time.Time `json:"last_seen_at"`
Manufacturer string `json:"manufacturer"`
OS string `json:"os"`
OSVersion string `json:"os_version"`
Platform string `json:"platform"`
PublicIPAddress string `json:"public_ip_address"`
}
type AgentService struct {
baseEndpoint string
requestClient *requestClient
}
type AgentAutoPairPayload struct {
DeviceID string `json:"device_id"`
DisplayName string `json:"display_name"`
}
type AgentAutoPairResponse struct {
AgentID string `json:"agent_id"`
DisplayName string `json:"display_name"`
PairCode string `json:"pair_code"`
}
type AgentPairPayload struct {
DeviceID string `json:"device_id"`
PairCode string `json:"pair_code"`
}
// https://docs.slide.tech/api/#tag/agents/GET/v1/agent
func (a AgentService) List(
ctx context.Context,
pageHandler func(response ListResponse[Agent]) error,
) error {
return a.ListWithQueryParameters(ctx, pageHandler)
}
// https://docs.slide.tech/api/#tag/agents/GET/v1/agent
func (c AgentService) ListWithQueryParameters(
ctx context.Context,
pageHandler func(response ListResponse[Agent]) error,
options ...paginationQueryParam,
) error {
queryParams := url.Values{}
for _, option := range options {
option(queryParams)
}
for {
target := ListResponse[Agent]{}
endpoint := c.baseEndpoint
if len(queryParams) > 0 {
endpoint = endpoint + "?"
}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
fmt.Sprintf("%s%s", endpoint, queryParams.Encode()),
http.NoBody,
)
if err != nil {
return err
}
if err := c.requestClient.SlideRequest(request, &target); err != nil {
return err
}
if err := pageHandler(target); err != nil {
return err
}
// No next offset marks the end of the paginated results
if target.Pagination.NextOffset == nil {
break
}
queryParams.Set(
"offset",
strconv.FormatUint(
uint64(*target.Pagination.NextOffset),
10,
),
)
}
return nil
}
// https://docs.slide.tech/api/#tag/agents/POST/v1/agent
func (c AgentService) AutoPair(ctx context.Context, payload AgentAutoPairPayload) (AgentAutoPairResponse, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return AgentAutoPairResponse{}, err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
c.baseEndpoint,
requestBody,
)
if err != nil {
return AgentAutoPairResponse{}, err
}
target := AgentAutoPairResponse{}
if err := c.requestClient.SlideRequest(request, &target); err != nil {
return AgentAutoPairResponse{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/agents/POST/v1/agent/pair
func (c AgentService) Pair(ctx context.Context, payload AgentPairPayload) (Agent, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return Agent{}, err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
c.baseEndpoint,
requestBody,
)
if err != nil {
return Agent{}, err
}
target := Agent{}
if err := c.requestClient.SlideRequest(request, &target); err != nil {
return Agent{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/agents/GET/v1/agent/{agent_id}
func (c AgentService) Get(ctx context.Context, agentID string) (Agent, error) {
target := Agent{}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
c.baseEndpoint+"/"+agentID,
http.NoBody,
)
if err != nil {
return Agent{}, err
}
if err := c.requestClient.SlideRequest(request, &target); err != nil {
return Agent{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/agents/PATCH/v1/agent/{agent_id}
func (c AgentService) Update(ctx context.Context, agentID, displayName string) (Agent, error) {
type agentPayload struct {
DisplayName string `json:"display_name"`
}
payload := agentPayload{
DisplayName: displayName,
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return Agent{}, err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPatch,
c.baseEndpoint+"/"+agentID,
requestBody,
)
if err != nil {
return Agent{}, err
}
target := Agent{}
if err := c.requestClient.SlideRequest(request, &target); err != nil {
return Agent{}, err
}
return target, nil
}
package goslide
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"time"
)
type Alert struct {
AgentID string `json:"agent_id"`
AlertFields string `json:"alert_fields"`
AlertID string `json:"alert_id"`
AlertType AlertType `json:"alert_type"`
CreatedAt time.Time `json:"created_at"`
DeviceID string `json:"device_id"`
Resolved bool `json:"resolved"`
ResolvedAt *time.Time `json:"resolved_at"`
ResolvedBy string `json:"resolved_by"`
}
type AlertService struct {
baseEndpoint string
requestClient *requestClient
}
type AlertType string
const (
AlertType_DEVICE_NOT_CHECKING_IN AlertType = "device_not_checking_in"
AlertType_DEVICE_OUT_OF_DATE AlertType = "device_out_of_date"
AlertType_DEVICE_STORAGE_NOT_HEALTHY AlertType = "device_storage_not_healthy"
AlertType_DEVICE_STORAGE_SPACE_LOW AlertType = "device_storage_space_low"
AlertType_DEVICE_STORAGE_SPACE_CRITICAL AlertType = "device_storage_space_critical"
AlertType_AGENT_NOT_CHECKING_IN AlertType = "agent_not_checking_in"
AlertType_AGENT_NOT_BACKING_UP AlertType = "agent_not_backing_up"
AlertType_AGENT_BACKUP_FAILED AlertType = "agent_backup_failed"
)
// https://docs.slide.tech/api/#tag/alerts/GET/v1/alert/{alert_id}
func (a AlertService) Get(ctx context.Context, alertID string) (Alert, error) {
target := Alert{}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
a.baseEndpoint+"/"+alertID,
http.NoBody,
)
if err != nil {
return Alert{}, err
}
if err := a.requestClient.SlideRequest(request, &target); err != nil {
return Alert{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/alerts/GET/v1/alert
func (a AlertService) List(
ctx context.Context,
pageHandler func(response ListResponse[Alert]) error,
) error {
return a.ListWithQueryParameters(ctx, pageHandler)
}
// https://docs.slide.tech/api/#tag/alerts/GET/v1/alert
func (a AlertService) ListWithQueryParameters(
ctx context.Context,
pageHandler func(response ListResponse[Alert]) error,
options ...paginationQueryParam,
) error {
queryParams := url.Values{}
for _, option := range options {
option(queryParams)
}
for {
target := ListResponse[Alert]{}
endpoint := a.baseEndpoint
if len(queryParams) > 0 {
endpoint = endpoint + "?"
}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
fmt.Sprintf("%s%s", endpoint, queryParams.Encode()),
http.NoBody,
)
if err != nil {
return err
}
if err := a.requestClient.SlideRequest(request, &target); err != nil {
return err
}
if err := pageHandler(target); err != nil {
return err
}
// No next offset marks the end of the paginated results
if target.Pagination.NextOffset == nil {
break
}
queryParams.Set(
"offset",
strconv.FormatUint(
uint64(*target.Pagination.NextOffset),
10,
),
)
}
return nil
}
// https://docs.slide.tech/api/#tag/alerts/PATCH/v1/alert/{alert_id}
func (a AlertService) Update(
ctx context.Context,
alertID string,
resolved bool,
) (Alert, error) {
type alertPayload struct {
Resolved bool `json:"resolved"`
}
payloadBytes, err := json.Marshal(alertPayload{
Resolved: resolved,
})
if err != nil {
return Alert{}, err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPatch,
a.baseEndpoint+"/"+alertID,
requestBody,
)
if err != nil {
return Alert{}, err
}
target := Alert{}
if err := a.requestClient.SlideRequest(request, &target); err != nil {
return Alert{}, err
}
return target, nil
}
package goslide
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"time"
)
type BackupStatus string
const (
BackupStatus_CREATED BackupStatus = "created"
BackupStatus_PENDING BackupStatus = "pending"
BackupStatus_STARTED BackupStatus = "started"
BackupStatus_PREFLIGHT BackupStatus = "preflight"
BackupStatus_CONTACTING BackupStatus = "contacting"
BackupStatus_CREATING_VSS BackupStatus = "creating_vss"
BackupStatus_PREPARING BackupStatus = "preparing"
BackupStatus_TRANSFERRING BackupStatus = "transferring"
BackupStatus_SNAPSHOT BackupStatus = "snapshot"
BackupStatus_FINALIZING BackupStatus = "finalizing"
BackupStatus_CANCELING BackupStatus = "canceling"
BackupStatus_FAILING BackupStatus = "failing"
BackupStatus_CANCELED BackupStatus = "canceled"
BackupStatus_FAILED BackupStatus = "failed"
BackupStatus_SUCCEEDED BackupStatus = "succeeded"
)
type Backup struct {
AgentID string `json:"agent_id"`
BackupID string `json:"backup_id"`
EndedAt time.Time `json:"ended_at"`
ErrorCode uint `json:"error_code"`
ErrorMessage string `json:"error_message"`
SnapshotID string `json:"snapshot_id"`
StartedAt time.Time `json:"started_at"`
Status BackupStatus `json:"status"`
}
type BackupService struct {
baseEndpoint string
requestClient *requestClient
}
// https://docs.slide.tech/api/#tag/backups/GET/v1/backup
func (b BackupService) List(
ctx context.Context,
pageHandler func(response ListResponse[Backup]) error,
) error {
return b.ListWithQueryParameters(ctx, pageHandler)
}
// https://docs.slide.tech/api/#tag/backups/GET/v1/backup
func (b BackupService) ListWithQueryParameters(
ctx context.Context,
pageHandler func(response ListResponse[Backup]) error,
options ...paginationQueryParam,
) error {
queryParams := url.Values{}
for _, option := range options {
option(queryParams)
}
for {
target := ListResponse[Backup]{}
endpoint := b.baseEndpoint
if len(queryParams) > 0 {
endpoint = endpoint + "?"
}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
fmt.Sprintf("%s%s", endpoint, queryParams.Encode()),
http.NoBody,
)
if err != nil {
return err
}
if err := b.requestClient.SlideRequest(request, &target); err != nil {
return err
}
if err := pageHandler(target); err != nil {
return err
}
// No next offset marks the end of the paginated results
if target.Pagination.NextOffset == nil {
break
}
queryParams.Set(
"offset",
strconv.FormatUint(
uint64(*target.Pagination.NextOffset),
10,
),
)
}
return nil
}
// https://docs.slide.tech/api/#tag/backups/GET/v1/backup/{backup_id}
func (b BackupService) Get(ctx context.Context, backupID string) (Backup, error) {
target := Backup{}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
b.baseEndpoint+"/"+backupID,
http.NoBody,
)
if err != nil {
return Backup{}, err
}
if err := b.requestClient.SlideRequest(request, &target); err != nil {
return Backup{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/backups/POST/v1/backup
func (b BackupService) StartBackup(ctx context.Context, agentID string) error {
type backupPayload struct {
AgentID string `json:"agent_id"`
}
payloadBytes, err := json.Marshal(backupPayload{
AgentID: agentID,
})
if err != nil {
return err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
b.baseEndpoint,
requestBody,
)
if err != nil {
return err
}
return b.requestClient.SlideRequest(request, nil)
}
package goslide
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
)
type Client struct {
ClientID string `json:"client_id"`
Name string `json:"name"`
Comments string `json:"comments"`
}
type ClientService struct {
baseEndpoint string
requestClient *requestClient
}
type ClientPayload struct {
Comments string `json:"comments,omitempty"`
Name string `json:"name,omitempty"`
}
// https://docs.slide.tech/api/#tag/clients/GET/v1/client
func (c ClientService) List(
ctx context.Context,
pageHandler func(response ListResponse[Client]) error,
) error {
return c.ListWithQueryParameters(ctx, pageHandler)
}
// https://docs.slide.tech/api/#tag/clients/GET/v1/client
func (c ClientService) ListWithQueryParameters(
ctx context.Context,
pageHandler func(response ListResponse[Client]) error,
options ...paginationQueryParam,
) error {
queryParams := url.Values{}
for _, option := range options {
option(queryParams)
}
for {
target := ListResponse[Client]{}
endpoint := c.baseEndpoint
if len(queryParams) > 0 {
endpoint = endpoint + "?"
}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
fmt.Sprintf("%s%s", endpoint, queryParams.Encode()),
http.NoBody,
)
if err != nil {
return err
}
if err := c.requestClient.SlideRequest(request, &target); err != nil {
return err
}
if err := pageHandler(target); err != nil {
return err
}
// No next offset marks the end of the paginated results
if target.Pagination.NextOffset == nil {
break
}
queryParams.Set(
"offset",
strconv.FormatUint(
uint64(*target.Pagination.NextOffset),
10,
),
)
}
return nil
}
// https://docs.slide.tech/api/#tag/clients/POST/v1/client
func (c ClientService) Create(ctx context.Context, payload ClientPayload) (Client, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return Client{}, err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
c.baseEndpoint,
requestBody,
)
if err != nil {
return Client{}, err
}
target := Client{}
if err := c.requestClient.SlideRequest(request, &target); err != nil {
return Client{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/clients/GET/v1/client/{client_id}
func (c ClientService) Get(ctx context.Context, clientID string) (Client, error) {
target := Client{}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
c.baseEndpoint+"/"+clientID,
http.NoBody,
)
if err != nil {
return Client{}, err
}
if err := c.requestClient.SlideRequest(request, &target); err != nil {
return Client{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/clients/DELETE/v1/client/{client_id}
func (c ClientService) Delete(ctx context.Context, clientID string) error {
request, err := http.NewRequestWithContext(
ctx,
http.MethodDelete,
c.baseEndpoint+"/"+clientID,
http.NoBody,
)
if err != nil {
return err
}
return c.requestClient.SlideRequest(request, nil)
}
// https://docs.slide.tech/api/#tag/clients/PATCH/v1/client/{client_id}
func (c ClientService) Update(ctx context.Context, clientID string, payload ClientPayload) (Client, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return Client{}, err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPatch,
c.baseEndpoint+"/"+clientID,
requestBody,
)
if err != nil {
return Client{}, err
}
target := Client{}
if err := c.requestClient.SlideRequest(request, &target); err != nil {
return Client{}, err
}
return target, nil
}
package goslide
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"time"
)
type Device struct {
Addresses []Address `json:"addresses"`
BootedAt time.Time `json:"booted_at"`
ClientID string `json:"client_id"`
DeviceID string `json:"device_id"`
DisplayName string `json:"display_name"`
HardwareModelName string `json:"hardware_model_name"`
Hostname string `json:"hostname"`
ImageVersion string `json:"image_version"`
PublicIPAddress string `json:"public_ip_address"`
LastSeenAt time.Time `json:"last_seen_at"`
NFR bool `json:"nfr"`
PackageVersion string `json:"package_version"`
SerialNumber string `json:"serial_number"`
ServiceModelName string `json:"service_model_name"`
ServiceModelNameShort string `json:"service_model_name_short"`
ServiceStatus string `json:"service_status"`
StorageTotalBytes uint64 `json:"storage_total_bytes"`
StorageUsedBytes uint64 `json:"storage_used_bytes"`
}
type Address struct {
MAC string `json:"mac"`
IPs []string `json:"ips"`
}
type DeviceService struct {
baseEndpoint string
requestClient *requestClient
}
type DevicePayload struct {
DisplayName string `json:"display_name,omitempty"`
Hostname string `json:"hostname,omitempty"`
ClientID string `json:"client_id,omitempty"`
}
// https://docs.slide.tech/api/#tag/devices
func (d DeviceService) List(
ctx context.Context,
pageHandler func(response ListResponse[Device]) error,
) error {
return d.ListWithQueryParameters(ctx, pageHandler)
}
// https://docs.slide.tech/api/#tag/devices
func (d DeviceService) ListWithQueryParameters(
ctx context.Context,
pageHandler func(response ListResponse[Device]) error,
options ...paginationQueryParam,
) error {
queryParams := url.Values{}
for _, option := range options {
option(queryParams)
}
for {
target := ListResponse[Device]{}
endpoint := d.baseEndpoint
if len(queryParams) > 0 {
endpoint = endpoint + "?"
}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
fmt.Sprintf("%s%s", endpoint, queryParams.Encode()),
http.NoBody,
)
if err != nil {
return err
}
if err := d.requestClient.SlideRequest(request, &target); err != nil {
return err
}
if err := pageHandler(target); err != nil {
return err
}
// No next offset marks the end of the paginated results
if target.Pagination.NextOffset == nil {
break
}
queryParams.Set(
"offset",
strconv.FormatUint(
uint64(*target.Pagination.NextOffset),
10,
),
)
}
return nil
}
// https://docs.slide.tech/api/#tag/devices/GET/v1/device/{device_id}
func (d DeviceService) Get(ctx context.Context, deviceID string) (Device, error) {
target := Device{}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
d.baseEndpoint+"/"+deviceID,
http.NoBody,
)
if err != nil {
return Device{}, err
}
if err := d.requestClient.SlideRequest(request, &target); err != nil {
return Device{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/devices/PATCH/v1/device/{device_id}
func (d DeviceService) Update(ctx context.Context, deviceID string, payload DevicePayload) (Device, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return Device{}, err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPatch,
d.baseEndpoint+"/"+deviceID,
requestBody,
)
if err != nil {
return Device{}, err
}
target := Device{}
if err := d.requestClient.SlideRequest(request, &target); err != nil {
return Device{}, err
}
return target, nil
}
package goslide
import (
"fmt"
"strings"
)
type APIErrorCode string
const (
APIErrorCode_ERR_ENDPOINT_NOT_FOUND APIErrorCode = "err_endpoint_not_found"
APIErrorCode_ERR_ENTITY_NOT_FOUND APIErrorCode = "err_entity_not_found"
APIErrorCode_ERR_VALIDATION_ERROR APIErrorCode = "err_validation_error"
APIErrorCode_ERR_MISSING_AUTHENTICATION APIErrorCode = "err_missing_authentication"
APIErrorCode_ERR_UNAUTHORIZED APIErrorCode = "err_unauthorized"
APIErrorCode_ERR_INTERNAL_SERVER_ERROR APIErrorCode = "err_internal_server_error"
APIErrorCode_ERR_RATE_LIMIT_EXCEEDED APIErrorCode = "err_rate_limit_exceeded"
APIErrorCode_ERR_AGENT_NOT_CONNECTED_TO_DEVICE APIErrorCode = "err_agent_not_connected_to_device"
APIErrorCode_ERR_DEVICE_NOT_CONNECTED_TO_CLOUD APIErrorCode = "err_device_not_connected_to_cloud"
APIErrorCode_ERR_BACKUP_ALREADY_RUNNING APIErrorCode = "err_backup_already_running"
APIErrorCode_ERR_CLIENT_NOT_FOUND APIErrorCode = "err_client_not_found"
)
type SlideError struct {
HTTPStatusCode int
HTTPRequestPath string
HTTPRequestMethod string
Codes []APIErrorCode `json:"codes"`
Details []string `json:"details"`
Message string `json:"message"`
}
func (e *SlideError) Error() string {
var sb strings.Builder
sb.Write([]byte("slide api request error"))
if len(e.Codes) > 0 {
for _, errcode := range e.Codes {
sb.WriteString(fmt.Sprintf(" %s ", errcode))
}
}
return sb.String()
}
package goslide
import (
"context"
"errors"
"net/http"
)
// healthCheck is the service that is used to report whether an API Token is valid.
//
// TODO: This can be deprecated if Slide implements an endpoint to validate tokens.
type HealthCheck struct {
requestClient *requestClient
}
// IsAuthenticated makes a simple GET request to the list users endpoint with a limit of 1.
// If an error is encountered, the http.Response error code is checked to validate if the error is
// authentication related (401) or a generic API error
func (h HealthCheck) IsAuthenticated(ctx context.Context) (bool, error) {
userListReq, err := http.NewRequestWithContext(ctx, http.MethodGet, "/v1/user?limit=1", http.NoBody)
if err != nil {
return false, err
}
err = h.requestClient.SlideRequest(userListReq, nil)
if err != nil {
var slideError *SlideError
if errors.As(err, &slideError) {
for _, errCode := range slideError.Codes {
if errCode == APIErrorCode_ERR_MISSING_AUTHENTICATION ||
errCode == APIErrorCode_ERR_UNAUTHORIZED {
return false, slideError
}
}
} else {
return true, err
}
}
return true, nil
}
package goslide
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
)
type NetworkService struct {
baseEndpoint string
requestClient *requestClient
}
type Network struct {
BridgeDeviceID string `json:"bridge_device_id"`
ClientID string `json:"client_id"`
Comments string `json:"comments"`
ConnectedVirtIDs []string `json:"connected_virt_ids"`
DHCP bool `json:"dhcp"`
DHCPRangeEnd string `json:"dhcp_range_end"`
DHCPRangeStart string `json:"dhcp_range_start"`
Internet bool `json:"internet"`
Name string `json:"name"`
Nameservers string `json:"nameservers"`
NetworkID string `json:"network_id"`
RouterPrefix string `json:"router_prefix"`
Type NetworkTypeDisaster `json:"type"`
}
type NetworkTypeDisaster string
const (
NetworkTypeDisaster_STANDARD NetworkTypeDisaster = "standard"
NetworkTypeDisaster_BRIDGE_LAN NetworkTypeDisaster = "bridge-lan"
)
// https://docs.slide.tech/api/#tag/networks/GET/v1/network
func (n NetworkService) List(
ctx context.Context,
pageHandler func(response ListResponse[Network]) error,
) error {
return n.ListWithQueryParameters(ctx, pageHandler)
}
// https://docs.slide.tech/api/#tag/networks/GET/v1/network
func (n NetworkService) ListWithQueryParameters(
ctx context.Context,
pageHandler func(response ListResponse[Network]) error,
options ...paginationQueryParam,
) error {
queryParams := url.Values{}
for _, option := range options {
option(queryParams)
}
for {
target := ListResponse[Network]{}
endpoint := n.baseEndpoint
if len(queryParams) > 0 {
endpoint = endpoint + "?"
}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
fmt.Sprintf("%s%s", endpoint, queryParams.Encode()),
http.NoBody,
)
if err != nil {
return err
}
if err := n.requestClient.SlideRequest(request, &target); err != nil {
return err
}
if err := pageHandler(target); err != nil {
return err
}
// No next offset marks the end of the paginated results
if target.Pagination.NextOffset == nil {
break
}
queryParams.Set(
"offset",
strconv.FormatUint(
uint64(*target.Pagination.NextOffset),
10,
),
)
}
return nil
}
type NetworkCreatePayload struct {
Name string `json:"name"`
Type NetworkTypeDisaster `json:"type"`
BridgeDeviceID string `json:"bridge_device_id,omitempty"`
ClientID string `json:"client_id,omitempty"`
Comments string `json:"comments,omitempty"`
DHCP bool `json:"dhcp,omitempty"`
DHCPRangeEnd string `json:"dhcp_range_end,omitempty"`
DHCPRangeStart string `json:"dhcp_range_start,omitempty"`
Internet bool `json:"internet,omitempty"`
Nameservers string `json:"nameservers,omitempty"`
RouterPrefix string `json:"router_prefix,omitempty"`
}
type NetworkUpdatePayload struct {
Name string `json:"name,omitempty"`
Type NetworkTypeDisaster `json:"type,omitempty"`
Comments string `json:"comments,omitempty"`
DHCP bool `json:"dhcp,omitempty"`
DHCPRangeEnd string `json:"dhcp_range_end,omitempty"`
DHCPRangeStart string `json:"dhcp_range_start,omitempty"`
Internet bool `json:"internet,omitempty"`
Nameservers string `json:"nameservers,omitempty"`
RouterPrefix string `json:"router_prefix,omitempty"`
}
// https://docs.slide.tech/api/#tag/networks/POST/v1/network
func (n NetworkService) Create(ctx context.Context, payload NetworkCreatePayload) (Network, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return Network{}, err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
n.baseEndpoint,
requestBody,
)
if err != nil {
return Network{}, err
}
target := Network{}
if err := n.requestClient.SlideRequest(request, &target); err != nil {
return Network{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/networks/GET/v1/network/{network_id}
func (n NetworkService) Get(ctx context.Context, networkID string) (Network, error) {
target := Network{}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
n.baseEndpoint+"/"+networkID,
http.NoBody,
)
if err != nil {
return Network{}, err
}
if err := n.requestClient.SlideRequest(request, &target); err != nil {
return Network{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/networks/DELETE/v1/network/{network_id}
func (n NetworkService) Delete(ctx context.Context, networkID string) error {
request, err := http.NewRequestWithContext(
ctx,
http.MethodDelete,
n.baseEndpoint+"/"+networkID,
http.NoBody,
)
if err != nil {
return err
}
return n.requestClient.SlideRequest(request, nil)
}
// https://docs.slide.tech/api/#tag/networks/PATCH/v1/network/{network_id}
func (n NetworkService) Update(ctx context.Context, networkID string, payload NetworkUpdatePayload) (Network, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return Network{}, err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPatch,
n.baseEndpoint+"/"+networkID,
requestBody,
)
if err != nil {
return Network{}, err
}
target := Network{}
if err := n.requestClient.SlideRequest(request, &target); err != nil {
return Network{}, err
}
return target, nil
}
type NetworkPortForwardPayload struct {
Dest string `json:"dest"`
NetworkID string `json:"network_id"`
Proto NetworkProto `json:"proto"`
}
type NetworkProto string
const (
NetworkProto_UDP NetworkProto = "udp"
NetworkProto_TCP NetworkProto = "tcp"
)
// https://docs.slide.tech/api/#tag/networks/POST/v1/network/{network_id}/port-forwards
func (n NetworkService) CreatePortForward(ctx context.Context, networkID string, payload NetworkPortForwardPayload) (NetworkPortForward, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return NetworkPortForward{}, err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
n.baseEndpoint+"/"+networkID+"/port-forwards",
requestBody,
)
if err != nil {
return NetworkPortForward{}, err
}
target := NetworkPortForward{}
if err := n.requestClient.SlideRequest(request, &target); err != nil {
return NetworkPortForward{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/networks/DELETE/v1/network/{network_id}/port-forwards
func (n NetworkService) DeletePortForward(ctx context.Context, networkID string, payload NetworkPortForwardPayload) error {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
n.baseEndpoint+"/"+networkID+"/port-forwards",
requestBody,
)
if err != nil {
return err
}
if err := n.requestClient.SlideRequest(request, nil); err != nil {
return err
}
return nil
}
// https://docs.slide.tech/api/#tag/networks/PATCH/v1/network/{network_id}/port-forwards
func (n NetworkService) UpdatePortForward(ctx context.Context, networkID string, payload NetworkPortForwardUpdatePayload) error {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
n.baseEndpoint+"/"+networkID+"/port-forwards",
requestBody,
)
if err != nil {
return err
}
if err := n.requestClient.SlideRequest(request, nil); err != nil {
return err
}
return nil
}
type NetworkPortForwardUpdatePayload struct {
Dest string `json:"dest"`
NetworkID string `json:"network_id"`
Port uint `json:"port"`
ProtoNew NetworkProto `json:"proto_new"`
ProtoOld NetworkProto `json:"proto_old"`
}
type NetworkPortForward struct {
Dest string `json:"dest"`
NetworkID string `json:"network_id"`
Port uint `json:"port"`
Proto NetworkProto `json:"proto"`
}
type NetworkWGPeer struct {
NetworkID string `json:"network_id"`
PeerName string `json:"peer_name"`
RemoteNetworks []string `json:"remote_networks"`
WGAddress string `json:"wg_address"`
WGPrivateKey string `json:"wg_private_key"`
WGPublicKey string `json:"wg_public_key"`
}
type NetworkWGPeerCreatePayload struct {
NetworkID string `json:"network_id"`
PeerName string `json:"peer_name"`
RemoteNetworks []string `json:"remote_networks"`
}
// https://docs.slide.tech/api/#tag/networks/POST/v1/network/{network_id}/wg-peers
func (n NetworkService) CreateWGPeer(ctx context.Context, networkID string, payload NetworkWGPeerCreatePayload) (NetworkWGPeer, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return NetworkWGPeer{}, err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
n.baseEndpoint+"/"+networkID+"/wg-peers",
requestBody,
)
if err != nil {
return NetworkWGPeer{}, err
}
target := NetworkWGPeer{}
if err := n.requestClient.SlideRequest(request, &target); err != nil {
return NetworkWGPeer{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/networks/DELETE/v1/network/{network_id}/wg-peers
func (n NetworkService) DeleteWGPeer(ctx context.Context, networkID, wgAddress string) error {
type networkWGPeerDeletePayload struct {
NetworkID string `json:"network_id"`
WGAddress string `json:"wg_address"`
}
payload := networkWGPeerDeletePayload{
NetworkID: networkID,
WGAddress: wgAddress,
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
n.baseEndpoint+"/"+networkID+"/wg-peers",
requestBody,
)
if err != nil {
return err
}
if err := n.requestClient.SlideRequest(request, nil); err != nil {
return err
}
return nil
}
// https://docs.slide.tech/api/#tag/networks/PATCH/v1/network/{network_id}/wg-peers
func (n NetworkService) UpdateWGPeer(ctx context.Context, networkID string, payload NetworkPortForwardUpdatePayload) (NetworkWGPeer, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return NetworkWGPeer{}, err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
n.baseEndpoint+"/"+networkID+"/wg-peers",
requestBody,
)
if err != nil {
return NetworkWGPeer{}, err
}
target := NetworkWGPeer{}
if err := n.requestClient.SlideRequest(request, &target); err != nil {
return NetworkWGPeer{}, err
}
return target, nil
}
package goslide
import (
"net/url"
"strconv"
)
type OffsetPagination struct {
Total uint `json:"total"`
NextOffset *uint `json:"next_offset"`
}
type ListResponse[Record any] struct {
Pagination OffsetPagination `json:"pagination"`
Data []Record `json:"data"`
}
type paginationQueryParam func(u url.Values)
func WithOffset(offset uint) paginationQueryParam {
return func(u url.Values) {
u.Set("offset", strconv.FormatUint(uint64(offset), 10))
}
}
func WithLimit(limit uint) paginationQueryParam {
return func(u url.Values) {
u.Set("limit", strconv.FormatUint(uint64(limit), 10))
}
}
func WithSortDirection(ascending bool) paginationQueryParam {
return func(u url.Values) {
u.Set("sort_asc", strconv.FormatBool(ascending))
}
}
func WithSortBy(field string) paginationQueryParam {
return func(u url.Values) {
u.Set("sort_by", url.QueryEscape(field))
}
}
func WithAgentID(agentID string) paginationQueryParam {
return func(u url.Values) {
u.Set("agent_id", url.QueryEscape(agentID))
}
}
func WithDeviceID(deviceID string) paginationQueryParam {
return func(u url.Values) {
u.Set("device_id", url.QueryEscape(deviceID))
}
}
func WithIncludeResolvedAlerts(b bool) paginationQueryParam {
return func(u url.Values) {
u.Set("resolved", strconv.FormatBool(b))
}
}
func WithSnapshotID(snapshotID string) paginationQueryParam {
return func(u url.Values) {
u.Set("snapshot_id", url.QueryEscape(snapshotID))
}
}
func WithPath(path string) paginationQueryParam {
return func(u url.Values) {
u.Set("path", url.QueryEscape(path))
}
}
func WithSnapshotLocationFilter(snapshotLocation SnapshotLocationFilter) paginationQueryParam {
return func(u url.Values) {
u.Set("snapshot_location", url.QueryEscape(string(snapshotLocation)))
}
}
type SnapshotLocationFilter string
const (
SnapshotLocationFilter_EXISTS_LOCAL SnapshotLocationFilter = "exists_local"
SnapshotLocationFilter_EXISTS_CLOUD SnapshotLocationFilter = "exists_cloud"
SnapshotLocationFilter_EXISTS_DELETED SnapshotLocationFilter = "exists_deleted"
SnapshotLocationFilter_EXISTS_DELETED_RETENTION SnapshotLocationFilter = "exists_deleted_retention"
SnapshotLocationFilter_EXISTS_DELETED_MANUAL SnapshotLocationFilter = "exists_deleted_manual"
SnapshotLocationFilter_EXISTS_DELETED_OTHER SnapshotLocationFilter = "exists_deleted_other"
)
package goslide
import (
"encoding/json"
"fmt"
"io"
"net/http"
"golang.org/x/oauth2"
)
type requestClient struct {
token *oauth2.Token
apiURL string
httpClient *http.Client
}
func (rc *requestClient) do(request *http.Request, target any) error {
if request.URL.Host == "" {
request.URL.Host = rc.apiURL
}
request.URL.Scheme = "https"
request.Header.Set("Accept", "application/json")
if request.Header.Get("Content-Type") == "" {
request.Header.Set("Content-Type", "application/json")
}
response, err := rc.httpClient.Do(request)
if err != nil {
return err
}
defer response.Body.Close()
if response.StatusCode >= http.StatusBadRequest {
slideAPIError := &SlideError{
HTTPStatusCode: response.StatusCode,
HTTPRequestPath: request.URL.Path,
HTTPRequestMethod: request.Method,
}
bodyBytes, err := io.ReadAll(response.Body)
if err != nil {
return err
}
if err := json.Unmarshal(bodyBytes, slideAPIError); err != nil {
return fmt.Errorf("goslide library error while unmarshalling Slide API response - %w. HTTP Status Code %d", err, response.StatusCode)
}
return slideAPIError
}
if target != nil {
bodyBytes, err := io.ReadAll(response.Body)
if err != nil {
return err
}
if err := json.Unmarshal(bodyBytes, target); err != nil {
return err
}
}
return nil
}
func (rc *requestClient) SlideRequest(request *http.Request, target any) error {
if request.Header.Get("Authorization") == "" {
if rc.token == nil {
return fmt.Errorf("unable to set authorization header - API Token not set")
}
rc.token.SetAuthHeader(request)
}
return rc.do(request, target)
}
func (rc *requestClient) Request(request *http.Request, target any) error {
return rc.do(request, target)
}
package goslide
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"time"
)
type FileRestore struct {
AgentID string `json:"agent_id"`
CreatedAt time.Time `json:"created_at"`
DeviceID string `json:"device_id"`
ExpiresAt time.Time `json:"expires_at"`
FileRestoreID string `json:"file_restore_id"`
SnapshotID string `json:"snapshot_id"`
}
type FileRestoreService struct {
baseEndpoint string
requestClient *requestClient
}
type FileRestorePayload struct {
DeviceID string `json:"device_id"`
SnapshotID string `json:"snapshot_id"`
}
// https://docs.slide.tech/api/#tag/restores-file/GET/v1/restore/file
func (f FileRestoreService) List(
ctx context.Context,
pageHandler func(response ListResponse[FileRestore]) error,
) error {
return f.ListWithQueryParameters(ctx, pageHandler)
}
// https://docs.slide.tech/api/#tag/restores-file/GET/v1/restore/file
func (f FileRestoreService) ListWithQueryParameters(
ctx context.Context,
pageHandler func(response ListResponse[FileRestore]) error,
options ...paginationQueryParam,
) error {
queryParams := url.Values{}
for _, option := range options {
option(queryParams)
}
for {
target := ListResponse[FileRestore]{}
endpoint := f.baseEndpoint
if len(queryParams) > 0 {
endpoint = endpoint + "?"
}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
fmt.Sprintf("%s%s", endpoint, queryParams.Encode()),
http.NoBody,
)
if err != nil {
return err
}
if err := f.requestClient.SlideRequest(request, &target); err != nil {
return err
}
if err := pageHandler(target); err != nil {
return err
}
// No next offset marks the end of the paginated results
if target.Pagination.NextOffset == nil {
break
}
queryParams.Set(
"offset",
strconv.FormatUint(
uint64(*target.Pagination.NextOffset),
10,
),
)
}
return nil
}
// https://docs.slide.tech/api/#tag/restores-file/POST/v1/restore/file
func (f FileRestoreService) Create(ctx context.Context, payload FileRestorePayload) (FileRestore, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return FileRestore{}, err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
f.baseEndpoint,
requestBody,
)
if err != nil {
return FileRestore{}, err
}
target := FileRestore{}
if err := f.requestClient.SlideRequest(request, &target); err != nil {
return FileRestore{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/restores-file/GET/v1/restore/file/{file_restore_id}
func (f FileRestoreService) Get(ctx context.Context, fileRestoreID string) (FileRestore, error) {
target := FileRestore{}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
f.baseEndpoint+"/"+fileRestoreID,
http.NoBody,
)
if err != nil {
return FileRestore{}, err
}
if err := f.requestClient.SlideRequest(request, &target); err != nil {
return FileRestore{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/restores-file/GET/v1/restore/file/{file_restore_id}/browse
func (f FileRestoreService) Browse(
ctx context.Context,
fileRestoreID string,
pageHandler func(response ListResponse[FileRestoreData]) error,
) error {
return f.BrowseWithQueryParameters(ctx, fileRestoreID, pageHandler)
}
// https://docs.slide.tech/api/#tag/restores-file/GET/v1/restore/file/{file_restore_id}/browse
func (f FileRestoreService) BrowseWithQueryParameters(
ctx context.Context,
fileRestoreID string,
pageHandler func(response ListResponse[FileRestoreData]) error,
options ...paginationQueryParam,
) error {
queryParams := url.Values{}
for _, option := range options {
option(queryParams)
}
for {
target := ListResponse[FileRestoreData]{}
endpoint := f.baseEndpoint + "/" + fileRestoreID + "/browse"
if len(queryParams) > 0 {
endpoint = endpoint + "?"
}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
fmt.Sprintf("%s%s", endpoint, queryParams.Encode()),
http.NoBody,
)
if err != nil {
return err
}
if err := f.requestClient.SlideRequest(request, &target); err != nil {
return err
}
if err := pageHandler(target); err != nil {
return err
}
// No next offset marks the end of the paginated results
if target.Pagination.NextOffset == nil {
break
}
queryParams.Set(
"offset",
strconv.FormatUint(
uint64(*target.Pagination.NextOffset),
10,
),
)
}
return nil
}
// https://docs.slide.tech/api/#tag/restores-file/DELETE/v1/restore/file/{file_restore_id}
func (f FileRestoreService) Delete(ctx context.Context, fileRestoreID string) error {
request, err := http.NewRequestWithContext(
ctx,
http.MethodDelete,
f.baseEndpoint+"/"+fileRestoreID,
http.NoBody,
)
if err != nil {
return err
}
return f.requestClient.SlideRequest(request, nil)
}
type FileRestoreData struct {
DownloadURIs []FileRestoreDownloadURI `json:"download_uris"`
ModifiedAt string `json:"modified_at"`
Name string `json:"name"`
Path string `json:"path"`
Size uint `json:"size"`
SymlinkTargetPath string `json:"symlink_target_path"`
Type FileRestoreDataType `json:"type"`
}
type FileRestoreDownloadURI struct {
Type FileRestoreDownloadType `json:"type"`
URI string `json:"uri"`
}
type FileRestoreDownloadType string
const (
FileRestoreDownloadType_LOCAL FileRestoreDownloadType = "local"
FileRestoreDownloadType_CLOUD FileRestoreDownloadType = "cloud"
)
type FileRestoreDataType string
const (
FileRestoreDataType_FILE FileRestoreDataType = "file"
FileRestoreDataType_DIR FileRestoreDataType = "dir"
FileRestoreDataType_SYMLINK FileRestoreDataType = "symlink"
)
package goslide
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"time"
)
type ImageExportRestore struct {
AgentID string `json:"agent_id"`
CreatedAt time.Time `json:"created_at"`
DeviceID string `json:"device_id"`
ImageExportID string `json:"image_export_id"`
ImageType ImageExportType `json:"image_type"`
SnapshotID string `json:"snapshot_id"`
}
type ImageExportRestoreService struct {
baseEndpoint string
requestClient *requestClient
}
type ImageExportType string
const (
ImageExportType_VHDX ImageExportType = "vhdx"
ImageExportType_VHDX_DYNAMIC ImageExportType = "vhdx-dynamic"
ImageExportType_VHD ImageExportType = "vhd"
ImageExportType_RAW ImageExportType = "raw"
)
type ImageExportRestoreData struct {
DiskID string `json:"disk_id"`
DownloadURIs []ImageExportRestoreDownloadURI `json:"download_uris"`
Name string `json:"name"`
Size uint `json:"size"`
}
type ImageExportRestoreDownloadURI struct {
Type ImageExportDownloadType `json:"type"`
URI string `json:"uri"`
}
type ImageExportDownloadType string
const (
ImageExportDownloadType_LOCAL ImageExportDownloadType = "local"
ImageExportDownloadType_CLOUD ImageExportDownloadType = "cloud"
)
type ImageExportRestorePayload struct {
DeviceID string `json:"device_id"`
SnapshotID string `json:"snapshot_id"`
ImageType ImageExportType `json:"image_type"`
BootMods []BootMod `json:"boot_mods,omitempty"`
}
// https://docs.slide.tech/api/#tag/restores-image/GET/v1/restore/image
func (i ImageExportRestoreService) List(
ctx context.Context,
pageHandler func(response ListResponse[ImageExportRestore]) error,
) error {
return i.ListWithQueryParameters(ctx, pageHandler)
}
// https://docs.slide.tech/api/#tag/restores-image/GET/v1/restore/image
func (i ImageExportRestoreService) ListWithQueryParameters(
ctx context.Context,
pageHandler func(response ListResponse[ImageExportRestore]) error,
options ...paginationQueryParam,
) error {
queryParams := url.Values{}
for _, option := range options {
option(queryParams)
}
for {
target := ListResponse[ImageExportRestore]{}
endpoint := i.baseEndpoint
if len(queryParams) > 0 {
endpoint = endpoint + "?"
}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
fmt.Sprintf("%s%s", endpoint, queryParams.Encode()),
http.NoBody,
)
if err != nil {
return err
}
if err := i.requestClient.SlideRequest(request, &target); err != nil {
return err
}
if err := pageHandler(target); err != nil {
return err
}
// No next offset marks the end of the paginated results
if target.Pagination.NextOffset == nil {
break
}
queryParams.Set(
"offset",
strconv.FormatUint(
uint64(*target.Pagination.NextOffset),
10,
),
)
}
return nil
}
// https://docs.slide.tech/api/#tag/restores-image/POST/v1/restore/image
func (i ImageExportRestoreService) Create(ctx context.Context, payload ImageExportRestorePayload) (ImageExportRestore, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return ImageExportRestore{}, err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
i.baseEndpoint,
requestBody,
)
if err != nil {
return ImageExportRestore{}, err
}
target := ImageExportRestore{}
if err := i.requestClient.SlideRequest(request, &target); err != nil {
return ImageExportRestore{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/restores-image/GET/v1/restore/image/{image_export_id}
func (i ImageExportRestoreService) Get(ctx context.Context, imageExportRestoreID string) (ImageExportRestore, error) {
target := ImageExportRestore{}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
i.baseEndpoint+"/"+imageExportRestoreID,
http.NoBody,
)
if err != nil {
return ImageExportRestore{}, err
}
if err := i.requestClient.SlideRequest(request, &target); err != nil {
return ImageExportRestore{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/restores-image/GET/v1/restore/image/{image_export_id}/browse
func (i ImageExportRestoreService) Browse(
ctx context.Context,
imageExportRestoreID string,
pageHandler func(response ListResponse[ImageExportRestoreData]) error,
) error {
return i.BrowseWithQueryParameters(ctx, imageExportRestoreID, pageHandler)
}
// https://docs.slide.tech/api/#tag/restores-image/GET/v1/restore/image/{image_export_id}/browse
func (i ImageExportRestoreService) BrowseWithQueryParameters(
ctx context.Context,
imageExportRestoreID string,
pageHandler func(response ListResponse[ImageExportRestoreData]) error,
options ...paginationQueryParam,
) error {
queryParams := url.Values{}
for _, option := range options {
option(queryParams)
}
for {
target := ListResponse[ImageExportRestoreData]{}
endpoint := i.baseEndpoint + "/" + imageExportRestoreID + "/browse"
if len(queryParams) > 0 {
endpoint = endpoint + "?"
}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
fmt.Sprintf("%s%s", endpoint, queryParams.Encode()),
http.NoBody,
)
if err != nil {
return err
}
if err := i.requestClient.SlideRequest(request, &target); err != nil {
return err
}
if err := pageHandler(target); err != nil {
return err
}
// No next offset marks the end of the paginated results
if target.Pagination.NextOffset == nil {
break
}
queryParams.Set(
"offset",
strconv.FormatUint(
uint64(*target.Pagination.NextOffset),
10,
),
)
}
return nil
}
// https://docs.slide.tech/api/#tag/restores-image/DELETE/v1/restore/image/{image_export_id}
func (i ImageExportRestoreService) Delete(ctx context.Context, imageExportRestoreID string) error {
request, err := http.NewRequestWithContext(
ctx,
http.MethodDelete,
i.baseEndpoint+"/"+imageExportRestoreID,
http.NoBody,
)
if err != nil {
return err
}
return i.requestClient.SlideRequest(request, nil)
}
package goslide
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"time"
)
type VirtualMachineRestore struct {
AgentID string `json:"agent_id"`
CPUCount uint `json:"cpu_count"`
CreatedAt time.Time `json:"created_at"`
DeviceID string `json:"device_id"`
DiskBus DiskBus `json:"disk_bus"`
ExpiresAt time.Time `json:"expires_at"`
MemoryInMB uint `json:"memory_in_mb"`
NetworkModel VirtualMachineNetworkModel `json:"network_model"`
NetworkType VirtualMachineNetworkType `json:"network_type"`
SnapshotID string `json:"snapshot_id"`
State VirtualMachineState `json:"state"`
VirtID string `json:"virt_id"`
VNC []VirtualMachineVNC `json:"vnc"`
VNCPassword string `json:"vnc_password"`
}
type VirtualMachineRestoreService struct {
baseEndpoint string
requestClient *requestClient
}
type VirtualMachineVNC struct {
Host string `json:"host"`
Port uint `json:"port"`
Type VirtualMachineVNCType `json:"type"`
WebsocketURI string `json:"websocket_uri"`
}
// https://docs.slide.tech/api/#tag/restores-virtual-machine/GET/v1/restore/virt
func (v VirtualMachineRestoreService) List(
ctx context.Context,
pageHandler func(response ListResponse[VirtualMachineRestore]) error,
) error {
return v.ListWithQueryParameters(ctx, pageHandler)
}
// https://docs.slide.tech/api/#tag/restores-virtual-machine/GET/v1/restore/virt
func (v VirtualMachineRestoreService) ListWithQueryParameters(
ctx context.Context,
pageHandler func(response ListResponse[VirtualMachineRestore]) error,
options ...paginationQueryParam,
) error {
queryParams := url.Values{}
for _, option := range options {
option(queryParams)
}
for {
target := ListResponse[VirtualMachineRestore]{}
endpoint := v.baseEndpoint
if len(queryParams) > 0 {
endpoint = endpoint + "?"
}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
fmt.Sprintf("%s%s", endpoint, queryParams.Encode()),
http.NoBody,
)
if err != nil {
return err
}
if err := v.requestClient.SlideRequest(request, &target); err != nil {
return err
}
if err := pageHandler(target); err != nil {
return err
}
// No next offset marks the end of the paginated results
if target.Pagination.NextOffset == nil {
break
}
queryParams.Set(
"offset",
strconv.FormatUint(
uint64(*target.Pagination.NextOffset),
10,
),
)
}
return nil
}
// https://docs.slide.tech/api/#tag/restores-virtual-machine/POST/v1/restore/virt
func (v VirtualMachineRestoreService) Create(ctx context.Context, payload VirtualMachineRestoreCreatePayload) (VirtualMachineRestore, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return VirtualMachineRestore{}, err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
v.baseEndpoint,
requestBody,
)
if err != nil {
return VirtualMachineRestore{}, err
}
target := VirtualMachineRestore{}
if err := v.requestClient.SlideRequest(request, &target); err != nil {
return VirtualMachineRestore{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/restores-virtual-machine/GET/v1/restore/virt/{virt_id}
func (v VirtualMachineRestoreService) Get(ctx context.Context, virtID string) (VirtualMachineRestore, error) {
target := VirtualMachineRestore{}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
v.baseEndpoint+"/"+virtID,
http.NoBody,
)
if err != nil {
return VirtualMachineRestore{}, err
}
if err := v.requestClient.SlideRequest(request, &target); err != nil {
return VirtualMachineRestore{}, err
}
return target, nil
}
// https://docs.slide.tech/api/#tag/restores-virtual-machine/DELETE/v1/restore/virt/{virt_id}
func (v VirtualMachineRestoreService) Delete(ctx context.Context, virtID string) error {
request, err := http.NewRequestWithContext(
ctx,
http.MethodDelete,
v.baseEndpoint+"/"+virtID,
http.NoBody,
)
if err != nil {
return err
}
return v.requestClient.SlideRequest(request, nil)
}
// https://docs.slide.tech/api/#tag/restores-virtual-machine/PATCH/v1/restore/virt/{virt_id}
func (v VirtualMachineRestoreService) Update(ctx context.Context, virtID string, payload VirtualMachineRestoreUpdatePayload) (VirtualMachineRestore, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return VirtualMachineRestore{}, err
}
requestBody := bytes.NewReader(payloadBytes)
request, err := http.NewRequestWithContext(
ctx,
http.MethodPatch,
v.baseEndpoint+"/"+virtID,
requestBody,
)
if err != nil {
return VirtualMachineRestore{}, err
}
target := VirtualMachineRestore{}
if err := v.requestClient.SlideRequest(request, &target); err != nil {
return VirtualMachineRestore{}, err
}
return target, nil
}
type VirtualMachineRestoreUpdatePayload struct {
State VirtualMachineState `json:"state,omitempty"`
CPUCount uint `json:"cpu_count,omitempty"`
MemoryInMB uint `json:"memory_in_mb,omitempty"`
ExpiresAt time.Time `json:"expires_at,omitempty"`
}
type VirtualMachineRestoreCreatePayload struct {
DeviceID string `json:"device_id"`
SnapshotID string `json:"snapshot_id"`
BootMods []BootMod `json:"boot_mods,omitempty"`
CPUCount uint `json:"cpu_count,omitempty"`
DiskBus DiskBus `json:"disk_bus,omitempty"`
MemoryInMB uint `json:"memory_in_mb,omitempty"`
NetworkModel VirtualMachineNetworkModel `json:"network_model,omitempty"`
NetworkType VirtualMachineNetworkType `json:"network_type,omitempty"`
}
type BootMod string
const (
BootMod_PASSWORDLESS_ADMIN_USER BootMod = "passwordless_admin_user"
)
type DiskBus string
const (
DiskBus_SATA DiskBus = "sata"
DiskBus_VIRTIO DiskBus = "virtio"
)
type VirtualMachineNetworkModel string
const (
VirtualMachineNetworkModel_HYPERVISOR_DEFAULT VirtualMachineNetworkModel = "hypervisor_default"
VirtualMachineNetworkModel_E1000 VirtualMachineNetworkModel = "e1000"
VirtualMachineNetworkModel_RTL8139 VirtualMachineNetworkModel = "rtl8139"
)
type VirtualMachineNetworkType string
const (
VirtualMachineNetworkType_NETWORK VirtualMachineNetworkType = "network"
VirtualMachineNetworkType_NETWORK_ISOLATED VirtualMachineNetworkType = "network-isolated"
VirtualMachineNetworkType_BRIDGE VirtualMachineNetworkType = "bridge"
)
type VirtualMachineState string
const (
VirtualMachineState_RUNNING VirtualMachineState = "running"
VirtualMachineState_STOPPED VirtualMachineState = "stopped"
VirtualMachineState_PAUSED VirtualMachineState = "paused"
)
type VirtualMachineVNCType string
const (
VirtualMachineVNCType_LOCAL VirtualMachineVNCType = "local"
VirtualMachineVNCType_CLOUD VirtualMachineVNCType = "cloud"
)
package goslide
import (
"context"
"net/http"
"golang.org/x/oauth2"
)
type serviceConfig struct {
roundtripper http.RoundTripper
apiURL string
}
type configOption func(s *serviceConfig)
func WithCustomRoundtripper(roundtripper http.RoundTripper) configOption {
return func(s *serviceConfig) {
s.roundtripper = roundtripper
}
}
func AddRequestPreProcessor(roundtripper http.RoundTripper) configOption {
return func(s *serviceConfig) {
s.roundtripper = roundtripper
}
}
func WithSlogger(roundtripper http.RoundTripper) configOption {
return func(s *serviceConfig) {
s.roundtripper = roundtripper
}
}
type Service struct {
accounts AccountService
agents AgentService
alerts AlertService
backups BackupService
clients ClientService
devices DeviceService
fileRestores FileRestoreService
health HealthCheck
imageExportRestores ImageExportRestoreService
networks NetworkService
snapshots SnapshotService
users UserService
virtualMachineRestores VirtualMachineRestoreService
}
func NewService(
apiToken string,
options ...configOption,
) Service {
config := &serviceConfig{
apiURL: "api.slide.tech",
}
for _, option := range options {
option(config)
}
oauthToken := &oauth2.Token{
AccessToken: apiToken,
}
requestClient := &requestClient{
token: oauthToken,
apiURL: config.apiURL,
httpClient: &http.Client{
Transport: config.roundtripper,
},
}
return Service{
accounts: AccountService{
baseEndpoint: "/v1/account",
requestClient: requestClient,
},
agents: AgentService{
baseEndpoint: "/v1/agent",
requestClient: requestClient,
},
alerts: AlertService{
baseEndpoint: "/v1/alert",
requestClient: requestClient,
},
backups: BackupService{
baseEndpoint: "/v1/backup",
requestClient: requestClient,
},
clients: ClientService{
baseEndpoint: "/v1/client",
requestClient: requestClient,
},
devices: DeviceService{
baseEndpoint: "/v1/device",
requestClient: requestClient,
},
fileRestores: FileRestoreService{
baseEndpoint: "/v1/restore/file",
requestClient: requestClient,
},
health: HealthCheck{
requestClient: requestClient,
},
imageExportRestores: ImageExportRestoreService{
baseEndpoint: "/v1/restore/image",
requestClient: requestClient,
},
networks: NetworkService{
baseEndpoint: "/v1/network",
requestClient: requestClient,
},
snapshots: SnapshotService{
baseEndpoint: "/v1/snapshot",
requestClient: requestClient,
},
users: UserService{
baseEndpoint: "/v1/user",
requestClient: requestClient,
},
virtualMachineRestores: VirtualMachineRestoreService{
baseEndpoint: "/v1/restore/virt",
requestClient: requestClient,
},
}
}
// https://docs.slide.tech/api/#tag/accounts
func (s Service) Accounts() AccountService {
return s.accounts
}
// https://docs.slide.tech/api/#tag/agents
func (s Service) Agents() AgentService {
return s.agents
}
// https://docs.slide.tech/api/#tag/alerts
func (s Service) Alerts() AlertService {
return s.alerts
}
// https://docs.slide.tech/api/#tag/backups
func (s Service) Backups() BackupService {
return s.backups
}
// https://docs.slide.tech/api/#tag/clients
func (s Service) Clients() ClientService {
return s.clients
}
// https://docs.slide.tech/api/#tag/devices
func (s Service) Devices() DeviceService {
return s.devices
}
// https://docs.slide.tech/api/#tag/restores-file
func (s Service) FileRestores() FileRestoreService {
return s.fileRestores
}
// https://docs.slide.tech/api/#tag/restores-image
func (s Service) ImageExportRestores() ImageExportRestoreService {
return s.imageExportRestores
}
// https://docs.slide.tech/api/#tag/networks
func (s Service) Networks() NetworkService {
return s.networks
}
// https://docs.slide.tech/api/#tag/snapshots
func (s Service) Snapshots() SnapshotService {
return s.snapshots
}
// https://docs.slide.tech/api/#tag/users
func (s Service) Users() UserService {
return s.users
}
// https://docs.slide.tech/api/#tag/restores-virtual-machine
func (s Service) VirtualMachineRestores() VirtualMachineRestoreService {
return s.virtualMachineRestores
}
func (s Service) CheckAuthenticationToken(ctx context.Context) (bool, error) {
return s.health.IsAuthenticated(ctx)
}
package goslide
import (
"context"
"fmt"
"net/http"
"net/url"
"strconv"
"time"
)
type Snapshot struct {
AgentID string `json:"agent_id"`
BackupEndedAt time.Time `json:"backup_ended_at"`
BackupStartedAt time.Time `json:"backup_started_at"`
Locations []SnapshotLocation `json:"locations"`
Deleted *time.Time `json:"deleted"`
Deletions []SnapshotDeletion `json:"deletions"`
SnapshotID string `json:"snapshot_id"`
VerifyBootScreenshotURL string `json:"verify_boot_screenshot_url"`
VerifyBootStatus SnapshotBootStatus `json:"verify_boot_status"`
VerifyFSStatus SnapshotFSStatus `json:"verify_fs_status"`
}
type SnapshotService struct {
baseEndpoint string
requestClient *requestClient
}
type SnapshotLocation struct {
DeviceID string `json:"device_id"`
Type SnapshotLocationType `json:"type"`
}
type SnapshotDeletion struct {
Deleted time.Time `json:"deleted"`
DeletedBy string `json:"deleted_by"`
FirstAndLastName string `json:"first_and_last_name"`
Type SnapshotLocationType `json:"type"`
}
type SnapshotLocationType string
const (
SnapshotLocationType_LOCAL SnapshotLocationType = "local"
SnapshotLocationType_CLOUD SnapshotLocationType = "cloud"
)
type SnapshotFSStatus string
const (
SnapshotFSStatus_SUCCESS SnapshotFSStatus = "success"
SnapshotFSStatus_WARNING SnapshotFSStatus = "warning"
SnapshotFSStatus_ERROR SnapshotFSStatus = "error"
SnapshotFSStatus_SKIPPED SnapshotFSStatus = "skipped"
)
type SnapshotBootStatus string
const (
SnapshotBootStatus_SUCCESS SnapshotBootStatus = "success"
SnapshotBootStatus_WARNING SnapshotBootStatus = "warning"
SnapshotBootStatus_ERROR SnapshotBootStatus = "error"
SnapshotBootStatus_SKIPPED SnapshotBootStatus = "skipped"
SnapshotBootStatus_PENDING SnapshotBootStatus = "pending"
SnapshotBootStatus_PENDING_DUE_TO_DISASTER_VM SnapshotBootStatus = "pending_due_to_disaster_vm"
)
// https://docs.slide.tech/api/#tag/snapshots/GET/v1/snapshot
func (s SnapshotService) List(
ctx context.Context,
pageHandler func(response ListResponse[Snapshot]) error,
) error {
return s.ListWithQueryParameters(ctx, pageHandler)
}
// https://docs.slide.tech/api/#tag/snapshots/GET/v1/snapshot
func (s SnapshotService) ListWithQueryParameters(
ctx context.Context,
pageHandler func(response ListResponse[Snapshot]) error,
options ...paginationQueryParam,
) error {
queryParams := url.Values{}
for _, option := range options {
option(queryParams)
}
for {
target := ListResponse[Snapshot]{}
endpoint := s.baseEndpoint
if len(queryParams) > 0 {
endpoint = endpoint + "?"
}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
fmt.Sprintf("%s%s", endpoint, queryParams.Encode()),
http.NoBody,
)
if err != nil {
return err
}
if err := s.requestClient.SlideRequest(request, &target); err != nil {
return err
}
if err := pageHandler(target); err != nil {
return err
}
// No next offset marks the end of the paginated results
if target.Pagination.NextOffset == nil {
break
}
queryParams.Set(
"offset",
strconv.FormatUint(
uint64(*target.Pagination.NextOffset),
10,
),
)
}
return nil
}
// https://docs.slide.tech/api/#tag/snapshots/GET/v1/snapshot/{snapshot_id}
func (s SnapshotService) Get(ctx context.Context, snapshotID string) (Snapshot, error) {
target := Snapshot{}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
s.baseEndpoint+"/"+snapshotID,
http.NoBody,
)
if err != nil {
return Snapshot{}, err
}
if err := s.requestClient.SlideRequest(request, &target); err != nil {
return Snapshot{}, err
}
return target, nil
}
package goslide
import (
"context"
"fmt"
"net/http"
"net/url"
"strconv"
)
type User struct {
DisplayName string `json:"display_name"`
Email string `json:"email"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
RoleID UserRole `json:"role_id"`
UserID string `json:"user_id"`
}
type UserRole string
const (
UserRole_AccountOwner UserRole = "r_account_owner"
UserRole_AccountAdmin UserRole = "r_account_admin"
UserRole_AccountTech UserRole = "r_account_tech"
UserRole_AccountReadOnly UserRole = "r_readonly"
)
type UserService struct {
baseEndpoint string
requestClient *requestClient
}
// https://docs.slide.tech/api/#tag/users/GET/v1/user
func (u UserService) List(
ctx context.Context,
pageHandler func(response ListResponse[User]) error,
) error {
return u.ListWithQueryParameters(ctx, pageHandler)
}
// https://docs.slide.tech/api/#tag/users/GET/v1/user
func (u UserService) ListWithQueryParameters(
ctx context.Context,
pageHandler func(response ListResponse[User]) error,
options ...paginationQueryParam,
) error {
queryParams := url.Values{}
for _, option := range options {
option(queryParams)
}
for {
target := ListResponse[User]{}
endpoint := u.baseEndpoint
if len(queryParams) > 0 {
endpoint = endpoint + "?"
}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
fmt.Sprintf("%s%s", endpoint, queryParams.Encode()),
http.NoBody,
)
if err != nil {
return err
}
if err := u.requestClient.SlideRequest(request, &target); err != nil {
return err
}
if err := pageHandler(target); err != nil {
return err
}
// No next offset marks the end of the paginated results
if target.Pagination.NextOffset == nil {
break
}
queryParams.Set(
"offset",
strconv.FormatUint(
uint64(*target.Pagination.NextOffset),
10,
),
)
}
return nil
}
// https://docs.slide.tech/api/#tag/users/GET/v1/user/{user_id}
func (u UserService) Get(ctx context.Context, userID string) (User, error) {
target := User{}
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
"/v1/user/"+userID,
http.NoBody,
)
if err != nil {
return User{}, err
}
if err := u.requestClient.SlideRequest(request, &target); err != nil {
return User{}, err
}
return target, nil
}