geyser

package module
v2.0.0 Latest Latest
Warning

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

Go to latest
Published: Apr 30, 2020 License: MIT Imports: 9 Imported by: 0

README

geyser

MIT GoDoc Travis GoReportCard codecov

Steam Web API client for Go.

Interface methods are auto-generated by the apigen package.

Documentation

Overview

Package geyser provides HTTP clients for Steam API and Steam API-like endpoints.

NOTE: To avoid confusion between Go terminology and abstract API entities (terms like "interface" and "method"), API entity names are expressed using capitalized words. Concrete Go names and statements are surrounded by backticks.

Steam APIs are structured as trees, with the base endpoint being the root node. The root node has a set of Interface nodes as children. Interfaces can be parameterized by AppID, so one Interface with 2 different versions results in 2 different nodes. Each Interface has a collection of Method leaf nodes. Methods can be versioned, so a Method with 2 different versions results in 2 different nodes, and can contain a set of Parameters (HTTP request parameters).

Schemas and client code are generated automatically by sub-package "apigen".

NOTE: A non-partner api key is used to fetch the API schema, so only non-partner Interfaces are generated.

Schema

Types in sub-package "schema" represent the tree structure of an API schema:

Schema
|-- Interface<appID>
    |-- Method<version>

A schema starts with a root `Schema` node that contains API information and an `Interfaces` collection.

`Interfaces` is a collection of `Interface` nodes. It also provides helper methods to group `Interface`s by name, extract AppIDs and so on.

`Interface` holds the specificiation of the Interface and a `Methods` collection.

`Methods` is a collection of `Method` nodes and provides helpers methods to group `Method`s by name, extract versions and so on.

`Method` represents the specification of an Interface Method. It also contains a collection of parameters `MethodParams`.

`MethodParams` is a collection of `MethodParam`. It also provides helpers for parameter validation.

`MethodParam` holds the specification of an Interface Method Parameter.

The specification for each Interface is exposed through `Schema<InterfaceName>` public variables. These can also be used direcly by users for any other purpose, including instantiating individual interface structs directly.

Node types provide JSON tags to encode/decode to/from JSON format. All of the collection types provide JSON encoding/decoding methods. A root `Schema` node is then well-suited to encode/decode API schemas to/from JSON format (i.e., when consuming "ISteamWebAPIUtil/GetSupportedAPIList").

Client Structure

Interfaces are grouped by their base name, with the AppID (if present) removed. For example: all "IGCVersion_<ID>" Interfaces are grouped into a single struct named "IGCVersion" that is instantiated by passing the AppID as parameter (`NewIGCVersion(client, appID)` or `client.IGCVersion(appID)`).

Interfaces that don't have an AppID do not require an AppID parameter (e.g.: "ISteamApps" -> `NewISteamApps(client)` or `client.NewISteamApps()`). Interfaces that have only one AppID still require an AppID parameter.

The same grouping is done for Interface Methods. All Methods with the same name but different versions are grouped by name and the version is parameterized (`iface.MethodName(version)`). Methods that have a single version do not require a version parameter (`iface.MethodName()`).

The workflow to perform an HTTP request to an Interface Method is:

Client
|-> Interface([appID])
    |-> Method([version])
        |-> Request
            |-> Configure Request (optional)
            |-> Request execution
                |-> HTTP response
                    |-> Check HTTP response status
                    |-> Parse response body or use the configured result value

Client is the root element. Interfaces are instantiated with methods in the `Client` struct (possibly requiring an AppID parameter). Interface Methods are struct methods of the Interface struct (possibly requiring a version parameter).

Parameters to the request must be set in the `Request` configuration step (see `Request.SetOptions`). Parameters are validated automatically conforming the Interface Method specification and the request execution will return an error if the validation failed.

The meaning and functionality of each Parameter for all Interface Methods is not well known, neither are they well documented (here, in official documentation or anywhere else). The user will have to do some experimenting and research to figure it out. That said, whenever Parameters are known, there may exist manually implemented helper functions to generate values (either query parameters or form data, as `net/url.Values`). These helper functions are named by concatenating the Interface struct name, the Method name and either "Params" or "FormData", depending on the HTTP method (e.g.: "ISteamRemoteStorage/GetCollectionDetails" -> "ISteamRemoteStorageGetCollectionDetailsFormData").

Results of requests can be obtained in two ways: parsing the response body manually, or configuring the result object in the `Request` before executing.

Requests are always sent using `format=json`, so response bodies are (or seem to be) always in JSON format. For manual parsing, checking "Content-Type" response header is recommended. When setting the result object in the `Request` (see `Request.SetResult`), a JSON-unmarshable object is expected.

