tesedi

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 6, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

README

tesedi

Go

Go client library for the Tesedi partner API.

Installation

go get github.com/enthus-golang/tesedi

Usage

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/enthus-golang/tesedi"
)

func main() {
	client := tesedi.New(
		"https://example.tesedi.com/api",
		"https://example.tesedi.com/auth",
		"YOUR_API_KEY",
	)

	contract, err := client.GetContractByNumber(context.Background(), "FAKE-CONTRACT-0001")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("contract %s ends %s\n", contract.ContractNumber, contract.EndDate)

	assets, err := client.GetContractAssets(context.Background(), contract.ContractID, nil)
	if err != nil {
		log.Fatal(err)
	}

	for _, a := range assets.Data {
		fmt.Printf("  %s %s — %s\n", a.ProductSKU, a.SerialNumber, a.ServiceGroup)
	}
}

Options

client := tesedi.New(baseURL, authURL, apiKey,
	tesedi.WithHTTPClient(myHTTPClient),
	tesedi.WithRateLimit(60, 3600),    // requests per minute, per hour (0 = unlimited)
	tesedi.WithRetry(3, 500*time.Millisecond), // max attempts, base backoff
)

License

Apache License 2.0

Documentation

Overview

Package tesedi is a Go client library for the Tesedi partner API.

The API is authenticated via a static API key that is exchanged for a short-lived bearer token at the auth endpoint. The client caches the token internally and refreshes it proactively before expiry.

All public methods accept a context.Context for cancellation and timeout control. Non-2xx responses surface as typed errors that wrap the upstream status code and body.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrContractNotFound is returned by GetContractByNumber when the search
	// yields no exact match for the requested contract number.
	ErrContractNotFound = errors.New("tesedi: contract not found")

	// ErrAmbiguousContractNumber is returned by GetContractByNumber when the
	// search yields more than one exact match. This indicates upstream data
	// inconsistency.
	ErrAmbiguousContractNumber = errors.New("tesedi: ambiguous contract number")

	// ErrUnauthorized matches HTTP 401/403 responses via errors.Is.
	ErrUnauthorized = errors.New("tesedi: unauthorized")

	// ErrNotFound matches HTTP 404 responses via errors.Is.
	ErrNotFound = errors.New("tesedi: not found")
)

Sentinel errors. Match with errors.Is.

Functions

This section is empty.

Types

type APIError

type APIError struct {
	StatusCode int
	Body       string
}

APIError carries the HTTP status code and raw response body for an unsuccessful API call.

func (*APIError) Error

func (e *APIError) Error() string

func (*APIError) Is

func (e *APIError) Is(target error) bool

Is allows callers to match ErrUnauthorized / ErrNotFound via errors.Is.

type Asset

type Asset struct {
	AssetName       string         `json:"assetName"`
	ProductSKU      string         `json:"productSku"`
	SerialNumber    string         `json:"serialNumber"`
	ServiceGroupSKU string         `json:"serviceGroupSku"`
	ServiceGroup    string         `json:"serviceGroup"`
	ServiceLevels   []ServiceLevel `json:"serviceLevels"`
	RetailPrice     *float64       `json:"retailPrice"`
	StartDate       Date           `json:"startDate"`
	EndDate         Date           `json:"endDate"`
}

Asset represents an asset covered by a contract.

type AssetPage

type AssetPage struct {
	Data []Asset  `json:"data"`
	Meta PageMeta `json:"meta"`
}

AssetPage is a single page of assets.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a Tesedi API client. It is safe for concurrent use by multiple goroutines.

func New

func New(baseURL, authURL, apiKey string, opts ...Option) *Client

New creates a Tesedi API client.

baseURL is the API root, e.g. "https://example.tesedi.com/api". authURL is the token-exchange endpoint, e.g. "https://example.tesedi.com/auth". apiKey is the static key sent on the auth request as the "apiKey" header.

All three arguments are required; this constructor does not validate them — instead, the first call needing them surfaces any errors.

func (*Client) GetContract

func (c *Client) GetContract(ctx context.Context, contractID string) (*Contract, error)

GetContract fetches a contract by its contractId.

The upstream API does not populate the Vendor field on this response — it is only present on list/search responses. Use GetContractByNumber or SearchContracts when Vendor is required.

func (*Client) GetContractAssets

func (c *Client) GetContractAssets(ctx context.Context, contractID string, opts *ListOptions) (*AssetPage, error)

GetContractAssets returns a page of assets covered by the given contract. Pagination is cursor-based; pass ListOptions.Cursor to resume.

func (*Client) GetContractByNumber

func (c *Client) GetContractByNumber(ctx context.Context, number string) (*Contract, error)

GetContractByNumber resolves a contract by its contractNumber.

