hhru

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: May 12, 2026 License: MIT Imports: 14 Imported by: 0

README

go-hhru-api

Русский | English

Go Reference Go 1.26 CI

Клиент на Go для API HeadHunter: типизированные подклиенты из OpenAPI и фасад hhru.New с общими заголовками, опциональным OAuth Bearer, дефолтными query и ретраями на 429 / 503. Документация API пакета на pkg.go.dev (бейдж выше).

Дублируется в README.ru.md для явного имени локали — поддерживайте оба файла в актуальном состоянии.

Содержание

Зачем эта библиотека

  • Типы и методы под OpenAPI — меньше ручной сборки URL, query и заголовков; четыре сгенерированных клиента (работодатель, соискатель, публичное, приложение).
  • Единые правила HH — обязательный HH-User-Agent, при необходимости дефолтные host / locale, Bearer через TokenSource, опциональные ретраи при перегрузке API, опциональный лимит частоты запросов и хуки запроса/ответа.
  • Долгоживущие сервисыNewRefreshingTokenSource обновляет access-токен по refresh_token под мьютексом; hhru.New остаётся без состояния, передаёте один TokenSource.
  • Воспроизводимость — в репозитории зафиксированы api/openapi.yml и gen/; CI проверяет совпадение генерации с коммитом.

Установка

go get github.com/Zoomish/go-hhru-api

Быстрый старт

import (
    "context"
    "github.com/Zoomish/go-hhru-api"
    "github.com/Zoomish/go-hhru-api/gen/public"
)

c, err := hhru.New(hhru.Options{
    HHUserAgent: "MyService/1.0 (mailto:you@example.com)",
    DefaultHost: "hh.ru",
})
if err != nil {
    panic(err)
}
host := public.GetCountriesParamsHostHhRu
resp, err := c.Public.GetCountriesWithResponse(context.Background(), &public.GetCountriesParams{
    HHUserAgent: c.HHUserAgent(),
    Host:        &host,
})

Заголовок HH-User-Agent также подставляется редактором запроса, если его нет; во многих *Params поле HHUserAgent всё равно ожидается — используйте c.HHUserAgent().

Примеры go run

Из корня репозитория.

Справочники без OAuth:

go run ./examples/public_countries
go run ./examples/public_locales
go run ./examples/public_areas
go run ./examples/public_industries
go run ./examples/public_languages
go run ./examples/public_position_suggest
go run ./examples/public_position_suggest -text "backend engineer"

Опции клиента (DefaultHost, DefaultLocale, MaxRetries):

go run ./examples/custom_options

OAuth:

export HH_CLIENT_ID=… HH_CLIENT_SECRET=…
go run ./examples/app_token
export HH_CLIENT_ID=… HH_CLIENT_SECRET=…
export HH_INITIAL_TOKEN_JSON_PATH=/path/to/token.json
go run ./examples/refreshing_token

Файл токена должен соответствовать TokenResponse: access_token, refresh_token, expires_in после OAuth пользователя. Флаги -hh-user-agent или переменная HH_USER_AGENT поддерживаются в примерах.

Токен приложения (client credentials)

tok, err := hhru.ExchangeClientCredentials(ctx, http.DefaultClient,
    hhru.TokenEndpoint(hhru.DefaultBaseURL),
    "MyService/1.0 (mailto:you@example.com)",
    clientID, clientSecret,
)
c, err := hhru.New(hhru.Options{
    HHUserAgent: "MyService/1.0 (mailto:you@example.com)",
    TokenSource: hhru.AccessToken(tok.AccessToken),
})

Обновление через ExchangeRefreshToken с grant_type=refresh_token (при необходимости client_id / client_secret).

Пользовательский токен и автообновление

После OAuth обычно есть access_token, refresh_token, expires_in. Передайте первый ответ в NewRefreshingTokenSource и используйте как TokenSource в hhru.New.

initial := &hhru.TokenResponse{
    AccessToken:  accessFromOAuth,
    RefreshToken: refreshFromOAuth,
    ExpiresIn:    expiresInSeconds,
}
ts, err := hhru.NewRefreshingTokenSource(http.DefaultClient,
    hhru.TokenEndpoint(hhru.DefaultBaseURL),
    "MyService/1.0 (mailto:you@example.com)",
    clientID, clientSecret,
    initial,
)
if err != nil {
    panic(err)
}
c, err := hhru.New(hhru.Options{
    HHUserAgent: "MyService/1.0 (mailto:you@example.com)",
    TokenSource: ts,
})

Токены только client_credentials не обновляются этим путём; перевыпускайте приложение через AccessToken.

Опции: ретраи и лимиты

  • Options.MaxRetries — ретраи для безопасных запросов на 429 / 503 с учётом Retry-After и экспоненциальной задержкой; при заданных RetryBackoffMin / RetryBackoffMax длительность сна ограничивается этим интервалом.
  • Options.MaxRequestsPerSecond — мягкий лимит частоты исходящих запросов (равномерный интервал между стартами запросов).
  • Options.RequestHook / Options.ResponseHook — хуки до отправки и после ответа (тело ответа не читается; удобно для логов и трассировки).

Пагинация

PagesUntil вызывает page = start, start+1, …, пока колбэк не вернёт continueNext == false или ошибку.

Наблюдаемость и ошибки API

Мейнтейнерам: генерация gen/

Нужны Go и сеть для загрузки модулей инструментов:

make generate

См. CONTRIBUTING.md про тесты и тег integration. Спека: api/openapi.yml. CI: .github/workflows/.

Интеграционные тесты

  • go test -short ./... — быстрая проверка без живых вызовов HH (token_refresh_test.go на httptest).
  • Живой API: go test -tags=integration -timeout 5m ./integration (без -short). С HH_TEST_CLIENT_ID и HH_TEST_CLIENT_SECRET из dev.hh.ru дополнительно выполняется OAuth-сценарий.
  • Вручную: .github/workflows/integration-tests.yml (workflow_dispatch).

Версионирование

После v1 — семантическое версионирование. До этого минорные релизы при изменении openapi.yml / gen/ могут ломать совместимость. См. CHANGELOG.md.

Публикация: push тега v* запускает .github/workflows/release.yml: те же проверки, что и в CI (composite action), затем GitHub Release. Подробности в CONTRIBUTING.md.

Documentation

Overview

Пакет hhru — типизированный HTTP-клиент для API HeadHunter (api.hh.ru).

Вызовите New с заполненным Options.HHUserAgent: имя приложения и контактный e-mail, как требует HH. Опциональный [Options.TokenSource] добавляет Bearer ко всем запросам. Для токена приложения используйте ExchangeClientCredentials и AccessToken. Для пользовательского OAuth с refresh_token — NewRefreshingTokenSource или NewRefreshingTokenSourceWithOptions с Clock в тестах.

Сгенерированные подклиенты (по разбиению публичной OpenAPI) лежат в пакетах:

На Client они доступны как Employer, Applicant, Public и App.

Надёжность и наблюдаемость через Options: [Options.MaxRetries], пределы паузы [Options.RetryBackoffMin] и [Options.RetryBackoffMax], ограничение частоты [Options.MaxRequestsPerSecond], хуки [Options.RequestHook] и [Options.ResponseHook]. Разбор JSON-ошибок API — ParseAPIError.

Запускаемые примеры — в каталоге examples/ (см. README репозитория). Живые HTTP-тесты — с тегом сборки "integration" в пакете integration/.

Официальная документация OpenAPI: https://api.hh.ru/openapi/redoc

Index

Examples

Constants

View Source
const DefaultBaseURL = "https://api.hh.ru"

Variables

This section is empty.

Functions

func PagesUntil

func PagesUntil(ctx context.Context, startPage int, fn func(ctx context.Context, page int) (continueNext bool, err error)) error
Example
ctx := context.Background()
sum := 0
_ = hhru.PagesUntil(ctx, 1, func(ctx context.Context, page int) (bool, error) {
	sum += page
	if page >= 3 {
		return false, nil
	}
	return true, nil
})
fmt.Println(sum)
Output:
6

func TokenEndpoint

func TokenEndpoint(base string) string
Example
u := hhru.TokenEndpoint(hhru.DefaultBaseURL)
fmt.Println(u)
Output:
https://api.hh.ru/token

Types

type APIError

type APIError struct {
	StatusCode int
	RequestID  string
	RawBody    []byte
}

func ParseAPIError

func ParseAPIError(statusCode int, body []byte) *APIError
Example
e := hhru.ParseAPIError(400, []byte(`{"request_id":"rid"}`))
fmt.Println(e.RequestID)
Output:
rid

func (*APIError) Error

func (e *APIError) Error() string

type Client

type Client struct {
	Employer  *employer.ClientWithResponses
	Applicant *applicant.ClientWithResponses
	Public    *public.ClientWithResponses
	App       *app.ClientWithResponses
	// contains filtered or unexported fields
}

func New

func New(opts Options) (*Client, error)
Example
_, err := hhru.New(hhru.Options{
	HHUserAgent: "MyBot/1.0 (mailto:you@example.com)",
})
fmt.Println(err == nil)
Output:
true

func (*Client) HHUserAgent

func (c *Client) HHUserAgent() string

type Clock

type Clock interface {
	Now() time.Time
}

type Options

type Options struct {
	HTTPClient *http.Client
	BaseURL    string

	HHUserAgent string

	DefaultHost   string
	DefaultLocale string

	TokenSource TokenSource

	MaxRetries int

	RetryBackoffMin time.Duration
	RetryBackoffMax time.Duration

	MaxRequestsPerSecond float64

	RequestHook  func(ctx context.Context, req *http.Request) error
	ResponseHook func(ctx context.Context, resp *http.Response)
}

type RefreshingSourceOptions

type RefreshingSourceOptions struct {
	Clock Clock
}

type RefreshingTokenSource

type RefreshingTokenSource struct {
	HTTPClient   *http.Client
	TokenURL     string
	HHUserAgent  string
	ClientID     string
	ClientSecret string
	// contains filtered or unexported fields
}

func (*RefreshingTokenSource) Token

type TokenResponse

type TokenResponse struct {
	AccessToken  string `json:"access_token"`
	TokenType    string `json:"token_type"`
	ExpiresIn    int    `json:"expires_in"`
	RefreshToken string `json:"refresh_token"`
}

func ExchangeClientCredentials

func ExchangeClientCredentials(ctx context.Context, c *http.Client, tokenURL, hhUserAgent, clientID, clientSecret string) (*TokenResponse, error)

func ExchangeRefreshToken

func ExchangeRefreshToken(ctx context.Context, c *http.Client, tokenURL, hhUserAgent, refreshToken, clientID, clientSecret string) (*TokenResponse, error)

type TokenSource

type TokenSource interface {
	Token(ctx context.Context) (string, error)
}

func AccessToken

func AccessToken(token string) TokenSource
Example
ts := hhru.AccessToken("opaque")
tok, _ := ts.Token(context.Background())
fmt.Println(tok)
Output:
opaque

func NewRefreshingTokenSource

func NewRefreshingTokenSource(httpClient *http.Client, tokenURL, hhUserAgent, clientID, clientSecret string, initial *TokenResponse) (TokenSource, error)
Example
ts, err := hhru.NewRefreshingTokenSource(nil,
	hhru.TokenEndpoint(hhru.DefaultBaseURL),
	"MyBot/1.0 (mailto:you@example.com)",
	"client-id", "client-secret",
	&hhru.TokenResponse{
		AccessToken:  "initial-access",
		RefreshToken: "refresh-token",
		ExpiresIn:    3600,
	},
)
if err != nil {
	fmt.Println("err")
	return
}
tok, err := ts.Token(context.Background())
if err != nil || tok == "" {
	fmt.Println("bad")
	return
}
fmt.Println("ok")
Output:
ok

func NewRefreshingTokenSourceWithOptions

func NewRefreshingTokenSourceWithOptions(httpClient *http.Client, tokenURL, hhUserAgent, clientID, clientSecret string, initial *TokenResponse, opts RefreshingSourceOptions) (TokenSource, error)

Directories

Path Synopsis
examples
app_token command
custom_options command
public_areas command
public_locales command
gen
app
Package app provides primitives to interact with the openapi HTTP API.
Package app provides primitives to interact with the openapi HTTP API.
applicant
Package applicant provides primitives to interact with the openapi HTTP API.
Package applicant provides primitives to interact with the openapi HTTP API.
employer
Package employer provides primitives to interact with the openapi HTTP API.
Package employer provides primitives to interact with the openapi HTTP API.
public
Package public provides primitives to interact with the openapi HTTP API.
Package public provides primitives to interact with the openapi HTTP API.
Package integration holds optional live-API tests; run with -tags=integration.
Package integration holds optional live-API tests; run with -tags=integration.
scripts
generate command
split-openapi command

Jump to

Keyboard shortcuts

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