Given that each response has a specific format and result structs cannot be automatically generated, decoding of a specific response is left to the user. When a response format is known, there will be a manually implemented struct with the appropriate fields. These result structs are named by concatenating the Interface struct name and the Method name (e.g.: "ISteamApps/GetAppList" -> "ISteamAppsGetAppList"). Look for these result structs for each Interface Method to see if there's any available at the time. Contributions to implement proper result structs are welcome!

Since not all similarly named Interfaces with different AppIDs are necessarily identical, the Interface/Method grouping can result in generated Interface struct methods that are not present in a given Interface. For example, given a (pseudo) schema:

{
  IUsers_100 : [GetName/v1, GetName/v2],
  IUsers_200 : [GetName/v2, GetAge/v1],
}

the resulting (pseudo) structure would be:

{
  IUsers(appID) : [GetName(version), GetAge()],
}

GetName and GetAge are generated struct methods of IUsers, regardless of AppID and version.

Accessing `IUsers(100).GetName(1)`, `IUsers(100).GetName(2)`, `IUsers(200).GetName(2)` and `IUsers(200).GetAge()` is valid. But accessing `IUsers(100).GetAge()` and `IUsers(200).GetName(1)` is invalid (returns error).

Available APIs

APIs that are available on different hosts and have different schemas are generated under self-contained sub-packages.

These sub-packages will contain their own `Client` struct that are thin wrappers around the base `Client`.

Currently, these are available:

+===============+=============+
| API           | sub-package |
+===============+=============+
| Steam Web API | steam       |
+---------------+-------------+
| Dota2 Web API | dota2       |
+---------------+-------------+

Official Documentation

Steam Web API documentation can be found at:

https://partner.steamgames.com/doc/webapi_overview

https://developer.valvesoftware.com/wiki/Steam_Web_API

https://wiki.teamfortress.com/wiki/WebAPI

Undocumented APIs

There are several Interfaces and Methods not present in the official API schema (returned by "ISteamWebAPIUtil/GetSupportedAPIList"), including game-specific schemas.

These undocumented Interfaces are fetched from third-party sources and are also generated along with the official one.

Specifications of undocumented Methods don't define any Parameters, so no validation is performed or any documentation is generated. More importantly, they also don't define the HTTP method to be used. For now, these Methods default to GET HTTP method.

When an Interface or Method originates from an undocumented source, they'll have a comment indicating it.

A comprehensive list of Interfaces and Methods, documented and undocumented, can be found at https://steamapi.xpaw.me

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrRequestNoInterface = errors.New("geyser: no Interface set in Request")
	ErrRequestNoMethod    = errors.New("geyser: no Method set in Request")
)

Functions

This section is empty.

Types

type Client

type Client interface {
	// Execute performs a Request.
	//
	// Returns `ErrRequestNoInterface` if `Request.Interface` is nil.
	// Returns `ErrRequestNoMethod` if `Request.Method` is nil.
	//
	// Before execution, it sets `Request.Time` to the current time.
	//
	// After execution, regardless of errors, it sets `Request.Raw` with the http.Request used to
	// perform the HTTP request.
	Execute(req *Request) (*Response, error)
	// SetCookies appends multiple HTTP cookies.
	// These cookies will be used with all requests sent by this Client.
	SetCookies(cookies []*http.Cookie) Client
	// SetCookieJar sets a custom http.CookieJar in Client.
	SetCookieJar(jar http.CookieJar) Client
	// SetDebug enables the debug mode on Client.
	// Debug mode logs details of every request and response.
	SetDebug(debug bool) Client
	// SetHeader sets a single header field and its value in Client.
	// This header field will be applied to all requests sent by this Client.
	SetHeader(key string, value string) Client
	// SetHeaders sets multiple header fields and their values in Client.
	// These header fields will be applied to all requests sent by this Client.
	SetHeaders(header http.Header) Client
	// HostURL returns the Client's base host URL.
	HostURL() string
	// SetHostURL sets Host URL in Client.
	// It will be used with requests sent by this client with relative URL.
	SetHostURL(host string) Client
	// SetLogger sets the logger in Client that will log request and response details.
	SetLogger(logger Logger) Client
	// SetProxy sets the Proxy URL and Port for Client.
	SetProxy(proxy string) Client
	// SetQueryParam sets a single parameter and its value in Client.
	// This parameter will be applied to all requests sent by this Client.
	SetQueryParam(key string, value string) Client
	// SetQueryParams sets multiple parameters and their values in Client.
	// These parameters will be applied to all requests sent by this Client.
	SetQueryParams(query url.Values) Client
	// SetRetryCount enables request retrying and allows to set the number of tries, using a backoff
	// algorithm.
	// Setting to 0 disables retrying.
	SetRetryCount(count int) Client
	// SetRetryMaxWaitTime sets max time to wait before retrying a request (default: 2s).
	SetRetryMaxWaitTime(maxWait time.Duration) Client
	// SetRetryWaitTime sets default time to wait before retrying a request (default: 100ms).
	SetRetryWaitTime(wait time.Duration) Client
	// SetTimeout sets the timeout for requests sent by this Client.
	SetTimeout(timeout time.Duration) Client
	// SetTransport sets a custom `*http.Transport` or any `http.RoundTripper` compatible interface
	// implementation to be used as the transport by this Client.
	SetTransport(transport http.RoundTripper) Client
}