The upstream API does not support exact lookup by contractNumber, so this method issues a search and validates that exactly one returned row has a contractNumber equal to the requested value. Returns ErrContractNotFound if no exact match exists, or ErrAmbiguousContractNumber if more than one row matches.

func (*Client) SearchContracts

func (c *Client) SearchContracts(ctx context.Context, query string, opts *ListOptions) (*ContractPage, error)

SearchContracts returns a page of contracts matching the query. An empty query lists all contracts visible to the partner. Pagination is cursor-based; pass ListOptions.Cursor to resume.

Note: the Tesedi /contracts endpoint silently ignores unknown query params, so SearchContracts only sets parameters known to be honored upstream.

type Contract

type Contract struct {
	ContractID      string  `json:"contractId"`
	ContractNumber  string  `json:"contractNumber"`
	Status          string  `json:"status"`
	GroupID         string  `json:"groupId"`
	SAR             string  `json:"sar"`
	Vendor          string  `json:"vendor,omitempty"`
	Distributor     string  `json:"distributor"`
	Reseller        string  `json:"reseller"`
	EndCustomerName string  `json:"endCustomerName"`
	EndCustomerID   string  `json:"endCustomerId"`
	StartDate       Date    `json:"startDate"`
	EndDate         Date    `json:"endDate"`
	ResellerPrice   float64 `json:"resellerPrice"`
	RetailPrice     float64 `json:"retailPrice"`
	Currency        string  `json:"currency"`
}

Contract represents a Tesedi service contract.

The Vendor field is populated by list/search responses but omitted by GetContract — that is an upstream API asymmetry, not a bug in this client.

type ContractPage

type ContractPage struct {
	Data []Contract `json:"data"`
	Meta PageMeta   `json:"meta"`
}

ContractPage is a single page of contracts.

type Date added in v0.2.0

type Date struct {
	time.Time
}

Date is a calendar date as it appears on Tesedi contracts and assets. It wraps time.Time and parses Tesedi's "YYYY-MM-DD" wire format.

JSON methods are defined directly on Date because the embedded time.Time's MarshalJSON/UnmarshalJSON expect RFC3339 — leaving them promoted would silently break round-tripping with Tesedi's date-only format.

func (Date) MarshalJSON added in v0.2.0

func (d Date) MarshalJSON() ([]byte, error)

MarshalJSON emits Tesedi's "YYYY-MM-DD" wire format. A zero Date emits the empty string so it round-trips cleanly with UnmarshalJSON.

func (*Date) UnmarshalJSON added in v0.2.0

func (d *Date) UnmarshalJSON(data []byte) error

UnmarshalJSON parses Tesedi's "YYYY-MM-DD" wire format. Empty string and JSON null both decode to a zero Date.

type ListOptions

type ListOptions struct {
	Limit     int    // 0 = upstream default
	Cursor    string // resume from this cursor
	SortBy    string // server-defined fields
	SortOrder string // "asc" | "desc"
}

ListOptions configures pagination and sorting for list endpoints. The zero value is valid and yields the upstream defaults.

type Logger

type Logger interface {
	Printf(format string, args ...any)
}

Logger is the minimal logging interface accepted by WithLogger.

type Option

type Option func(*Client)

Option configures a Client.

func WithHTTPClient

func WithHTTPClient(httpClient *http.Client) Option

WithHTTPClient sets a custom HTTP client. The default is a new client with a 30s timeout. nil is ignored.

func WithLogger

func WithLogger(logger Logger) Option

WithLogger sets a logger for transient retry events. nil disables logging.

func WithRateLimit

func WithRateLimit(perMinute, perHour int) Option

WithRateLimit caps requests per minute and per hour. A non-positive value disables that limit. Both limits are evaluated; a request blocks (respecting context) when either bucket is empty.

func WithRetry

func WithRetry(maxAttempts int, baseBackoff time.Duration) Option

WithRetry configures retry behavior for transient failures (network errors, HTTP 5xx, HTTP 429). maxAttempts is the total number of attempts including the first; pass 1 to disable retries. baseBackoff is the initial delay between attempts; subsequent delays double up to a 30s cap with ±25% jitter. HTTP 429 honors the Retry-After header when present.

type PageMeta

type PageMeta struct {
	HasNextPage bool   `json:"hasNextPage"`
	NextCursor  string `json:"nextCursor"`
}

PageMeta carries cursor-based pagination metadata.

type ServiceLevel

type ServiceLevel struct {
	ServiceLevelSKU string  `json:"serviceLevelSku"`
	ServiceLevel    string  `json:"serviceLevel"`
	RetailPrice     float64 `json:"retailPrice"`
}

ServiceLevel describes one component of an asset's service group.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL