package catalog

import "github.com/weswhet/applevpp/catalog"

Package catalog provides a Go client for Apple's Apps and Books for Organizations API.

The catalog API is separate from Apple's VPP management API and is intended for storefront-aware metadata retrieval. This package includes:

The main entry point is NewClient. Runnable usage snippets are available in example_test.go.

Index

Examples

Variables

var ErrNoDeveloperToken = errors.New("catalog: developer token is required")

ErrNoDeveloperToken indicates that a catalog endpoint was called without a developer token.

var ErrNoPlatform = errors.New("catalog: platform is required")

ErrNoPlatform indicates that a catalog request omitted Apple's required platform query parameter.

var ErrNoResourceID = errors.New("catalog: resource ID is required")

ErrNoResourceID indicates that a single-resource catalog request omitted the required resource identifier.

var ErrNoSToken = errors.New("catalog: sToken is required for this endpoint")

ErrNoSToken indicates that an sToken-authenticated endpoint was called without an sToken cookie value.

var ErrNoStorefront = errors.New("catalog: storefront is required")

ErrNoStorefront indicates that a catalog request omitted a required storefront path parameter.

Functions

func GenerateDeveloperToken

func GenerateDeveloperToken(keyID, teamID string, privateKeyPEM []byte, opts GenerateDeveloperTokenOptions) (string, error)

GenerateDeveloperToken creates an ES256 JWT that can be used with Apple's Apps and Books for Organizations catalog API.

Example
privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
der, _ := x509.MarshalECPrivateKey(privateKey)
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: der})

token, _ := GenerateDeveloperToken("ABC123DEFG", "TEAM123456", keyPEM, GenerateDeveloperTokenOptions{
	IssuedAt:  time.Unix(1700000000, 0).UTC(),
	ExpiresAt: time.Unix(1700003600, 0).UTC(),
})

fmt.Println(strings.Count(token, "."))

Output:

2

Types

type APIError

type APIError struct {
	StatusCode int
	Errors     []Error
	RetryAfter time.Duration
	Body       []byte
}

APIError represents an HTTP error returned by Apple's catalog API.

func (*APIError) Error

func (e *APIError) Error() string

type AllStorefrontsQuery

type AllStorefrontsQuery struct {
	Extend   ScopedValues
	Include  ScopedValues
	Language string
	Limit    ScopedInt
	Offset   string
	Platform Platform
	Relate   ScopedValues
}

AllStorefrontsQuery filters the paginated storefront listing endpoint.

type App

type App struct {
	Attributes    AppAttributes           `json:"attributes,omitempty"`
	Href          string                  `json:"href"`
	ID            string                  `json:"id"`
	Relationships map[string]Relationship `json:"relationships,omitempty"`
	Type          string                  `json:"type"`
}

type AppAttributes

type AppAttributes struct {
	ArtistName                       string                           `json:"artistName,omitempty"`
	Artwork                          *Artwork                         `json:"artwork,omitempty"`
	ContentRatingsBySystem           map[string]ContentRating         `json:"contentRatingsBySystem,omitempty"`
	Description                      *DescriptionAttribute            `json:"description,omitempty"`
	DeviceFamilies                   []string                         `json:"deviceFamilies,omitempty"`
	GenreDisplayName                 string                           `json:"genreDisplayName,omitempty"`
	GenreNames                       []string                         `json:"genreNames,omitempty"`
	HasEula                          bool                             `json:"hasEula,omitempty"`
	IsB2BCustomApp                   bool                             `json:"isB2BCustomApp,omitempty"`
	IsFirstPartyHideableApp          bool                             `json:"isFirstPartyHideableApp,omitempty"`
	IsIOSBinaryMacOSCompatible       bool                             `json:"isIOSBinaryMacOSCompatible,omitempty"`
	IsVppDeviceBasedLicensingEnabled bool                             `json:"isVppDeviceBasedLicensingEnabled,omitempty"`
	Name                             string                           `json:"name,omitempty"`
	Offers                           []Offer                          `json:"offers,omitempty"`
	PlatformAttributes               map[string]AppPlatformAttributes `json:"platformAttributes,omitempty"`
	SupportsDeviceSharing            bool                             `json:"supportsDeviceSharing,omitempty"`
	URL                              string                           `json:"url,omitempty"`
	UserRating                       *UserRating                      `json:"userRating,omitempty"`
	UsesClassKit                     bool                             `json:"usesClassKit,omitempty"`
	Unknown                          map[string]json.RawMessage       `json:"-"`
}