Client is the API entry-point.

func New

func New(options ...ClientOption) (Client, error)

New creates a new Client with given options.

Example (Conditional)
// Conditional options
options := []geyser.ClientOption{
	geyser.WithKey("<api_key>"),
}

if os.Getenv("DEBUG") != "" {
	options = append(options, geyser.WithDebug())
}

_, _ = geyser.New(options...)
Output:

Example (Inline)
// Inline options
_, _ = geyser.New(
	geyser.WithUserAgent("mylib 1.2.3"),
	geyser.WithKey("<api_key>"),
	geyser.WithTimeout(3*time.Second),
	geyser.WithRetryCount(5),
)
Output:

type ClientOption

type ClientOption func(Client) error

ClientOption functions set options in `Client`.

func WithCookieJar

func WithCookieJar(jar http.CookieJar) ClientOption

WithCookieJar sets the cookie jar used with all requests.

func WithDebug

func WithDebug() ClientOption

WithDebug enables debug logging.

func WithHeaders

func WithHeaders(headers http.Header) ClientOption

WithHeaders sets the headers sent with all requests.

func WithHostURL

func WithHostURL(baseURL string) ClientOption

WithHostURL sets the host URL for all requests.

func WithKey

func WithKey(key string) ClientOption

WithKey sets the Steam API key ("key" parameter) for all requests.

func WithLanguage

func WithLanguage(lang string) ClientOption

WithLanguage sets the language ("language" parameter) for all requests.

func WithLogger

func WithLogger(logger Logger) ClientOption

WithLogger sets the logger.

func WithProxy

func WithProxy(proxy string) ClientOption

WithProxy sets the proxy.

func WithQueryParams

func WithQueryParams(query url.Values) ClientOption

WithQueryParams sets the query parameters sent with all requests.

func WithRetryCount

func WithRetryCount(retryCount int) ClientOption

WithRetryCount enables request retrying and allows to set the number of tries, using a backoff mechanism.

Setting to 0 disables retrying.

func WithRetryMaxWaitTime

func WithRetryMaxWaitTime(retryMaxWaitTime time.Duration) ClientOption

WithRetryMaxWaitTime sets the max time to wait before retrying a request.

Default is 2s.

func WithRetryWaitTime

func WithRetryWaitTime(retryWaitTime time.Duration) ClientOption

WithRetryWaitTime sets the default time to wait before retrying a request.

Default is 100ms.

func WithTimeout

func WithTimeout(timeout time.Duration) ClientOption

WithTimeout sets the timeout duration for each request.

func WithTransport

func WithTransport(transport http.RoundTripper) ClientOption

WithTransport sets the underlying HTTP transport used to perform requests.

func WithUserAgent

func WithUserAgent(userAgent string) ClientOption

WithUserAgent sets the "User-Agent" HTTP header for all requests.

type Logger

type Logger interface {
	Errorf(format string, v ...interface{})
	Warnf(format string, v ...interface{})
	Debugf(format string, v ...interface{})
}

Logger is the logger interface type accepted by `Client`.

type Request

type Request struct {
	// Schema Interface to request.
	Interface *schema.Interface
	// Schema Method to request.
	Method *schema.Method
	// Context in which the request is to be executed.
	Context context.Context
	// Header fields to be sent with the request.
	Header http.Header
	// Cookies to be sent with the request.
	Cookies []*http.Cookie
	// Query parameters to be sent in the request URL.
	Query url.Values
	// Values to be form-encoded in request body.
	Form url.Values
	// Raw request body.
	Body interface{}
	// Result object where the response will be deserialized into.
	Result interface{}
	// Underlying http.Request.
	// It's nil if the Request was not executed.
	Raw *http.Request
	// Time at which the request execution started.
	// It's zero Time if the Request was not executed.
	Time time.Time
}

