Documentation
¶
Overview ¶
Package shopware provides a standalone HTTP client and Data Abstraction Layer (DAL) helpers for talking to the Shopware Admin API.
The package is self-contained: it depends only on the standard library and golang.org/x/sync/singleflight, so it can be vendored or extracted into its own module without dragging application-specific code along. Anything application-specific (custom HTTP transports, extra request headers, SSRF protection) is injected via Config rather than imported.
The high-level entry points are:
- Client — authenticated HTTP client with OAuth token caching.
- Criteria — fluent builder for DAL search/aggregation payloads.
- EntityRepository — typed CRUD/search over a single entity.
Index ¶
- Constants
- func DefaultFileTokenStorageDir() (string, error)
- func UUID() string
- type APIError
- type Aggregation
- func AvgAggregation(name, field string) Aggregation
- func CountAggregation(name, field string) Aggregation
- func EntityAggregation(name, field, definition string) Aggregation
- func FilterAggregation(name string, filter []Filter, nested Aggregation) Aggregation
- func HistogramAggregation(name, field, interval, format string, nested Aggregation, timeZone string) Aggregation
- func MaxAggregation(name, field string) Aggregation
- func MinAggregation(name, field string) Aggregation
- func StatsAggregation(name, field string) Aggregation
- func SumAggregation(name, field string) Aggregation
- func TermsAggregation(name, field string, limit *int, sort *Sorting, nested Aggregation) Aggregation
- type AggregationResults
- func (a AggregationResults) Decode(name string, target any) error
- func (a AggregationResults) GetAvg(name string) (*AvgResult, error)
- func (a AggregationResults) GetCount(name string) (*CountResult, error)
- func (a AggregationResults) GetDateHistogram(name string) (*DateHistogramResult, error)
- func (a AggregationResults) GetEntity(name string) (*EntityResult, error)
- func (a AggregationResults) GetMax(name string) (*MaxResult, error)
- func (a AggregationResults) GetMin(name string) (*MinResult, error)
- func (a AggregationResults) GetStats(name string) (*StatsResult, error)
- func (a AggregationResults) GetSum(name string) (*SumResult, error)
- func (a AggregationResults) GetTerms(name string) (*TermsResult, error)
- func (a AggregationResults) Has(name string) bool
- func (a AggregationResults) Names() []string
- type AvgResult
- type Bucket
- type Client
- func (c *Client) AccessToken(ctx context.Context) (string, error)
- func (c *Client) Authenticate(ctx context.Context) error
- func (c *Client) Delete(ctx context.Context, path string, body any) (*Response, error)
- func (c *Client) Get(ctx context.Context, path string) (*Response, error)
- func (c *Client) Info(ctx context.Context) (*Info, error)
- func (c *Client) Patch(ctx context.Context, path string, body any) (*Response, error)
- func (c *Client) Post(ctx context.Context, path string, body any) (*Response, error)
- func (c *Client) Put(ctx context.Context, path string, body any) (*Response, error)
- func (c *Client) Request(ctx context.Context, method, path string, body any, ...) (*Response, error)
- func (c *Client) RequestRaw(ctx context.Context, method, path string, ...) (*Response, error)
- func (c *Client) SetAccessToken(ctx context.Context, token string, expiry time.Time) error
- func (c *Client) Version(ctx context.Context) (string, error)
- type Config
- type CountResult
- type Credentials
- type Criteria
- func (c *Criteria) AddAggregation(aggregation Aggregation) *Criteria
- func (c *Criteria) AddAssociation(path string) *Criteria
- func (c *Criteria) AddFields(fields ...string) *Criteria
- func (c *Criteria) AddFilter(filter Filter) *Criteria
- func (c *Criteria) AddGrouping(field string) *Criteria
- func (c *Criteria) AddIncludes(includes map[string][]string) *Criteria
- func (c *Criteria) AddPostFilter(filter Filter) *Criteria
- func (c *Criteria) AddQuery(filter Filter, score float64, scoreField ...string) *Criteria
- func (c *Criteria) AddSorting(sorting Sorting) *Criteria
- func (c *Criteria) GetAssociation(path string) *Criteria
- func (c *Criteria) GetLimit() int
- func (c *Criteria) GetPage() int
- func (c *Criteria) HasAssociation(name string) bool
- func (c *Criteria) MarshalJSON() ([]byte, error)
- func (c *Criteria) ResetSorting() *Criteria
- func (c *Criteria) SetIDs(ids []string) *Criteria
- func (c *Criteria) SetLimit(limit int) *Criteria
- func (c *Criteria) SetPage(page int) *Criteria
- func (c *Criteria) SetTerm(term string) *Criteria
- func (c *Criteria) SetTitle(title string) *Criteria
- func (c *Criteria) SetTotalCountMode(mode TotalCountMode) *Criteria
- func (c *Criteria) ToPayload() map[string]any
- type DateHistogramResult
- type EntityRepository
- func (r *EntityRepository[T]) Aggregate(ctx context.Context, criteria *Criteria, opts ...RequestOption) (AggregationResults, error)
- func (r *EntityRepository[T]) AggregateAs[A any](ctx context.Context, criteria *Criteria, opts ...RequestOption) (*A, error)
- func (r *EntityRepository[T]) Delete(ctx context.Context, payload []map[string]any, opts ...RequestOption) error
- func (r *EntityRepository[T]) DeleteByFilters(ctx context.Context, filters []Filter, opts ...RequestOption) error
- func (r *EntityRepository[T]) Search(ctx context.Context, criteria *Criteria, opts ...RequestOption) (*SearchResult[T], error)
- func (r *EntityRepository[T]) SearchIDs(ctx context.Context, criteria *Criteria, opts ...RequestOption) ([]string, error)
- func (r *EntityRepository[T]) SearchIDsAs[ID any](ctx context.Context, criteria *Criteria, opts ...RequestOption) ([]ID, error)
- func (r *EntityRepository[T]) Upsert(ctx context.Context, payload []T, opts ...RequestOption) error
- type EntityResult
- type FileTokenStorage
- func (s *FileTokenStorage) Delete(ctx context.Context, key string) error
- func (s *FileTokenStorage) Dir() string
- func (s *FileTokenStorage) Get(ctx context.Context, key string) (string, time.Time, error)
- func (s *FileTokenStorage) PathForKey(key string) string
- func (s *FileTokenStorage) Set(ctx context.Context, key, token string, expiry time.Time) error
- type Filter
- func Contains(field, value string) Filter
- func Equals(field string, value any) Filter
- func EqualsAny(field string, value []any) Filter
- func Multi(operator string, queries []Filter) Filter
- func Not(operator string, queries []Filter) Filter
- func Prefix(field, value string) Filter
- func Range(field string, parameters map[string]any) Filter
- func Suffix(field, value string) Filter
- type InMemoryTokenStorage
- type Info
- type InfoBundle
- type IntegrationCredentials
- type MaxResult
- type MinResult
- type NoOpTokenStorage
- type Numeric
- type PasswordCredentials
- type Query
- type RefreshTokenCredentials
- type RequestOption
- func WithHeader(key, value string) RequestOption
- func WithIndexingBehaviour(value string) RequestOption
- func WithIndexingSkip(value string) RequestOption
- func WithInheritance(inherit bool) RequestOption
- func WithLanguage(languageID string) RequestOption
- func WithSkipTriggerFlows(skip bool) RequestOption
- func WithVersion(versionID string) RequestOption
- type Response
- type ScopedTokenStorage
- func (s *ScopedTokenStorage) Delete(ctx context.Context, key string) error
- func (s *ScopedTokenStorage) Get(ctx context.Context, key string) (string, time.Time, error)
- func (s *ScopedTokenStorage) Scope() string
- func (s *ScopedTokenStorage) Set(ctx context.Context, key, token string, expiry time.Time) error
- func (s *ScopedTokenStorage) Unwrap() TokenStorage
- type SearchResult
- type Sorting
- type StatsResult
- type SumResult
- type SyncOperation
- type SyncService
- type TermsResult
- type TokenStorage
- type TotalCountMode
Examples ¶
- APIError
- Criteria (NestedAssociations)
- EntityRepository.Aggregate
- EntityRepository.AggregateAs
- EntityRepository.Search
- EntityRepository.SearchIDsAs
- EntityRepository.Upsert
- NewClient (CustomHTTPClientAndStorage)
- NewClient (FileTokenStorage)
- NewClient (Integration)
- NewClient (Password)
- NewClient (PkceLogin)
Constants ¶
const ( // DefaultTimeout is applied to the internal http.Client when the caller // does not provide one. DefaultTimeout = 30 * time.Second // DefaultUserAgent is sent as the User-Agent header on every request // (token requests included) unless overridden via Config.UserAgent, // Config.Headers, or per-request extraHeaders. DefaultUserAgent = "go-shopware-http-client" )
const ( DefaultSystemLanguageID = "2fbb5fe2e29a4d70aa5854ce7ce3e20b" DefaultLiveVersionID = "0fa91ce3e96a4bc2be4bd9ce752c3425" DefaultSystemCurrencyID = "b7d2554b0ce847cd82f3ac9bd1c0dfca" DefaultSalesChannelTypeAPI = "f183ee5650cf4bdb8a774337575067a6" DefaultSalesChannelTypeStorefront = "8a243080f92e4c719546314b577cf82b" DefaultSalesChannelTypeProductExport = "ed535e5722134ac1aa6524f73e26881b" )
Well-known default IDs used throughout Shopware.
Variables ¶
This section is empty.
Functions ¶
func DefaultFileTokenStorageDir ¶
DefaultFileTokenStorageDir returns a user-private default directory for file token storage: <os.UserCacheDir()>/go-shopware-http-client/tokens. Callers typically pass it to NewFileTokenStorage.
Types ¶
type APIError ¶
APIError describes a non-2xx response from the API. When the body matches the Shopware errors envelope, Detail holds the first error's detail message.
Example ¶
package main
import (
"context"
"errors"
"fmt"
"github.com/shopwareLabs/go-shopware-http-client"
)
func main() {
ctx := context.Background()
client := shopware.NewClient(shopware.Config{BaseURL: "https://x", ClientID: "i", ClientSecret: "s"})
_, err := client.Get(ctx, "/search/does-not-exist")
var apiErr *shopware.APIError
if errors.As(err, &apiErr) {
fmt.Println(apiErr.StatusCode, apiErr.Detail)
}
}
Output:
type Aggregation ¶
Aggregation is a single DAL aggregation. Construct with the helpers (TermsAggregation, SumAggregation, ...).
func AvgAggregation ¶
func AvgAggregation(name, field string) Aggregation
AvgAggregation computes the average of field.
func CountAggregation ¶
func CountAggregation(name, field string) Aggregation
CountAggregation counts non-null values of field.
func EntityAggregation ¶
func EntityAggregation(name, field, definition string) Aggregation
EntityAggregation buckets documents by an associated entity.
func FilterAggregation ¶
func FilterAggregation(name string, filter []Filter, nested Aggregation) Aggregation
FilterAggregation applies filters before computing the nested aggregation.
func HistogramAggregation ¶
func HistogramAggregation(name, field, interval, format string, nested Aggregation, timeZone string) Aggregation
HistogramAggregation buckets documents into date intervals. Empty/nil optional arguments are omitted.
func MaxAggregation ¶
func MaxAggregation(name, field string) Aggregation
MaxAggregation computes the maximum of field.
func MinAggregation ¶
func MinAggregation(name, field string) Aggregation
MinAggregation computes the minimum of field.
func StatsAggregation ¶
func StatsAggregation(name, field string) Aggregation
StatsAggregation computes sum/max/min/avg/count of field at once.
func SumAggregation ¶
func SumAggregation(name, field string) Aggregation
SumAggregation computes the sum of field.
func TermsAggregation ¶
func TermsAggregation(name, field string, limit *int, sort *Sorting, nested Aggregation) Aggregation
TermsAggregation buckets documents by the distinct values of field. A nil nested aggregation or sorting is omitted.
type AggregationResults ¶
AggregationResults is the decoded "aggregations" object: a map from each aggregation's name to its raw result. Decode a named entry with the Get* helpers, or with Decode for a custom type.
func (AggregationResults) Decode ¶
func (a AggregationResults) Decode(name string, target any) error
Decode unmarshals the named aggregation into target. It returns an error if no aggregation with that name exists.
func (AggregationResults) GetAvg ¶
func (a AggregationResults) GetAvg(name string) (*AvgResult, error)
GetAvg decodes the named aggregation as an avg result.
func (AggregationResults) GetCount ¶
func (a AggregationResults) GetCount(name string) (*CountResult, error)
GetCount decodes the named aggregation as a count result.
func (AggregationResults) GetDateHistogram ¶
func (a AggregationResults) GetDateHistogram(name string) (*DateHistogramResult, error)
GetDateHistogram decodes the named aggregation as a date histogram result.
func (AggregationResults) GetEntity ¶
func (a AggregationResults) GetEntity(name string) (*EntityResult, error)
GetEntity decodes the named aggregation as an entity result.
func (AggregationResults) GetMax ¶
func (a AggregationResults) GetMax(name string) (*MaxResult, error)
GetMax decodes the named aggregation as a max result.
func (AggregationResults) GetMin ¶
func (a AggregationResults) GetMin(name string) (*MinResult, error)
GetMin decodes the named aggregation as a min result.
func (AggregationResults) GetStats ¶
func (a AggregationResults) GetStats(name string) (*StatsResult, error)
GetStats decodes the named aggregation as a stats result.
func (AggregationResults) GetSum ¶
func (a AggregationResults) GetSum(name string) (*SumResult, error)
GetSum decodes the named aggregation as a sum result.
func (AggregationResults) GetTerms ¶
func (a AggregationResults) GetTerms(name string) (*TermsResult, error)
GetTerms decodes the named aggregation as a terms result.
func (AggregationResults) Has ¶
func (a AggregationResults) Has(name string) bool
Has reports whether an aggregation with the given name is present.
func (AggregationResults) Names ¶
func (a AggregationResults) Names() []string
Names returns the names of all aggregations present.
type Bucket ¶
type Bucket struct {
Key string `json:"key"`
Count int `json:"count"`
// Nested is the sub-aggregation result merged into the bucket under its
// own name. It is nil when the bucket has no nested aggregation.
Nested jsontext.Value `json:"-"`
}
Bucket is one bucket of a terms or histogram aggregation. Key identifies the bucket, Count is the number of documents in it, and Nested holds the nested sub-aggregation result (if the criteria requested one) keyed by its name — decode it with NestedAs.
func (*Bucket) UnmarshalJSON ¶
UnmarshalJSON decodes the fixed bucket fields and captures any extra key as the nested aggregation result.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is an authenticated HTTP client for the Shopware Admin API. It caches the OAuth token, collapses concurrent token fetches into a single request, and transparently retries once on a 401 with a fresh token.
A Client is safe for concurrent use.
func NewClient ¶
NewClient creates a new Client from the given Config.
Example (CustomHTTPClientAndStorage) ¶
package main
import (
"net/http"
"time"
"github.com/shopwareLabs/go-shopware-http-client"
)
func main() {
_ = shopware.NewClient(shopware.Config{
BaseURL: "https://my-shop.example.com",
ClientID: "CLIENT_ID",
ClientSecret: "CLIENT_SECRET",
HTTPClient: &http.Client{Timeout: 30 * time.Second},
TokenStorage: shopware.NewInMemoryTokenStorage(),
Headers: map[string]string{"x-internal-proxy-token": "secret"},
})
}
Output:
Example (FileTokenStorage) ¶
package main
import (
"github.com/shopwareLabs/go-shopware-http-client"
)
func main() {
dir, err := shopware.DefaultFileTokenStorageDir()
if err != nil {
return
}
store, err := shopware.NewFileTokenStorage(dir)
if err != nil {
return
}
shopURL := "https://my-shop.example.com"
_ = shopware.NewClient(shopware.Config{
BaseURL: shopURL,
ClientID: "CLIENT_ID",
ClientSecret: "CLIENT_SECRET",
// Scope by shop so one shared directory stays correct if the same
// principal is ever used against multiple shops.
TokenStorage: shopware.NewScopedTokenStorage(store, shopURL),
})
}
Output:
Example (Integration) ¶
package main
import (
"context"
"github.com/shopwareLabs/go-shopware-http-client"
)
func main() {
client := shopware.NewClient(shopware.Config{
BaseURL: "https://my-shop.example.com",
Credentials: shopware.NewIntegrationCredentials("CLIENT_ID", "CLIENT_SECRET"),
})
_ = client.Authenticate(context.Background())
}
Output:
Example (Password) ¶
package main
import (
"github.com/shopwareLabs/go-shopware-http-client"
)
func main() {
_ = shopware.NewClient(shopware.Config{
BaseURL: "https://my-shop.example.com",
Credentials: shopware.NewPasswordCredentials("admin", "shopware"),
})
}
Output:
Example (PkceLogin) ¶
package main
import (
"context"
"fmt"
"github.com/shopwareLabs/go-shopware-http-client"
"github.com/shopwareLabs/go-shopware-http-client/pkce"
)
type exampleProduct struct {
ID string `json:"id"`
Name string `json:"name"`
}
func main() {
ctx := context.Background()
store, err := pkce.NewFileStore("/path/to/sessions")
if err != nil {
return
}
// Reuses the saved session for the shop, or opens the browser to log in.
// Every token change, including rotated refresh tokens, is saved to store.
client, err := pkce.NewClient(ctx, pkce.Config{
BaseURL: "https://my-shop.example.com",
}, store)
if err != nil {
return
}
products := shopware.NewRepository[exampleProduct](client, "product")
result, err := products.Search(ctx, shopware.NewCriteria().SetLimit(10))
if err != nil {
return
}
fmt.Println(result.Total)
}
Output:
func (*Client) AccessToken ¶
AccessToken returns a valid OAuth access token, fetching or refreshing it when the cached one is missing or about to expire. Use it to hand the token to external tools (curl, a browser session, ...); regular requests through the Client authenticate on their own.
func (*Client) Authenticate ¶
Authenticate verifies the credentials by fetching an access token. It returns nil on success. The context bounds the token request.
func (*Client) Info ¶
Info returns the shop's /api/_info/config, fetching it once and caching the result for the lifetime of the Client. Concurrent callers share a single fetch.
func (*Client) Request ¶
func (c *Client) Request(ctx context.Context, method, path string, body any, extraHeaders map[string]string) (*Response, error)
Request makes an authenticated request to /api+path with a JSON body. extraHeaders are merged on top of the per-request defaults (e.g. DAL context headers).
func (*Client) RequestRaw ¶
func (c *Client) RequestRaw(ctx context.Context, method, path string, bodyFactory func() (io.Reader, error), extraHeaders map[string]string) (*Response, error)
RequestRaw makes an authenticated request to /api+path sending the body produced by bodyFactory verbatim, without JSON marshaling. The caller sets the Content-Type (and any other headers) via extraHeaders. Use it for file uploads (multipart/form-data) or other non-JSON payloads.
bodyFactory is a factory, not a reader, because the request may be retried once after a 401 — the body must be reproducible. Return (nil, nil) for an empty body.
func (*Client) SetAccessToken ¶
SetAccessToken seeds the token cache with a pre-obtained access token and its expiry. Use it after an interactive PKCE login (via the pkce sub-package) to avoid an immediate refresh_token round-trip.
type Config ¶
type Config struct {
// BaseURL is the shop URL without a trailing "/api" (e.g.
// "https://shop.example.com"). A trailing slash is trimmed.
BaseURL string
// Credentials selects the OAuth grant used to authenticate. Use
// NewIntegrationCredentials (client_credentials) or NewPasswordCredentials
// (admin user login), or supply a custom Credentials implementation.
//
// If nil, ClientID/ClientSecret are used as IntegrationCredentials.
Credentials Credentials
// ClientID and ClientSecret are a shorthand for integration credentials and
// are only consulted when Credentials is nil.
ClientID string
ClientSecret string
// HTTPClient performs the actual requests. If nil, a default client with
// DefaultTimeout is used. Inject your own to add instrumentation, SSRF
// protection, or custom transports.
HTTPClient *http.Client
// Headers are attached to every request (token requests included). Use this
// for deployment-specific headers such as a reverse-proxy auth token.
// A "User-Agent" entry here overrides UserAgent/DefaultUserAgent.
Headers map[string]string
// UserAgent overrides DefaultUserAgent. If empty, DefaultUserAgent is
// sent. It can still be overridden per deployment via Headers or
// per request via extraHeaders (Request/RequestRaw).
UserAgent string
// TokenStorage caches OAuth tokens. If nil, a process-local
// InMemoryTokenStorage is used, caching the token for the lifetime of the
// Client. Provide a distributed store (Redis, ...) to share tokens across
// instances, NewFileTokenStorage() to persist tokens on disk across
// restarts and processes, or NewNoOpTokenStorage() to disable caching.
TokenStorage TokenStorage
// TokenStorageKey is the key under which this Client's token is stored. If
// empty, a key derived from the credentials' identity is used. Set this when
// several Clients share a TokenStorage but must not share tokens (e.g. the
// same principal against different shops).
TokenStorageKey string
}
Config configures a Client. Only BaseURL and the OAuth credentials are required; everything else has sensible defaults.
type CountResult ¶
CountResult is the result of a count aggregation.
type Credentials ¶
type Credentials interface {
// contains filtered or unexported methods
}
Credentials produces the OAuth token request for a Client. The Shopware Admin API supports two grant types, each modeled by an implementation here:
- IntegrationCredentials — client_credentials grant (integration / app).
- PasswordCredentials — password grant (admin user login).
Custom implementations may supply any other body the /api/oauth/token endpoint accepts.
type Criteria ¶
type Criteria struct {
// contains filtered or unexported fields
}
Criteria is a fluent builder for Shopware DAL search and aggregation payloads. The zero value is not ready for use; create one with NewCriteria.
All mutating methods return the receiver so calls can be chained:
c := NewCriteria().
SetLimit(10).
AddFilter(Equals("active", true)).
AddSorting(Sort("createdAt", "DESC")).
AddAssociation("media")
Example (NestedAssociations) ¶
package main
import (
"github.com/shopwareLabs/go-shopware-http-client"
)
func main() {
c := shopware.NewCriteria()
c.AddAssociation("categories")
c.GetAssociation("manufacturer.media").
AddFilter(shopware.Equals("private", false))
_ = c
}
Output:
func NewCriteria ¶
NewCriteria creates an empty Criteria, optionally pre-filled with ids.
func (*Criteria) AddAggregation ¶
func (c *Criteria) AddAggregation(aggregation Aggregation) *Criteria
AddAggregation appends an aggregation.
func (*Criteria) AddAssociation ¶
AddAssociation ensures a sub-criteria exists for each segment of the dot-separated path and returns the receiver for chaining.
func (*Criteria) AddGrouping ¶
AddGrouping groups the result by the given field.
func (*Criteria) AddIncludes ¶
AddIncludes merges the given include map (entity name -> fields) into the criteria's includes.
func (*Criteria) AddPostFilter ¶
AddPostFilter appends a filter applied to documents but not aggregations.
func (*Criteria) AddSorting ¶
AddSorting appends a sort instruction.
func (*Criteria) GetAssociation ¶
GetAssociation ensures a sub-criteria exists for each segment of the dot-separated path and returns the Criteria of the last segment, so it can be further refined (filters, sortings, nested associations).
func (*Criteria) HasAssociation ¶
HasAssociation reports whether a direct association with the given name exists.
func (*Criteria) MarshalJSON ¶
MarshalJSON implements json.Marshaler so a Criteria can be passed directly as a request body.
func (*Criteria) ResetSorting ¶
ResetSorting removes all sort instructions.
func (*Criteria) SetTitle ¶
SetTitle sets a title shown in the search request URL, useful for debugging.
func (*Criteria) SetTotalCountMode ¶
func (c *Criteria) SetTotalCountMode(mode TotalCountMode) *Criteria
SetTotalCountMode configures how the total count is computed.
type DateHistogramResult ¶
DateHistogramResult is the result of a histogram (date histogram) aggregation. Bucket keys are formatted timestamps (e.g. "2026-06-01 00:00:00").
type EntityRepository ¶
type EntityRepository[T any] struct { // contains filtered or unexported fields }
EntityRepository provides typed search and write access to a single entity. Create one with NewRepository:
products := NewRepository[Product](client, "product") res, err := products.Search(ctx, NewCriteria().SetLimit(10))
func NewRepository ¶
func NewRepository[T any](client *Client, entityName string) *EntityRepository[T]
NewRepository returns a repository for the given entity (e.g. "product", "sales_channel"). The entity name uses snake_case as in the DAL.
func (*EntityRepository[T]) Aggregate ¶
func (r *EntityRepository[T]) Aggregate(ctx context.Context, criteria *Criteria, opts ...RequestOption) (AggregationResults, error)
Aggregate executes the criteria for its aggregations only, forcing limit=1 so no documents are loaded. It returns the aggregations as an AggregationResults map; decode a named entry with its Get* helpers (GetTerms, GetStats, ...).
For a fully custom result shape, use the generic AggregateAs instead.
Example ¶
package main
import (
"context"
"fmt"
"github.com/shopwareLabs/go-shopware-http-client"
)
type exampleProduct struct {
ID string `json:"id"`
Name string `json:"name"`
}
func main() {
ctx := context.Background()
client := shopware.NewClient(shopware.Config{BaseURL: "https://x", ClientID: "i", ClientSecret: "s"})
products := shopware.NewRepository[exampleProduct](client, "product")
aggs, err := products.Aggregate(ctx,
shopware.NewCriteria().
AddAggregation(shopware.TermsAggregation("by_active", "active", nil, nil, nil)).
AddAggregation(shopware.StatsAggregation("price_stats", "price")))
if err != nil {
return
}
terms, _ := aggs.GetTerms("by_active")
for _, b := range terms.Buckets {
fmt.Println(b.Key, b.Count)
}
stats, _ := aggs.GetStats("price_stats")
fmt.Println(stats.Min.String(), stats.Avg)
}
Output:
func (*EntityRepository[T]) AggregateAs ¶
func (r *EntityRepository[T]) AggregateAs[A any](ctx context.Context, criteria *Criteria, opts ...RequestOption) (*A, error)
AggregateAs executes the criteria for its aggregations only and decodes the aggregation payload into A. The shape of A depends on the aggregations added to the criteria, not on the entity, so the type parameter lives on the method instead of the repository:
type Aggs struct {
ByActive struct {
Buckets []struct {
Key string `json:"key"`
Count int `json:"count"`
} `json:"buckets"`
} `json:"by_active"`
}
aggs, err := products.AggregateAs[Aggs](ctx, criteria)
Example ¶
package main
import (
"context"
"fmt"
"github.com/shopwareLabs/go-shopware-http-client"
)
type exampleProduct struct {
ID string `json:"id"`
Name string `json:"name"`
}
func main() {
ctx := context.Background()
client := shopware.NewClient(shopware.Config{BaseURL: "https://x", ClientID: "i", ClientSecret: "s"})
products := shopware.NewRepository[exampleProduct](client, "product")
type aggs struct {
ByActive shopware.TermsResult `json:"by_active"`
}
got, err := products.AggregateAs[aggs](ctx,
shopware.NewCriteria().AddAggregation(shopware.TermsAggregation("by_active", "active", nil, nil, nil)))
if err != nil {
return
}
fmt.Println(len(got.ByActive.Buckets))
}
Output:
func (*EntityRepository[T]) Delete ¶
func (r *EntityRepository[T]) Delete(ctx context.Context, payload []map[string]any, opts ...RequestOption) error
Delete removes the given entities. Each element typically carries at least an "id" field.
func (*EntityRepository[T]) DeleteByFilters ¶
func (r *EntityRepository[T]) DeleteByFilters(ctx context.Context, filters []Filter, opts ...RequestOption) error
DeleteByFilters removes all entities matching the given filters.
func (*EntityRepository[T]) Search ¶
func (r *EntityRepository[T]) Search(ctx context.Context, criteria *Criteria, opts ...RequestOption) (*SearchResult[T], error)
Search executes the criteria and returns the matching entities.
Example ¶
package main
import (
"context"
"fmt"
"github.com/shopwareLabs/go-shopware-http-client"
)
type exampleProduct struct {
ID string `json:"id"`
Name string `json:"name"`
}
func main() {
ctx := context.Background()
client := shopware.NewClient(shopware.Config{
BaseURL: "https://my-shop.example.com",
Credentials: shopware.NewIntegrationCredentials("id", "secret"),
})
products := shopware.NewRepository[exampleProduct](client, "product")
result, err := products.Search(ctx,
shopware.NewCriteria().
SetLimit(10).
AddFilter(shopware.Equals("active", true)).
AddSorting(shopware.Sort("createdAt", "DESC")),
)
if err != nil {
return
}
fmt.Println(result.Total, result.First())
}
Output:
func (*EntityRepository[T]) SearchIDs ¶
func (r *EntityRepository[T]) SearchIDs(ctx context.Context, criteria *Criteria, opts ...RequestOption) ([]string, error)
SearchIDs executes the criteria and returns only the matching entity IDs.
This is the convenience form for entities with a single-column primary key (the common case), where /search-ids returns a flat list of id strings. For mapping entities (m:n join tables such as product_category), whose primary key is composite, /search-ids returns objects instead — decode those into a struct with SearchIDsAs.
func (*EntityRepository[T]) SearchIDsAs ¶
func (r *EntityRepository[T]) SearchIDsAs[ID any](ctx context.Context, criteria *Criteria, opts ...RequestOption) ([]ID, error)
SearchIDsAs executes the criteria and returns the matching primary keys decoded as ID. Use it for mapping entities whose composite key /search-ids returns as objects:
type ProductCategory struct {
ProductID string `json:"productId"`
CategoryID string `json:"categoryId"`
}
pairs, err := mapping.SearchIDsAs[ProductCategory](ctx, criteria)
For single-column keys, ID is string and SearchIDs is the shorter spelling.
Example ¶
package main
import (
"context"
"fmt"
"github.com/shopwareLabs/go-shopware-http-client"
)
func main() {
ctx := context.Background()
client := shopware.NewClient(shopware.Config{BaseURL: "https://x", ClientID: "i", ClientSecret: "s"})
type productCategory struct {
ProductID string `json:"productId"`
CategoryID string `json:"categoryId"`
}
mapping := shopware.NewRepository[productCategory](client, "product_category")
pairs, err := mapping.SearchIDsAs[productCategory](ctx, shopware.NewCriteria())
if err != nil {
return
}
fmt.Println(len(pairs))
}
Output:
func (*EntityRepository[T]) Upsert ¶
func (r *EntityRepository[T]) Upsert(ctx context.Context, payload []T, opts ...RequestOption) error
Upsert creates or updates the given entities in a single sync operation.
Example ¶
package main
import (
"context"
"github.com/shopwareLabs/go-shopware-http-client"
)
type exampleProduct struct {
ID string `json:"id"`
Name string `json:"name"`
}
func main() {
ctx := context.Background()
client := shopware.NewClient(shopware.Config{BaseURL: "https://x", ClientID: "i", ClientSecret: "s"})
products := shopware.NewRepository[exampleProduct](client, "product")
_ = products.Upsert(ctx, []exampleProduct{
{ID: shopware.UUID(), Name: "New Product"},
})
_ = products.DeleteByFilters(ctx, []shopware.Filter{
shopware.Equals("active", false),
})
}
Output:
type EntityResult ¶
EntityResult is the result of an entity aggregation. Entities holds the raw entity payloads; decode them into your entity type.
type FileTokenStorage ¶
type FileTokenStorage struct {
// contains filtered or unexported fields
}
FileTokenStorage is a TokenStorage that persists OAuth tokens on disk, one file per storage key. The filename is the hex-encoded SHA-256 hash of the key (plus a ".json" suffix), so neither the client ID, username, nor any other key material ever appears in the directory listing, and keys with characters that are unsafe for filenames (or path separators) cannot escape the directory.
Because the key is hashed, distinct principals never share a file — the same guarantee InMemoryTokenStorage gives. But note the default storage key only identifies the principal (e.g. "integration:<client-id>"), not the shop. If a single directory is shared by clients talking to *different* shops with the same principal, scope each client with NewScopedTokenStorage (or set Config.TokenStorageKey explicitly); otherwise they will share one file and one token.
Tokens survive process restarts and are shared between processes on the same machine. Writes are atomic (write-temp-then-rename) and files are created with mode 0600. The directory is created with mode 0700 when missing. Like all TokenStorage implementations, expired tokens are treated as absent.
A FileTokenStorage is safe for concurrent use.
func NewFileTokenStorage ¶
func NewFileTokenStorage(dir string) (*FileTokenStorage, error)
NewFileTokenStorage creates a FileTokenStorage rooted at dir, creating the directory (with mode 0700) when it does not exist. It returns an error when dir is empty or the directory cannot be created.
func (*FileTokenStorage) Delete ¶
func (s *FileTokenStorage) Delete(ctx context.Context, key string) error
Delete removes the token for key. A missing file is not an error.
func (*FileTokenStorage) Dir ¶
func (s *FileTokenStorage) Dir() string
Dir returns the directory tokens are stored in.
func (*FileTokenStorage) Get ¶
Get returns the cached token for key, or an empty token when no valid (unexpired) token exists. Missing, expired, and unreadable/corrupt files all report a miss; corrupt and expired files are removed on a best-effort basis so the next request fetches (and stores) a fresh token.
func (*FileTokenStorage) PathForKey ¶
func (s *FileTokenStorage) PathForKey(key string) string
PathForKey returns the file backing key. It is exposed for debugging and tooling (e.g. listing or pruning cached tokens); callers should otherwise treat the layout as opaque.
type Filter ¶
Filter is a single DAL filter (equals, range, multi, ...). Construct filters with the package-level helpers (Equals, EqualsAny, Contains, Range, ...).
type InMemoryTokenStorage ¶
type InMemoryTokenStorage struct {
// contains filtered or unexported fields
}
InMemoryTokenStorage is a process-local TokenStorage backed by a map. It is the default for the Client. Tokens are lost on restart and not shared across processes; for multi-instance deployments back the Client with a distributed store instead.
func NewInMemoryTokenStorage ¶
func NewInMemoryTokenStorage() *InMemoryTokenStorage
NewInMemoryTokenStorage creates an empty in-memory token storage.
func (*InMemoryTokenStorage) Delete ¶
func (s *InMemoryTokenStorage) Delete(_ context.Context, key string) error
Delete removes the token for key.
type Info ¶
type Info struct {
Version string `json:"version"`
VersionRevision string `json:"versionRevision"`
Bundles map[string]InfoBundle `json:"bundles"`
}
Info is the payload of GET /api/_info/config.
type InfoBundle ¶
InfoBundle lists the administration assets a bundle ships.
type IntegrationCredentials ¶
IntegrationCredentials authenticates as a Shopware integration using the client_credentials grant.
func NewIntegrationCredentials ¶
func NewIntegrationCredentials(clientID, clientSecret string) IntegrationCredentials
NewIntegrationCredentials creates IntegrationCredentials.
type MaxResult ¶
MaxResult is the result of a max aggregation. The value may be numeric or a numeric string.
type MinResult ¶
MinResult is the result of a min aggregation. The value may be numeric or a numeric string.
type NoOpTokenStorage ¶
type NoOpTokenStorage struct{}
NoOpTokenStorage is a TokenStorage that never caches. Every request triggers a fresh token fetch. Suitable for tests or callers that manage tokens elsewhere.
func NewNoOpTokenStorage ¶
func NewNoOpTokenStorage() *NoOpTokenStorage
NewNoOpTokenStorage creates a NoOpTokenStorage.
func (*NoOpTokenStorage) Delete ¶
func (*NoOpTokenStorage) Delete(context.Context, string) error
Delete is a no-op.
type Numeric ¶
type Numeric struct {
// contains filtered or unexported fields
}
Numeric holds a metric value that Shopware may serialize as either a number or a string (decimal columns come back as strings, e.g. "1.5400"), matching the PHP `mixed`/`float|int|string|null` types on min/max.
func (Numeric) Float ¶
Float returns the value as a float64, parsing it whether it arrived as a JSON number or a numeric string.
func (Numeric) MarshalJSON ¶
MarshalJSON renders the original value.
func (*Numeric) UnmarshalJSON ¶
UnmarshalJSON stores the raw value for later, lossless interpretation.
type PasswordCredentials ¶
PasswordCredentials authenticates as an admin user using the password grant. The client_id is fixed to "administration", matching the Shopware admin SPA.
func NewPasswordCredentials ¶
func NewPasswordCredentials(username, password string) PasswordCredentials
NewPasswordCredentials creates PasswordCredentials.
type Query ¶
type Query struct {
Score float64 `json:"score"`
Query Filter `json:"query"`
ScoreField string `json:"scoreField,omitempty"`
}
Query is a scored query used for relevance ranking.
type RefreshTokenCredentials ¶
type RefreshTokenCredentials struct {
ClientID string
// OnRotate, if set, is called with the new refresh token whenever the
// server rotates it, before the new access token is used. An error is
// returned from the request that triggered the refresh. Set it before the
// credentials are first used.
OnRotate func(ctx context.Context, refreshToken string) error
// contains filtered or unexported fields
}
RefreshTokenCredentials authenticates using the refresh_token grant. It is the bridge from an interactive PKCE login (via the pkce sub-package) into ongoing Client use: the initial access + refresh tokens are obtained by browser-based authorization, then stored here so the Client can transparently refresh the access token without further user interaction.
The refresh token is mutable (supports rotation) and thread-safe.
Shopware revokes the previous refresh token on every refresh, so a rotated token that is not persisted logs the user out on the next run. Set OnRotate to persist it, or use pkce.NewClient, which does this for you.
func NewRefreshTokenCredentials ¶
func NewRefreshTokenCredentials(clientID, refreshToken string) *RefreshTokenCredentials
NewRefreshTokenCredentials creates RefreshTokenCredentials with the given client ID and initial refresh token. Use the client ID that was registered as a public OAuth client in Shopware (e.g. "shopware-cli").
func (*RefreshTokenCredentials) RefreshToken ¶
func (c *RefreshTokenCredentials) RefreshToken() string
RefreshToken returns the current refresh token (for inspection/persistence).
func (*RefreshTokenCredentials) SetRefreshToken ¶
func (c *RefreshTokenCredentials) SetRefreshToken(refreshToken string)
SetRefreshToken updates the stored refresh token. Call it after a PKCE exchange or when the server returns a rotated refresh token.
type RequestOption ¶
type RequestOption func(*requestOptions)
RequestOption configures the DAL context (language, version, inheritance, indexing behaviour) for a single repository operation. Pass any number to the repository methods; with none, no context headers are sent.
repo.Search(ctx, criteria) // no context repo.Search(ctx, criteria, WithLanguage(langID)) // with a language
func WithHeader ¶
func WithHeader(key, value string) RequestOption
WithHeader sets an arbitrary request header, an escape hatch for headers not covered by a dedicated option.
func WithIndexingBehaviour ¶
func WithIndexingBehaviour(value string) RequestOption
WithIndexingBehaviour sets the indexing-behavior header.
func WithIndexingSkip ¶
func WithIndexingSkip(value string) RequestOption
WithIndexingSkip sets the indexing-skip header (a comma-separated list of indexers to skip).
func WithInheritance ¶
func WithInheritance(inherit bool) RequestOption
WithInheritance toggles the sw-inheritance header.
func WithLanguage ¶
func WithLanguage(languageID string) RequestOption
WithLanguage sets the sw-language-id header.
func WithSkipTriggerFlows ¶
func WithSkipTriggerFlows(skip bool) RequestOption
WithSkipTriggerFlows toggles the sw-skip-trigger-flow header.
func WithVersion ¶
func WithVersion(versionID string) RequestOption
WithVersion sets the sw-version-id header.
type ScopedTokenStorage ¶
type ScopedTokenStorage struct {
// contains filtered or unexported fields
}
ScopedTokenStorage prefixes every key with a fixed scope before delegating to its parent storage. Use it to namespace one shared backend — e.g. a FileTokenStorage directory — per shop:
fileStore, err := shopware.NewFileTokenStorage(dir)
storage := shopware.NewScopedTokenStorage(fileStore, "https://shop.example.com")
client := shopware.NewClient(shopware.Config{
BaseURL: "https://shop.example.com",
Credentials: shopware.NewIntegrationCredentials(id, secret),
TokenStorage: storage,
})
The default storage key only identifies the principal ("integration:<client-id>", "password:<username>", "pkce:<client-id>"), so without scoping, clients with the same principal against different shops would share one cached token. ScopedTokenStorage makes the effective key "<scope>\x00<key>", which FileTokenStorage then hashes into the filename — effectively a hash of shop URL + client-id/username.
A ScopedTokenStorage is safe for concurrent use when its parent is.
func NewScopedTokenStorage ¶
func NewScopedTokenStorage(parent TokenStorage, scope string) *ScopedTokenStorage
NewScopedTokenStorage wraps parent so every key is namespaced under scope. Scope is typically the shop's BaseURL (any non-empty string works).
func (*ScopedTokenStorage) Delete ¶
func (s *ScopedTokenStorage) Delete(ctx context.Context, key string) error
Delete removes the token stored under the scoped key.
func (*ScopedTokenStorage) Scope ¶
func (s *ScopedTokenStorage) Scope() string
Scope returns the configured scope.
func (*ScopedTokenStorage) Unwrap ¶
func (s *ScopedTokenStorage) Unwrap() TokenStorage
Unwrap returns the parent storage.
type SearchResult ¶
type SearchResult[T any] struct { Total int `json:"total"` Data []T `json:"data"` Aggregations AggregationResults `json:"aggregations"` }
SearchResult is the typed result of a DAL search. Aggregations exposes the typed Get* helpers (GetTerms, GetStats, ...) for any aggregations requested.
func (*SearchResult[T]) First ¶
func (r *SearchResult[T]) First() *T
First returns a pointer to the first result, or nil if there are none.
type Sorting ¶
type Sorting struct {
Field string `json:"field"`
Order string `json:"order"`
NaturalSorting bool `json:"naturalSorting"`
Type string `json:"type,omitempty"`
}
Sorting describes a single sort instruction.
func NaturalSort ¶
NaturalSort sorts by field using natural ordering.
type StatsResult ¶
type StatsResult struct {
Name string `json:"name"`
Min Numeric `json:"min"`
Max Numeric `json:"max"`
Avg *float64 `json:"avg"`
Sum *float64 `json:"sum"`
}
StatsResult is the result of a stats aggregation. Min and Max may be numeric or numeric strings; Avg and Sum are nullable numbers.
type SyncOperation ¶
type SyncOperation struct {
Key string `json:"key"`
Entity string `json:"entity"`
Action string `json:"action"`
Payload []any `json:"payload"`
Criteria []Filter `json:"criteria,omitempty"`
}
SyncOperation is a single operation within a sync request.
func NewSyncOperation ¶
func NewSyncOperation(key, entity, action string, payload []any, criteria []Filter) SyncOperation
NewSyncOperation builds a SyncOperation. action is "upsert" or "delete".
type SyncService ¶
type SyncService struct {
// contains filtered or unexported fields
}
SyncService performs DAL sync operations against the /_action/sync endpoint.
func NewSyncService ¶
func NewSyncService(client *Client) *SyncService
NewSyncService returns a SyncService bound to the client.
func (*SyncService) Sync ¶
func (s *SyncService) Sync(ctx context.Context, operations []SyncOperation, opts ...RequestOption) error
Sync sends the operations to the /_action/sync endpoint.
type TermsResult ¶
TermsResult is the result of a terms aggregation.
type TokenStorage ¶
type TokenStorage interface {
// Get retrieves the token for key. It returns an empty string and the zero
// time when no valid (unexpired) token exists.
Get(ctx context.Context, key string) (token string, expiry time.Time, err error)
// Set stores token for key with the given expiry time.
Set(ctx context.Context, key string, token string, expiry time.Time) error
// Delete removes the token for key.
Delete(ctx context.Context, key string) error
}
TokenStorage stores and retrieves OAuth access tokens for a Client.
Implementations must treat expired tokens as absent: Get returns an empty token once the stored expiry has passed, prompting the Client to fetch a fresh one. Implementations must be safe for concurrent use.
Tokens are keyed by an opaque string chosen by the Client (its credentials' identity by default), so a single backend can serve many independent shops.
type TotalCountMode ¶
type TotalCountMode int
TotalCountMode controls how the total count of a search result is computed.
const ( // NoTotalCount selects no total count. Use when no pagination is required // (fastest). NoTotalCount TotalCountMode = 0 // ExactTotalCount selects the exact total count. Use when exact pagination // is required (slow). ExactTotalCount TotalCountMode = 1 // PaginationTotalCount fetches limit*5+1. Use when pagination only needs a // "next page exists" hint (fast). PaginationTotalCount TotalCountMode = 2 )
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
pkce-cli
command
pkce-cli is a minimal CLI that demonstrates the PKCE browser login against a Shopware instance and then calls /_info/me.
|
pkce-cli is a minimal CLI that demonstrates the PKCE browser login against a Shopware instance and then calls /_info/me. |
|
Package extension provides a typed helper over the Shopware /api/_action/extension/* endpoints: listing, lifecycle (install, update, activate, ...), and uploading extension zips.
|
Package extension provides a typed helper over the Shopware /api/_action/extension/* endpoints: listing, lifecycle (install, update, activate, ...), and uploading extension zips. |
|
Package instance provides helpers for operations on the shop as a whole rather than on a single entity, such as clearing its caches.
|
Package instance provides helpers for operations on the shop as a whole rather than on a single entity, such as clearing its caches. |
|
internal
|
|
|
assert
Package assert provides the small testify-style subset of test helpers this module's tests need, built on the standard library only.
|
Package assert provides the small testify-style subset of test helpers this module's tests need, built on the standard library only. |
|
Package media provides helpers for working with Shopware media: uploading files (by binary content or by URL) and resolving/creating media folders.
|
Package media provides helpers for working with Shopware media: uploading files (by binary content or by URL) and resolving/creating media folders. |
|
Package pkce provides an end-to-end OAuth 2.0 authorization-code flow with PKCE (Proof Key for Code Exchange) for public clients of the Shopware Admin API.
|
Package pkce provides an end-to-end OAuth 2.0 authorization-code flow with PKCE (Proof Key for Code Exchange) for public clients of the Shopware Admin API. |