func (*AppAttributes) UnmarshalJSON

func (a *AppAttributes) UnmarshalJSON(data []byte) error

type AppPlatformAttributes

type AppPlatformAttributes struct {
	Artwork                              *Artwork                   `json:"artwork,omitempty"`
	BundleID                             string                     `json:"bundleId,omitempty"`
	ExternalVersionID                    int64                      `json:"externalVersionId,omitempty"`
	IsVisionOSCompatible                 bool                       `json:"isVisionOSCompatible,omitempty"`
	MinimumMacOSVersion                  string                     `json:"minimumMacOSVersion,omitempty"`
	MinimumOSVersion                     string                     `json:"minimumOSVersion,omitempty"`
	MinimumVisionOSVersion               string                     `json:"minimumVisionOSVersion,omitempty"`
	RequiredCapabilities                 string                     `json:"requiredCapabilities,omitempty"`
	RequiredCapabilitiesForRealityDevice string                     `json:"requiredCapabilitiesForRealityDevice,omitempty"`
	Seller                               string                     `json:"seller,omitempty"`
	Subtitle                             string                     `json:"subtitle,omitempty"`
	Unknown                              map[string]json.RawMessage `json:"-"`
}

func (*AppPlatformAttributes) UnmarshalJSON

func (a *AppPlatformAttributes) UnmarshalJSON(data []byte) error

type AppsResponse

type AppsResponse struct {
	Data []App `json:"data"`
}

type Artwork

type Artwork struct {
	AssetToken           string                     `json:"assetToken,omitempty"`
	BGColor              string                     `json:"bgColor,omitempty"`
	Gradient             json.RawMessage            `json:"gradient,omitempty"`
	HasP3                bool                       `json:"hasP3,omitempty"`
	Height               float64                    `json:"height"`
	PictureFileType      string                     `json:"pictureFileType,omitempty"`
	SupportsLayeredImage bool                       `json:"supportsLayeredImage,omitempty"`
	TextColor1           string                     `json:"textColor1,omitempty"`
	TextColor2           string                     `json:"textColor2,omitempty"`
	TextColor3           string                     `json:"textColor3,omitempty"`
	TextColor4           string                     `json:"textColor4,omitempty"`
	URL                  string                     `json:"url"`
	Width                float64                    `json:"width"`
	Unknown              map[string]json.RawMessage `json:"-"`
}

func (*Artwork) UnmarshalJSON

func (a *Artwork) UnmarshalJSON(data []byte) error

type Book

type Book struct {
	Attributes    BookAttributes          `json:"attributes,omitempty"`
	Href          string                  `json:"href"`
	ID            string                  `json:"id"`
	Relationships map[string]Relationship `json:"relationships,omitempty"`
	Type          string                  `json:"type"`
}

type BookAttributes

type BookAttributes struct {
	ArtistName string                     `json:"artistName,omitempty"`
	Artwork    *Artwork                   `json:"artwork,omitempty"`
	GenreNames []string                   `json:"genreNames,omitempty"`
	ISBN       string                     `json:"isbn,omitempty"`
	Name       string                     `json:"name,omitempty"`
	Offers     []Offer                    `json:"offers,omitempty"`
	URL        string                     `json:"url,omitempty"`
	UserRating *UserRating                `json:"userRating,omitempty"`
	Unknown    map[string]json.RawMessage `json:"-"`
}

func (*BookAttributes) UnmarshalJSON

func (b *BookAttributes) UnmarshalJSON(data []byte) error

type BooksResponse

type BooksResponse struct {
	Data []Book `json:"data"`
}

type CatalogAppQuery

type CatalogAppQuery struct {
	AdditionalPlatforms []Platform
	Extend              ScopedValues
	Include             ScopedValues
	Language            string
	Platform            Platform
	Relate              ScopedValues
}