Request represents an HTTP request to an Interface Method.

func NewRequest

func NewRequest(si *schema.Interface, sm *schema.Method) *Request

NewRequest creates a new Request for the given Interface and Method.

func (*Request) SetAPIKey

func (r *Request) SetAPIKey(v string) *Request

SetKey sets the "key" request parameter.

func (*Request) SetBody

func (r *Request) SetBody(b interface{}) *Request

SetBody sets the raw request body.

func (*Request) SetContext

func (r *Request) SetContext(ctx context.Context) *Request

SetContext sets the request context.

func (*Request) SetCookies

func (r *Request) SetCookies(cookies []*http.Cookie) *Request

SetCookies sets the HTTP cookies.

func (*Request) SetForm

func (r *Request) SetForm(f url.Values) *Request

SetForm sets the values to be form-encoded in request body.

func (*Request) SetHeader

func (r *Request) SetHeader(key, value string) *Request

SetHeader sets a single header field.

func (*Request) SetHeaders

func (r *Request) SetHeaders(header http.Header) *Request

SetHeaders sets multiple header fields.

func (*Request) SetInterface

func (r *Request) SetInterface(i *schema.Interface) *Request

SetInterface sets the Interface for the request.

func (*Request) SetLanguage

func (r *Request) SetLanguage(v string) *Request

SetLang sets the "language" request parameter.

func (*Request) SetMethod

func (r *Request) SetMethod(m *schema.Method) *Request

SetMethod sets the Interface Method for the request.

func (*Request) SetQueryParam

func (r *Request) SetQueryParam(key, value string) *Request

SetQueryParam sets a single query parameter.

func (*Request) SetQueryParams

func (r *Request) SetQueryParams(query url.Values) *Request

SetQueryParams sets multiple query parameters.

func (*Request) SetResult

func (r *Request) SetResult(result interface{}) *Request

SetResult sets the result object where the response will be deserialized into.

type Response

type Response struct {
	// Originating Request.
	Request *Request
	// Underlying http.Response.
	Raw *http.Response
	// Body is the raw response body reader.
	// It's nil if `Request.Result` is non-nil (body already read).
	// If non-nil, must be manually consumed and closed.
	Body io.ReadCloser
	// BodyData contains the response body data that was read to be deserialized into `Request.Result`.
	// It's nil if `Request.Result` is nil.
	BodyData []byte
	// Time at which the response was received.
	Time time.Time
	// contains filtered or unexported fields
}

Response represents an HTTP response.

func (*Response) Cookies

func (r *Response) Cookies() []*http.Cookie

Cookies returns the response cookies.

Shortcut for `Raw.Cookies()`. Returns nil if `Raw` is nil.

func (*Response) Duration

func (r *Response) Duration() time.Duration

Duration returns the duration since the request was made until receiving the response.

It's the result of `Time.Sub(Request.Time)`.

Returns zero if any of the following is true: `Time` is the zero Time, `Request` is nil, `Request.Time` is the zero Time.

func (*Response) Header

func (r *Response) Header() http.Header

Header returns the response header.

Shortcut for `Raw.Header`. Returns nil if `Raw` is nil.

func (*Response) IsError

func (r *Response) IsError() bool

IsError returns true if HTTP status `code >= 400` otherwise false.

func (*Response) IsSuccess

func (r *Response) IsSuccess() bool

IsSuccess returns true if HTTP status `code >= 200 and < 300` otherwise false.

func (*Response) Result

func (r *Response) Result() interface{}

Result returns the parsed result.

Shortcut for `Request.Result`.

func (*Response) Status

func (r *Response) Status() string

Status returns the HTTP status string for the executed request.

Shortcut for `Raw.Status`. Returns empty string if `Raw` is nil.

func (*Response) StatusCode

func (r *Response) StatusCode() int

StatusCode returns the HTTP status code for the executed request.

Shortcut for `Raw.StatusCode`. Returns 0 if `Raw` is nil.

Directories

Path Synopsis
Package dota2 implements an HTTP client for the (undocumented) Dota 2 Web API.
Package dota2 implements an HTTP client for the (undocumented) Dota 2 Web API.
Package schema represents the tree structure of an API schema.
Package schema represents the tree structure of an API schema.
Package steam implements an HTTP client for the Steam Web API.
Package steam implements an HTTP client for the Steam Web API.

Jump to

Keyboard shortcuts

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