CatalogAppQuery filters metadata lookup for a single app.

type CatalogAppsQuery

type CatalogAppsQuery struct {
	AdditionalPlatforms []Platform
	Extend              ScopedValues
	IDs                 []string
	Include             ScopedValues
	Language            string
	Platform            Platform
	Relate              ScopedValues
}

CatalogAppsQuery filters metadata lookup for multiple apps.

func (CatalogAppsQuery) CatalogAppQuery

func (q CatalogAppsQuery) CatalogAppQuery() CatalogAppQuery

type CatalogBookQuery

type CatalogBookQuery struct {
	AdditionalPlatforms []Platform
	Include             ScopedValues
	Language            string
	Platform            Platform
	Relate              ScopedValues
}

CatalogBookQuery filters metadata lookup for a single book.

type CatalogBooksQuery

type CatalogBooksQuery struct {
	AdditionalPlatforms []Platform
	IDs                 []string
	Include             ScopedValues
	Language            string
	Platform            Platform
	Relate              ScopedValues
}

CatalogBooksQuery filters metadata lookup for multiple books.

func (CatalogBooksQuery) CatalogBookQuery

func (q CatalogBooksQuery) CatalogBookQuery() CatalogBookQuery

type Client

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

Client wraps Apple's Apps and Books for Organizations catalog API.

func NewClient

func NewClient(developerToken string, opts Options) (*Client, error)

NewClient constructs a catalog client.

The developer token may be provided either as the first argument or through Options.DeveloperToken.

func (*Client) GetAllStorefronts

func (c *Client) GetAllStorefronts(ctx context.Context, query AllStorefrontsQuery) (*StorefrontsResponse, error)

GetAllStorefronts returns a page of storefronts using Apple's paginated storefront listing endpoint.

func (*Client) GetApp

func (c *Client) GetApp(ctx context.Context, storefront, id string, query CatalogAppQuery) (*AppsResponse, error)

GetApp returns metadata for a single app in a storefront.

Example
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	_ = json.NewEncoder(w).Encode(AppsResponse{
		Data: []App{{ID: "623592465", Type: "apps", Href: "/v1/catalog/us/apps/623592465"}},
	})
}))
defer server.Close()

client, _ := NewClient("developer-token", Options{BaseURL: server.URL})
resp, _ := client.GetApp(context.Background(), "us", "623592465", CatalogAppQuery{
	Platform: PlatformIPhone,
})

fmt.Println(resp.Data[0].ID)

Output:

623592465

func (*Client) GetApps

func (c *Client) GetApps(ctx context.Context, storefront string, query CatalogAppsQuery) (*AppsResponse, error)

GetApps returns metadata for multiple apps in a storefront.

func (*Client) GetBook

func (c *Client) GetBook(ctx context.Context, storefront, id string, query CatalogBookQuery) (*BooksResponse, error)

GetBook returns metadata for a single book in a storefront.

func (*Client) GetBooks

func (c *Client) GetBooks(ctx context.Context, storefront string, query CatalogBooksQuery) (*BooksResponse, error)

GetBooks returns metadata for multiple books in a storefront.

func (*Client) GetOwnedApps

func (c *Client) GetOwnedApps(ctx context.Context, storefront string, query OwnedAppsQuery) (*ResourceCollectionResponse, error)

GetOwnedApps returns the apps visible to the current organization for the supplied storefront and sToken.

func (*Client) GetOwnedBooks

func (c *Client) GetOwnedBooks(ctx context.Context, storefront string, query OwnedBooksQuery) (*ResourceCollectionResponse, error)

GetOwnedBooks returns the books visible to the current organization for the supplied storefront and sToken.

func (*Client) GetStorefront

func (c *Client) GetStorefront(ctx context.Context, id string, query StorefrontQuery) (*StorefrontsResponse, error)

GetStorefront returns metadata for a single storefront.

func (*Client) GetStorefronts

func (c *Client) GetStorefronts(ctx context.Context, query StorefrontsQuery) (*StorefrontsResponse, error)

GetStorefronts returns metadata for specific storefront IDs.

func (*Client) Search

func (c *Client) Search(ctx context.Context, storefront string, query SearchQuery) (*ResultsResponse, error)

Search runs a storefront catalog search for apps or books.

Example
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	_ = json.NewEncoder(w).Encode(ResultsResponse{
		Results: map[string][]Resource{
			"apps": {{
				ID:   "623592465",
				Type: "apps",
			}},
		},
	})
}))
defer server.Close()

client, _ := NewClient("developer-token", Options{BaseURL: server.URL})
resp, _ := client.Search(context.Background(), "us", SearchQuery{
	Platform: PlatformIPhone,
	Term:     "Numbers",
	Types:    []SearchType{SearchTypeApps},
})

fmt.Println(len(resp.Results["apps"]))

Output:

1

type ContentRating

type ContentRating struct {
	Name       string   `json:"name,omitempty"`
	Value      int      `json:"value,omitempty"`
	Rank       int      `json:"rank,omitempty"`
	Advisories []string `json:"advisories,omitempty"`
}

type DescriptionAttribute

type DescriptionAttribute struct {
	Short    string `json:"short,omitempty"`
	Standard string `json:"standard"`
}

type Error

type Error struct {
	Code   string          `json:"code"`
	Detail string          `json:"detail,omitempty"`
	ID     string          `json:"id"`
	Source json.RawMessage `json:"source,omitempty"`
	Status string          `json:"status"`
	Title  string          `json:"title"`
}

type ErrorsResponse

type ErrorsResponse struct {
	Errors []Error `json:"errors"`
}

type GenerateDeveloperTokenOptions

type GenerateDeveloperTokenOptions struct {
	IssuedAt  time.Time
	ExpiresAt time.Time
	Origins   []string
}

GenerateDeveloperTokenOptions controls the issued-at, expiration, and origin claims embedded in a developer token.

type Offer

type Offer struct {
	ActionText            map[string]string          `json:"actionText,omitempty"`
	Assets                []OfferAsset               `json:"assets,omitempty"`
	BuyParams             string                     `json:"buyParams,omitempty"`
	CurrencyCode          string                     `json:"currencyCode,omitempty"`
	Price                 float64                    `json:"price,omitempty"`
	PriceFormatted        string                     `json:"priceFormatted,omitempty"`
	PricePerUnit          float64                    `json:"pricePerUnit,omitempty"`
	PricePerUnitFormatted string                     `json:"pricePerUnitFormatted,omitempty"`
	Quantity              int                        `json:"quantity,omitempty"`
	Type                  string                     `json:"type,omitempty"`
	Version               *OfferVersion              `json:"version,omitempty"`
	Unknown               map[string]json.RawMessage `json:"-"`
}

func (*Offer) UnmarshalJSON

func (o *Offer) UnmarshalJSON(data []byte) error

type OfferAsset

type OfferAsset struct {
	Flavor string `json:"flavor,omitempty"`
	Size   int64  `json:"size,omitempty"`
}

type OfferVersion

type OfferVersion struct {
	Display    string `json:"display,omitempty"`
	ExternalID int64  `json:"externalId,omitempty"`
}

type Options

type Options struct {
	BaseURL        string
	HTTPClient     *http.Client
	Organization   Organization
	DeveloperToken string
	SToken         string
	UserAgent      string
	Now            func() time.Time
}

Options configures a catalog client.

type Organization

type Organization string

Organization selects the Apple catalog host family.

const (
	OrganizationEnterprise Organization = "enterprise"
	OrganizationEducation  Organization = "education"
)

type OwnedAppsQuery

type OwnedAppsQuery struct {
	AdditionalPlatforms []Platform
	Extend              ScopedValues
	IDs                 []string
	Language            string
	Platform            Platform
}

OwnedAppsQuery filters sToken-authenticated owned app lookups.

type OwnedBooksQuery

type OwnedBooksQuery struct {
	AdditionalPlatforms []Platform
	IDs                 []string
	Language            string
	Platform            Platform
}

OwnedBooksQuery filters sToken-authenticated owned book lookups.

type Platform

type Platform string

Platform identifies the target Apple platform for catalog queries.

const (
	PlatformAppleTV       Platform = "appletv"
	PlatformIPad          Platform = "ipad"
	PlatformIPhone        Platform = "iphone"
	PlatformMac           Platform = "mac"
	PlatformRealityDevice Platform = "realityDevice"
	PlatformWeb           Platform = "web"
)

type Relationship

type Relationship struct {
	Data    []Resource                 `json:"data,omitempty"`
	Href    string                     `json:"href,omitempty"`
	Meta    map[string]json.RawMessage `json:"meta,omitempty"`
	Next    string                     `json:"next,omitempty"`
	Unknown map[string]json.RawMessage `json:"-"`
}

func (*Relationship) UnmarshalJSON

func (r *Relationship) UnmarshalJSON(data []byte) error

type Resource

type Resource struct {
	Attributes    json.RawMessage            `json:"attributes,omitempty"`
	Href          string                     `json:"href,omitempty"`
	ID            string                     `json:"id"`
	Meta          map[string]json.RawMessage `json:"meta,omitempty"`
	Relationships map[string]Relationship    `json:"relationships,omitempty"`
	Type          string                     `json:"type"`
	Views         map[string]json.RawMessage `json:"views,omitempty"`
	Unknown       map[string]json.RawMessage `json:"-"`
}

func (*Resource) UnmarshalJSON

func (r *Resource) UnmarshalJSON(data []byte) error

type ResourceCollectionResponse

type ResourceCollectionResponse struct {
	Data []Resource `json:"data"`
}

type ResultsResponse

type ResultsResponse struct {
	Results map[string][]Resource `json:"results"`
}

type ScopedInt

type ScopedInt struct {
	Default *int
	ByScope map[string]int
}

type ScopedValues

type ScopedValues struct {
	Default []string
	ByScope map[string][]string
}

type SearchQuery

type SearchQuery struct {
	AdditionalPlatforms []Platform
	Extend              ScopedValues
	Filter              ScopedValues
	Language            string
	Platform            Platform
	Term                string
	Types               []SearchType
}

SearchQuery filters a storefront catalog search.

type SearchType

type SearchType string

SearchType filters catalog search results by resource kind.

const (
	SearchTypeApps  SearchType = "apps"
	SearchTypeBooks SearchType = "books"
)

type Storefront

type Storefront struct {
	Attributes StorefrontAttributes `json:"attributes,omitempty"`
	Href       string               `json:"href"`
	ID         string               `json:"id"`
	Type       string               `json:"type"`
}

type StorefrontAttributes

type StorefrontAttributes struct {
	DefaultLanguageTag    string                     `json:"defaultLanguageTag,omitempty"`
	Name                  string                     `json:"name,omitempty"`
	SupportedLanguageTags []string                   `json:"supportedLanguageTags,omitempty"`
	Unknown               map[string]json.RawMessage `json:"-"`
}

func (*StorefrontAttributes) UnmarshalJSON

func (s *StorefrontAttributes) UnmarshalJSON(data []byte) error

type StorefrontQuery

type StorefrontQuery struct {
	Extend   ScopedValues
	Include  ScopedValues
	Language string
	Platform Platform
	Relate   ScopedValues
}

StorefrontQuery filters metadata lookup for a single storefront.

type StorefrontsQuery

type StorefrontsQuery struct {
	Extend   ScopedValues
	IDs      []string
	Include  ScopedValues
	Language string
	Platform Platform
	Relate   ScopedValues
}

StorefrontsQuery filters metadata lookup for specific storefront IDs.

func (StorefrontsQuery) StorefrontQuery

func (q StorefrontsQuery) StorefrontQuery() StorefrontQuery

type StorefrontsResponse

type StorefrontsResponse struct {
	Data []Storefront `json:"data"`
}

type UnauthorizedResponse

type UnauthorizedResponse struct{}

type UserRating

type UserRating struct {
	Value                     float64 `json:"value,omitempty"`
	RatingCount               int     `json:"ratingCount,omitempty"`
	ValueCurrentVersion       float64 `json:"valueCurrentVersion,omitempty"`
	RatingCountCurrentVersion int     `json:"ratingCountCurrentVersion,omitempty"`
}