Forked and cleaned.

This commit is contained in:
Alexander Zhuravlev 2019-04-08 14:49:07 +03:00
parent 09353bd310
commit 8ea8fbb39f
21 changed files with 1266 additions and 2 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# Created by .ignore support plugin (hsz.mobi)
/.idea

21
.travis.yml Normal file
View File

@ -0,0 +1,21 @@
language: go
go:
- "1.10.x"
- "1.11.x"
- "1.12.x"
- tip
#before_script:
# # coverage reporter
# - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
# - chmod +x ./cc-test-reporter
# - ./cc-test-reporter before-build
script:
# - go test -v -race -coverprofile=coverage.out -covermode=atomic
- go test -v -race
#after_script:
# - ./cc-test-reporter format-coverage -t gocov coverage.out
# - ./cc-test-reporter upload-coverage

View File

@ -1,2 +1,54 @@
# dadata
Golang client for DaData.ru
# Client for DaData.ru
Forked from https://github.com/webdeskltd/dadata.
[![Build Status](https://travis-ci.org/ekomobile/dadata.svg)](https://travis-ci.org/ekomobile/dadata)
[![GitHub release](https://img.shields.io/github/release/ekomobile/dadata.svg)](https://github.com/ekomobile/dadata/releases)
[![Go Report Card](https://goreportcard.com/badge/github.com/ekomobile/dadata)](https://goreportcard.com/report/github.com/ekomobile/dadata)
[![GoDoc](https://godoc.org/github.com/ekomobile/dadata?status.svg)](https://godoc.org/github.com/ekomobile/dadata)
DaData API v2
Implemented [Clean](https://dadata.ru/api/clean/) and [Suggest](https://dadata.ru/api/suggest/) methods.
## Installation
`go get github.com/ekomobile/dadata`
## Usage
```go
package main
import (
"fmt"
"github.com/ekomobile/dadata"
)
func main() {
// By default client gets keys from `DADATA_API_KEY` and `DADATA_SECRET_KEY` environment variables.
daData := dadata.NewClient()
banks, err := daData.SuggestBanks(dadata.SuggestRequestParams{Query: "Кредитный", Count: 3})
if nil != err {
fmt.Println(err)
}
for _, bank := range banks {
fmt.Println(bank.Data.Name.Full)
fmt.Println(bank.Data.Bic)
}
// Output:
// "МОСКОВСКИЙ КРЕДИТНЫЙ БАНК" (ПУБЛИЧНОЕ АКЦИОНЕРНОЕ ОБЩЕСТВО)
// 044525659
// КОММЕРЧЕСКИЙ БАНК "РЕСПУБЛИКАНСКИЙ КРЕДИТНЫЙ АЛЬЯНС" (ОБЩЕСТВО С ОГРАНИЧЕННОЙ ОТВЕТСТВЕННОСТЬЮ)
// 044525860
// ЖИЛИЩНО-КРЕДИТНЫЙ КОММЕРЧЕСКИЙ БАНК "ЖИЛКРЕДИТ" ОБЩЕСТВО С ОГРАНИЧЕННОЙ ОТВЕТСТВЕННОСТЬЮ
// 044525325
}
```
## Licence
MIT see [LICENSE](LICENSE)

21
balance.go Normal file
View File

@ -0,0 +1,21 @@
package dadata
import (
"context"
)
// ProfileBalance return daily statistics
// see documentation https://dadata.ru/api/stat/
func (c *Client) ProfileBalance() (*BalanceResponse, error) {
return c.ProfileBalanceWithCtx(context.Background())
}
// ProfileBalanceWithCtx return daily statistics
// see documentation https://dadata.ru/api/stat/
func (c *Client) ProfileBalanceWithCtx(ctx context.Context) (result *BalanceResponse, err error) {
result = new(BalanceResponse)
if err = c.sendRequestToURL(ctx, "GET", c.options.baseURL+"profile/balance", nil, result); err != nil {
result = nil
}
return
}

112
clean.go Normal file
View File

@ -0,0 +1,112 @@
package dadata
import "context"
func (c *Client) sendCleanRequest(ctx context.Context, lastURLPart string, source, result interface{}) error {
return c.sendRequest(ctx, "clean/"+lastURLPart, source, result)
}
// CleanAddresses clean all provided addresses
// Call https://dadata.ru/api/v2/clean/address
func (c *Client) CleanAddresses(sourceAddresses ...string) ([]Address, error) {
return c.CleanAddressesWithCtx(context.Background(), sourceAddresses...)
}
// CleanAddressesWithCtx clean all provided addresses
// Call https://dadata.ru/api/v2/clean/address
func (c *Client) CleanAddressesWithCtx(ctx context.Context, sourceAddresses ...string) (addresses []Address, err error) {
if err = c.sendCleanRequest(ctx, "address", &sourceAddresses, &addresses); err != nil {
addresses = nil
}
return
}
// CleanPhones clean all provided phones
// Call https://dadata.ru/api/v2/clean/phone
func (c *Client) CleanPhones(sourcePhones ...string) ([]Phone, error) {
return c.CleanPhonesWithCtx(context.Background(), sourcePhones...)
}
// CleanPhonesWithCtx clean all provided phones
// Call https://dadata.ru/api/v2/clean/phone
func (c *Client) CleanPhonesWithCtx(ctx context.Context, sourcePhones ...string) (phones []Phone, err error) {
if err = c.sendCleanRequest(ctx, "phone", &sourcePhones, &phones); err != nil {
phones = nil
}
return
}
// CleanNames clean all provided names
// Call https://dadata.ru/api/v2/clean/name
func (c *Client) CleanNames(sourceNames ...string) ([]Name, error) {
return c.CleanNamesWithCtx(context.Background(), sourceNames...)
}
// CleanNamesWithCtx clean all provided names
// Call https://dadata.ru/api/v2/clean/name
func (c *Client) CleanNamesWithCtx(ctx context.Context, sourceNames ...string) (names []Name, err error) {
if err = c.sendCleanRequest(ctx, "name", &sourceNames, &names); err != nil {
names = nil
}
return
}
// CleanEmails clean all provided emails
// Call https://dadata.ru/api/v2/clean/email
func (c *Client) CleanEmails(sourceEmails ...string) ([]Email, error) {
return c.CleanEmailsWithCtx(context.Background(), sourceEmails...)
}
// CleanEmailsWithCtx clean all provided emails
// Call https://dadata.ru/api/v2/clean/email
func (c *Client) CleanEmailsWithCtx(ctx context.Context, sourceEmails ...string) (emails []Email, err error) {
if err = c.sendCleanRequest(ctx, "email", &sourceEmails, &emails); err != nil {
emails = nil
}
return
}
// CleanBirthdates clean all provided birthdates
// Call https://dadata.ru/api/v2/clean/birthdate
func (c *Client) CleanBirthdates(sourceBirthdates ...string) ([]Birthdate, error) {
return c.CleanBirthdatesWithCtx(context.Background(), sourceBirthdates...)
}
// CleanBirthdatesWithCtx clean all provided birthdates
// Call https://dadata.ru/api/v2/clean/birthdate
func (c *Client) CleanBirthdatesWithCtx(ctx context.Context, sourceBirthdates ...string) (birthdates []Birthdate, err error) {
if err = c.sendCleanRequest(ctx, "birthdate", &sourceBirthdates, &birthdates); err != nil {
birthdates = nil
}
return
}
// CleanVehicles clean all provided vehicles
// Call https://dadata.ru/api/v2/clean/vehicle
func (c *Client) CleanVehicles(sourceVehicles ...string) ([]Vehicle, error) {
return c.CleanVehiclesWithCtx(context.Background(), sourceVehicles...)
}
// CleanVehiclesWithCtx clean all provided vehicles
// Call https://dadata.ru/api/v2/clean/vehicle
func (c *Client) CleanVehiclesWithCtx(ctx context.Context, sourceVehicles ...string) (vehicles []Vehicle, err error) {
if err = c.sendCleanRequest(ctx, "vehicle", &sourceVehicles, &vehicles); err != nil {
vehicles = nil
}
return
}
// CleanPassports clean all provided passports
// Call https://dadata.ru/api/v2/clean/passport
func (c *Client) CleanPassports(sourcePassports ...string) ([]Passport, error) {
return c.CleanPassportsWithCtx(context.Background(), sourcePassports...)
}
// CleanPassportsWithCtx clean all provided passports
// Call https://dadata.ru/api/v2/clean/passport
func (c *Client) CleanPassportsWithCtx(ctx context.Context, sourcePassports ...string) (passports []Passport, err error) {
if err = c.sendCleanRequest(ctx, "passport", &sourcePassports, &passports); err != nil {
passports = nil
}
return
}

38
clean_test.go Normal file
View File

@ -0,0 +1,38 @@
package dadata
import (
"fmt"
)
func ExampleClient_CleanAddresses() {
// By default client uses `DADATA_API_KEY` and `DADATA_SECRET_KEY` environment variables.
daData := NewClient()
// Or credentials may be passed as client option.
// daData := NewClient(WithCredentialProvider("API_KEY", "SECRET_KEY"))
addresses, err := daData.CleanAddresses("ул.Правды 26", "пер.Расковой 5")
if nil != err {
fmt.Println(err)
}
for _, address := range addresses {
fmt.Println(address.StreetTypeFull)
fmt.Println(address.Street)
fmt.Println(address.House)
}
}
func ExampleClient_CleanNames() {
daData := NewClient()
names, err := daData.CleanNames("Алексей Иванов", "Иван Алексеев")
if nil != err {
fmt.Println(err)
}
for _, name := range names {
fmt.Println(name.Surname)
fmt.Println(name.Name)
}
}

164
client.go Normal file
View File

@ -0,0 +1,164 @@
// Golang client library for DaData.ru (https://dadata.ru/).
// Package dadata implemented cleaning (https://dadata.ru/api/clean/) and suggesting (https://dadata.ru/api/suggest/)
package dadata
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"os"
)
const (
defaultBaseURL = "https://dadata.ru/api/v2/"
defaultBaseSuggestURL = "https://suggestions.dadata.ru/suggestions/api/4_1/rs/"
)
type (
Client struct {
options clientOptions
}
CredentialProvider interface {
ApiKey() string
SecretKey() string
}
Credentials struct {
ApiKeyValue string
SecretKeyValue string
}
EnvironmentCredentials struct {
ApiKeyName string
SecretKeyName string
}
clientOptions struct {
httpClient *http.Client
credentialProvider CredentialProvider
baseURL string
baseSuggestURL string
}
ClientOption func(opts *clientOptions)
)
// NewClient Create new client of DaData.
// Api and secret keys see on profile page (https://dadata.ru/profile/).
// By default client uses `DADATA_API_KEY` and `DADATA_SECRET_KEY` environment variables.
func NewClient(opts ...ClientOption) *Client {
options := clientOptions{
httpClient: http.DefaultClient,
credentialProvider: &EnvironmentCredentials{
ApiKeyName: "DADATA_API_KEY",
SecretKeyName: "DADATA_SECRET_KEY",
},
baseURL: defaultBaseURL,
baseSuggestURL: defaultBaseSuggestURL,
}
applyOptions(&options, opts...)
return &Client{
options: options,
}
}
func (c *Client) sendRequestToURL(ctx context.Context, method, url string, source interface{}, result interface{}) error {
if err := ctx.Err(); err != nil {
return fmt.Errorf("sendRequestToURL: ctx.Err return err=%v", err)
}
buffer := &bytes.Buffer{}
if err := json.NewEncoder(buffer).Encode(source); err != nil {
return fmt.Errorf("sendRequestToURL: json.Encode return err = %v", err)
}
request, err := http.NewRequest(method, url, buffer)
if err != nil {
return fmt.Errorf("sendRequestToURL: http.NewRequest return err = %v", err)
}
request = request.WithContext(ctx)
request.Header.Add("Authorization", fmt.Sprintf("Token %s", c.options.credentialProvider.ApiKey()))
request.Header.Add("X-Secret", c.options.credentialProvider.SecretKey())
request.Header.Add("Content-Type", "application/json")
request.Header.Add("Accept", "application/json")
response, err := c.options.httpClient.Do(request)
if err != nil {
return fmt.Errorf("sendRequestToURL: httpClient.Do return err = %v", err)
}
defer response.Body.Close()
if http.StatusOK != response.StatusCode {
return fmt.Errorf("sendRequestToURL: Request error %v", response.Status)
}
if err = json.NewDecoder(response.Body).Decode(&result); err != nil {
return fmt.Errorf("sendRequestToURL: json.Decode return err = %v", err)
}
return nil
}
// sendRequest
func (c *Client) sendRequest(ctx context.Context, lastURLPart string, source interface{}, result interface{}) error {
return c.sendRequestToURL(ctx, "POST", c.options.baseURL+lastURLPart, source, result)
}
func WithHttpClient(c *http.Client) ClientOption {
return func(opts *clientOptions) {
opts.httpClient = c
}
}
// WithCredentialProvider sets credential provider.
func WithCredentialProvider(c CredentialProvider) ClientOption {
return func(opts *clientOptions) {
opts.credentialProvider = c
}
}
func WithBaseURL(url string) ClientOption {
return func(opts *clientOptions) {
opts.baseURL = url
}
}
func WithBaseSuggestURL(url string) ClientOption {
return func(opts *clientOptions) {
opts.baseSuggestURL = url
}
}
func applyOptions(options *clientOptions, opts ...ClientOption) {
options = &clientOptions{}
for _, o := range opts {
o(options)
}
}
func (c *Credentials) ApiKey() string {
return c.ApiKeyValue
}
func (c *Credentials) SecretKey() string {
return c.SecretKeyValue
}
func (c *EnvironmentCredentials) ApiKey() string {
return os.Getenv(c.ApiKeyName)
}
func (c *EnvironmentCredentials) SecretKey() string {
return os.Getenv(c.SecretKeyName)
}

13
client_test.go Normal file
View File

@ -0,0 +1,13 @@
package dadata
import (
"testing"
)
func TestNewDaData(t *testing.T) {
daData := NewClient()
if daData == nil {
t.Errorf(`NewClient return nil`)
}
}

59
constants.go Normal file
View File

@ -0,0 +1,59 @@
package dadata
// Определите, нужна ли дополнительная проверка оператором, используя код качества (qc):
const (
QcSuccess = 0 // Исходное значение распознано уверенно. Не требуется ручная проверка.
QcFailure = 1 // Исходное значение распознано с допущениями или не распознано. Требуется ручная проверка.
)
// Определите пригодность к рассылке, используя код полноты адреса (qc_complete):
const (
QcCompleteSuitable = 0 // Пригоден для почтовой рассылки
QcCompleteNoRegion = 1 // Не пригоден, нет региона
QcCompleteNoCity = 2 // Не пригоден, нет города
QcCompleteNoStreet = 3 // Не пригоден, нет улицы
QcCompleteNotHome = 4 // Не пригоден, нет дома
QcCompleteNoApartment = 5 // Пригоден для юридических лиц или частных владений (нет квартиры)
QcCompleteNotSuitable = 6 // Не пригоден
QcCompleteCompleteForeignAddress = 7 // Иностранный адрес
QcCompleteCompleteNoKLADR = 10 // Пригоден, но низкая вероятность успешной доставки (дом не найден в КЛАДР)
)
// Определите вероятность успешной доставки письма по адресу, используя код проверки дома (qc_house):
const (
QcHouseExactMatch = 2 // Дом найден по точному совпадению (КЛАДР) Высокая
QcHouseNotExpansionMatch = 3 // Различие в расширении дома (КЛАДР) Средняя
QcHouseRangeMatch = 4 // Дом найден по диапазону (КЛАДР) Средняя
QcHouseNotFound = 10 // Дом не найден (КЛАДР) Низкая
)
// Определите точность координат адреса доставки с помощью кода qc_geo:
const (
QcGeoExactCoordinates = 0 // Точные координаты
QcGeoNearestHouse = 1 // Ближайший дом
QcGeoStreet = 2 // Улица
QcGeoLocality = 3 // Населенный пункт
QcGeoCity = 4 // Город
QcGeoNotDetermined = 5 // Координаты не определены
)
// Проверьте, указал ли клиент телефон, соответствующий его адресу, с помощью кода qc_conflict (удобно для проверки уровня риска):
const (
QcConflictFullMath = 0 // Телефон соответствует адресу
QcConflictCityMath = 2 // Города адреса и телефона отличаются
QcConflictRegionMath = 3 // Регионы адреса и телефона отличаются
)
// BoundValue type wrapper for suggest bounds
// full documentation https://confluence.hflabs.ru/pages/viewpage.action?pageId=222888017
type BoundValue string
// const for SuggestBound
const (
SuggestBoundRegion BoundValue = "region" // Регион
SuggestBoundArea BoundValue = "area" // Район
SuggestBoundCity BoundValue = "city" // Город
SuggestBoundSettlement BoundValue = "settlement" // Населенный пункт
SuggestBoundStreet BoundValue = "street" // Улица
SuggestBoundHouse BoundValue = "house" // Дом
)

84
find_by_id.go Normal file
View File

@ -0,0 +1,84 @@
package dadata
import (
"context"
"fmt"
)
// AddressByID find address by Fias or Kladr
// see full documentation https://confluence.hflabs.ru/pages/viewpage.action?pageId=312016944
func (c *Client) AddressByID(id string) (*ResponseAddress, error) {
return c.AddressByIDWithCtx(context.Background(), id)
}
// AddressByIDWithCtx find address by Fias or Kladr
// see full documentation https://confluence.hflabs.ru/pages/viewpage.action?pageId=312016944
func (c *Client) AddressByIDWithCtx(ctx context.Context, id string) (address *ResponseAddress, err error) {
var result []ResponseAddress
if result, err = c.AddressesByIDWithCtx(ctx, id); err != nil {
return
}
address = &result[0]
return
}
// AddressesByID find addresses by Fias or Kladr
// see full documentation https://confluence.hflabs.ru/pages/viewpage.action?pageId=312016944
func (c *Client) AddressesByID(id string) ([]ResponseAddress, error) {
return c.AddressesByIDWithCtx(context.Background(), id)
}
// AddressesByIDWithCtx find addresses by Fias or Kladr
// see full documentation https://confluence.hflabs.ru/pages/viewpage.action?pageId=312016944
func (c *Client) AddressesByIDWithCtx(ctx context.Context, id string) (addresses []ResponseAddress, err error) {
var result = &SuggestAddressResponse{}
var req = SuggestRequestParams{Query: id}
if err = c.sendRequestToURL(ctx, "POST", c.options.baseSuggestURL+"findById/address", req, result); err != nil {
return
}
if len(result.Suggestions) == 0 {
err = fmt.Errorf("dadata.AddressByID: cannot detect address by id %s", id)
return
}
addresses = result.Suggestions
return
}
// CountryByID find country by ID
func (c *Client) CountryByID(id string) (*ResponseCountry, error) {
return c.CountryByIDWithCtx(context.Background(), id)
}
// CountryByIDWithCtx find country by ID
func (c *Client) CountryByIDWithCtx(ctx context.Context, id string) (country *ResponseCountry, err error) {
var result []ResponseCountry
if result, err = c.CountriesByIDWithCtx(ctx, id); err != nil {
return
}
country = &result[0]
return
}
// CountriesByID find countries by ID
func (c *Client) CountriesByID(id string) ([]ResponseCountry, error) {
return c.CountriesByIDWithCtx(context.Background(), id)
}
// CountriesByIDWithCtx find countries by ID
func (c *Client) CountriesByIDWithCtx(ctx context.Context, id string) (addresses []ResponseCountry, err error) {
var result = &SuggestCountryResponse{}
var req = SuggestRequestParams{Query: id}
if err = c.sendRequestToURL(ctx, "POST", c.options.baseSuggestURL+"findById/country", req, result); err != nil {
return
}
if len(result.Suggestions) == 0 {
err = fmt.Errorf("dadata.CountryByID: cannot detect country by id %s", id)
return
}
addresses = result.Suggestions
return
}

16
find_by_id_test.go Normal file
View File

@ -0,0 +1,16 @@
package dadata
import (
"fmt"
)
func ExampleClient_AddressByID() {
daData := NewClient()
addr, err := daData.AddressByID("6300000100000")
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("City: %s,\nFiasID: %s,\nKladr: %s\n", addr.Data.City, addr.Data.FiasID, addr.Data.KladrID)
}

32
geoip.go Normal file
View File

@ -0,0 +1,32 @@
package dadata
import (
"context"
"fmt"
)
// GeoIP try to find address by IP
// see documentation on:
// https://dadata.ru/api/detect_address_by_ip/
// https://confluence.hflabs.ru/pages/viewpage.action?pageId=715096277
// ip string representation of ip-address (example 10.12.44.23)
// if ip=="" then dadata try to get ip-address from X-Forwarded-For header
func (c *Client) GeoIP(ip string) (*GeoIPResponse, error) {
return c.GeoIPWithCtx(context.Background(), ip)
}
// GeoIPWithCtx try to find address by IP
// see documentation on:
// https://dadata.ru/api/detect_address_by_ip/
// https://confluence.hflabs.ru/pages/viewpage.action?pageId=715096277
// ip string representation of ip-address (example 10.12.44.23)
// if ip=="" then dadata try to get ip-address from X-Forwarded-For header
func (c *Client) GeoIPWithCtx(ctx context.Context, ip string) (result *GeoIPResponse, err error) {
result = &GeoIPResponse{}
if err = c.sendRequestToURL(ctx, "GET", c.options.baseSuggestURL+"detectAddressByIp?ip="+ip, nil, &result); err != nil {
result = nil
} else if result.Location == nil {
result, err = nil, fmt.Errorf("dadata.GeoIP: cannot detect address by ip %s", ip)
}
return
}

23
geoip_test.go Normal file
View File

@ -0,0 +1,23 @@
package dadata
import (
"fmt"
)
func ExampleDaData_GeoIP() {
daData := NewClient()
geoIPResponse, err := daData.GeoIP("83.220.54.223")
if nil != err {
fmt.Println(err)
return
}
if geoIPResponse.Location == nil {
fmt.Println("empty result from GeoIP")
return
}
address := geoIPResponse.Location.Data
fmt.Println(address.Country)
fmt.Println(address.City)
fmt.Printf("see on https://www.google.com/maps/@%s,%sf,14z\n", address.GeoLat, address.GeoLon)
}

6
geoip_types.go Normal file
View File

@ -0,0 +1,6 @@
package dadata
// GeoIPResponse response for GeoIP
type GeoIPResponse struct {
Location *ResponseAddress `json:"location"`
}

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module github.com/ekomobile/dadata
go 1.12

0
go.sum Normal file
View File

308
model.go Normal file
View File

@ -0,0 +1,308 @@
package dadata
// Address base struct for datdata.Address
type Address struct {
Source string `json:"source"` // Исходный адрес одной строкой
Result string `json:"result"` // Стандартизованный адрес одной строкой
PostalCode string `json:"postal_code"` // Индекс
Country string `json:"country"` // Страна
RegionFiasID string `json:"region_fias_id"` // Код ФИАС региона
RegionKladrID string `json:"region_kladr_id"` // Код КЛАДР региона
RegionWithType string `json:"region_with_type"` // Регион с типом
RegionType string `json:"region_type"` // Тип региона (сокращенный)
RegionTypeFull string `json:"region_type_full"` // Тип региона
Region string `json:"region"` // Регион
AreaFiasID string `json:"area_fias_id"` // Код ФИАС района в регионе
AreaKladrID string `json:"area_kladr_id"` // Код КЛАДР района в регионе
AreaWithType string `json:"area_with_type"` // Район в регионе с типом
AreaType string `json:"area_type"` // Тип района в регионе (сокращенный)
AreaTypeFull string `json:"area_type_full"` // Тип района в регионе
Area string `json:"area"` // Район в регионе
CityFiasID string `json:"city_fias_id"` // Код ФИАС города
CityKladrID string `json:"city_kladr_id"` // Код КЛАДР города
CityWithType string `json:"city_with_type"` // Город с типом
CityType string `json:"city_type"` // Тип города (сокращенный)
CityTypeFull string `json:"city_type_full"` // Тип города
City string `json:"city"` // Город
CityArea string `json:"city_area"` // Административный округ (только для Москвы)
CityDistrictFiasID string `json:"city_district_fias_id"` // Код ФИАС района города (заполняется, только если район есть в ФИАС)
CityDistrictKladrID string `json:"city_district_kladr_id"` // Код КЛАДР района города (не заполняется)
CityDistrictWithType string `json:"city_district_with_type"` // Район города с типом
CityDistrictType string `json:"city_district_type"` // Тип района города (сокращенный)
CityDistrictTypeFull string `json:"city_district_type_full"` // Тип района города
CityDistrict string `json:"city_district"` // Район города
SettlementFiasID string `json:"settlement_fias_id"` // Код ФИАС нас. пункта
SettlementKladrID string `json:"settlement_kladr_id"` // Код КЛАДР нас. пункта
SettlementWithType string `json:"settlement_with_type"` // Населенный пункт с типом
SettlementType string `json:"settlement_type"` // Тип населенного пункта (сокращенный)
SettlementTypeFull string `json:"settlement_type_full"` // Тип населенного пункта
Settlement string `json:"settlement"` // Населенный пункт
StreetFiasID string `json:"street_fias_id"` // Код ФИАС улицы
StreetKladrID string `json:"street_kladr_id"` // Код КЛАДР улицы
StreetWithType string `json:"street_with_type"` // Улица с типом
StreetType string `json:"street_type"` // Тип улицы (сокращенный)
StreetTypeFull string `json:"street_type_full"` // Тип улицы
Street string `json:"street"` // Улица
HouseFiasID string `json:"house_fias_id"` // Код ФИАС дома
HouseKladrID string `json:"house_kladr_id"` // Код КЛАДР дома
HouseType string `json:"house_type"` // Тип дома (сокращенный)
HouseTypeFull string `json:"house_type_full"` // Тип дома
House string `json:"house"` // Дом
BlockType string `json:"block_type"` // Тип корпуса/строения (сокращенный)
BlockTypeFull string `json:"block_type_full"` // Тип корпуса/строения
Block string `json:"block"` // Корпус/строение
FlatType string `json:"flat_type"` // Тип квартиры (сокращенный)
FlatTypeFull string `json:"flat_type_full"` // Тип квартиры
Flat string `json:"flat"` // Квартира
FlatArea string `json:"flat_area"` // Площадь квартиры
SquareMeterPrice string `json:"square_meter_price"` // Рыночная стоимость м²
FlatPrice string `json:"flat_price"` // Рыночная стоимость квартиры
PostalBox string `json:"postal_box"` // Абонентский ящик
FiasID string `json:"fias_id"` // Код ФИАС
FiasLevel string `json:"fias_level"` // Уровень детализации, до которого адрес найден в ФИАС
KladrID string `json:"kladr_id"` // Код КЛАДР
CapitalMarker string `json:"capital_marker"` // Статус центра
Okato string `json:"okato"` // Код ОКАТО
Oktmo string `json:"oktmo"` // Код ОКТМО
TaxOffice string `json:"tax_office"` // Код ИФНС для физических лиц
Timezone string `json:"timezone"` // Часовой пояс
GeoLat string `json:"geo_lat"` // Координаты: широта
GeoLon string `json:"geo_lon"` // Координаты: долгота
BeltwayHit string `json:"beltway_hit"` // Внутри кольцевой?
BeltwayDistance string `json:"beltway_distance"` // Расстояние от кольцевой в км.
// QualityCodeGeo для clean вызовов он int для suggest в адресе банков он string поэтому в поле поставил interface{} чтобы работало и там и там)\
QualityCodeGeo interface{} `json:"qc_geo,string"` // Код точности координат
QualityCodeComplete interface{} `json:"qc_complete,string"` // Код полноты
QualityCodeHouse interface{} `json:"qc_house,string"` // Код проверки дома
QualityCode interface{} `json:"qc,string"` // Код качества
UnparsedParts string `json:"unparsed_parts"` // Нераспознанная часть адреса. Для адреса
Metro []Metro `json:"metro"`
}
// ResponseAddress api response for address
type ResponseAddress struct {
Value string `json:"value"`
UnrestrictedValue string `json:"unrestricted_value"`
Data Address `json:"data"`
}
// Metro base struct for dadata.Metro
type Metro struct {
Name string `json:"name"`
Line string `json:"line"`
Distance float64 `json:"distance"`
}
// Phone base struct for dadata.Phone
type Phone struct {
Source string `json:"source"` // Исходный телефон одной строкой
Type string `json:"type"` // Тип телефона
Phone string `json:"phone"` // Стандартизованный телефон одной строкой
CountryCode string `json:"country_code"` // Код страны
CityCode string `json:"city_code"` // Код города / DEF-код
Number string `json:"number"` // Локальный номер телефона
Extension string `json:"extension"` // Добавочный номер
Provider string `json:"provider"` // Оператор связи
Region string `json:"region"` // Регион
Timezone string `json:"timezone"` // Часовой пояс
QualityCodeConflict int `json:"qc_conflict"` // Признак конфликта телефона с адресом
QualityCode int `json:"qc"` // Код качества
}
// Name base struct for dadata.Name
type Name struct {
Source string `json:"source"` // Исходное ФИО одной строкой
Result string `json:"result"` // Стандартизованное ФИО одной строкой
ResultGenitive string `json:"result_genitive"` // ФИО в родительном падеже (кого?)
ResultDative string `json:"result_dative"` // ФИО в дательном падеже (кому?)
ResultAblative string `json:"result_ablative"` // ФИО в творительном падеже (кем?)
Surname string `json:"surname"` // Фамилия
Name string `json:"name"` // Имя
Patronymic string `json:"patronymic"` // Отчество
Gender string `json:"gender"` // Пол
QualityCode interface{} `json:"qc"` // Код качества
}
// ResponseName api response for name
type ResponseName struct {
Value string `json:"value"`
UnrestrictedValue string `json:"unrestricted_value"`
Data Name `json:"data"`
}
// Email base struct for dadata.Email
type Email struct {
Source string `json:"source"` // Исходный e-mail
Email string `json:"email"` // Стандартизованный e-mail
QualityCode int `json:"qc"` // Код качества
}
// ResponseEmail api response for email
type ResponseEmail struct {
Value string `json:"value"`
UnrestrictedValue string `json:"unrestricted_value"`
Data Email `json:"data"`
}
// Birthdate base struct for dadata.Birthdate
type Birthdate struct {
Source string `json:"source"` // Исходная дата
Birthdate string `json:"birthdate"` // Стандартизованная дата
QualityCode int `json:"qc"` // Код качества
}
// Vehicle base struct for dadata.Vehicle
type Vehicle struct {
Source string `json:"source"` // Исходное значение
Result string `json:"result"` // Стандартизованное значение
Brand string `json:"brand"` // Марка
Model string `json:"model"` // Модель
QualityCode int `json:"qc"` // Код проверки
}
// Passport base struct for dadata.Passport
type Passport struct {
Source string `json:"source"` // Исходная серия и номер одной строкой
Series string `json:"series"` // Серия
Number string `json:"number"` // Номер
QualityCode int `json:"qc"` // Код проверки
}
// Bank base struct for dadata.Bank
type Bank struct {
Opf OrganizationOPF `json:"opf"`
Name BankName `json:"name"`
Bic string `json:"bic"` // Банковский идентификационный код (БИК) ЦБ РФ
Swift string `json:"swift"` // Банковский идентификационный код в системе SWIFT
Okpo string `json:"okpo"` // Код ОКПО
CorrespondentAccount string `json:"correspondent_account"` // Корреспондентский счет в ЦБ РФ
RegistrationNumber string `json:"registration_number"` // Регистрационный номер в ЦБ РФ
// Go cannot use recursive types - thats why we need copy paste all fields to Rks
Rkc struct {
Opf OrganizationOPF `json:"opf"`
Name BankName `json:"name"`
Bic string `json:"bic"`
Swift string `json:"swift"`
Okpo string `json:"okpo"`
CorrespondentAccount string `json:"correspondent_account"`
RegistrationNumber string `json:"registration_number"`
Rkc interface{} `json:"rkc"`
Address ResponseAddress `json:"address"`
Phone string `json:"phone"`
State OrganizationState `json:"state"`
} `json:"rkc"` // Расчетно-кассовый центр. Объект такой же структуры, как сам банк.
Address ResponseAddress `json:"address"` // см ResponseAddress
Phone string `json:"phone"` // Не заполняется
State OrganizationState `json:"state"`
}
// ResponseBank api response for bank
type ResponseBank struct {
Value string `json:"value"`
UnrestrictedValue string `json:"unrestricted_value"`
Data Bank `json:"data"`
}
// OrganizationOPF Тип Кредитной организации
type OrganizationOPF struct {
Type string `json:"type"` // Тип кредитной организации
Full string `json:"full"` // Тип кредитной организации (на русском)
Short string `json:"short"` // Тип кредитной организации (на русском, сокращенный)
}
// BankName наименование банка
type BankName struct {
Payment string `json:"payment"` // Платежное наименование
Full string `json:"full"` // Полное наименование
Short string `json:"short"` // Краткое наименование
}
// OrganizationState Статус организации
type OrganizationState struct {
Status string `json:"status"` // Статус организации:
// ACTIVE — действующая
// LIQUIDATING — ликвидируется
// LIQUIDATED — ликвидирована
ActualityDate int64 `json:"actuality_date"` // Дата актуальности сведений
RegistrationDate int64 `json:"registration_date"` // Дата регистрации
LiquidationDate string `json:"liquidation_date"` // Дата ликвидации
}
// Party base struct for dadata.Party (rus Организация)
type Party struct {
Kpp string `json:"kpp"`
Capital string `json:"capital"`
Management struct {
Name string `json:"name"`
Post string `json:"post"`
} `json:"management"`
Founders string `json:"founders"`
Managers string `json:"managers"`
BranchType string `json:"branch_type"`
BranchCount int `json:"branch_count"`
Source string `json:"source"`
Qc string `json:"qc"`
Hid string `json:"hid"`
Type string `json:"type"`
State OrganizationState `json:"state"`
Opf OrganizationOPF `json:"opf"`
Name struct {
FullWithOpf string `json:"full_with_opf"`
ShortWithOpf string `json:"short_with_opf"`
Latin string `json:"latin"`
Full string `json:"full"`
Short string `json:"short"`
} `json:"name"`
Inn string `json:"inn"`
Ogrn string `json:"ogrn"`
Okpo string `json:"okpo"`
Okved string `json:"okved"`
Okveds string `json:"okveds"`
Authorities string `json:"authorities"`
Documents string `json:"documents"`
Licenses string `json:"licenses"`
Address ResponseAddress `json:"address"`
Phones string `json:"phones"`
Emails string `json:"emails"`
OgrnDate int64 `json:"ogrn_date"`
OkvedType string `json:"okved_type"`
}
// ResponseParty api response for party
type ResponseParty struct {
Value string `json:"value"`
UnrestrictedValue string `json:"unrestricted_value"`
Data Party `json:"data"`
}
// StatResponse usage statitics
// see docs https://dadata.ru/api/stat/
type StatResponse struct {
Date string `json:"date"`
Services struct {
Merging int `json:"merging"`
Suggestions int `json:"suggestions"`
Clean int `json:"clean"`
} `json:"services"`
}
// BalanceResponse Response of API request
type BalanceResponse struct {
Balance float64 `json:"balance"`
}
// Country base struct for dadata.Country
type Country struct {
Code string `json:"code"`
Alfa2 string `json:"alfa2"`
Alfa3 string `json:"alfa3"`
NameShort string `json:"name_short"`
Name string `json:"name"`
}
// ResponseCountry api response for country
type ResponseCountry struct {
Value string `json:"value"`
Data Country `json:"data"`
}

25
stat.go Normal file
View File

@ -0,0 +1,25 @@
package dadata
import (
"context"
"time"
)
// DailyStat return daily statistics
// see documentation https://dadata.ru/api/stat/
func (c *Client) DailyStat(date time.Time) (*StatResponse, error) {
return c.DailyStatWithCtx(context.Background(), date)
}
// DailyStatWithCtx return daily statistics
// see documentation https://dadata.ru/api/stat/
func (c *Client) DailyStatWithCtx(ctx context.Context, date time.Time) (result *StatResponse, err error) {
var dateStr string
result, dateStr = &StatResponse{}, date.Format("2006-01-02")
if err = c.sendRequestToURL(ctx, "GET", c.options.baseURL+"stat/daily?date="+dateStr, nil, result); err != nil {
result = nil
}
return
}

97
suggest.go Normal file
View File

@ -0,0 +1,97 @@
package dadata
import "context"
func (c *Client) sendSuggestRequest(ctx context.Context, lastURLPart string, requestParams SuggestRequestParams, result interface{}) error {
return c.sendRequest(ctx, "suggest/"+lastURLPart, requestParams, result)
}
// SuggestAddresses try to return suggest addresses by requestParams
func (c *Client) SuggestAddresses(requestParams SuggestRequestParams) ([]ResponseAddress, error) {
return c.SuggestAddressesWithCtx(context.Background(), requestParams)
}
// SuggestAddressesWithCtx try to return suggest addresses by requestParams
func (c *Client) SuggestAddressesWithCtx(ctx context.Context, requestParams SuggestRequestParams) (ret []ResponseAddress, err error) {
var result = &SuggestAddressResponse{}
if err = c.sendSuggestRequest(ctx, "address", requestParams, result); err != nil {
return
}
ret = result.Suggestions
return
}
// SuggestNames try to return suggest names by requestParams
func (c *Client) SuggestNames(requestParams SuggestRequestParams) ([]ResponseName, error) {
return c.SuggestNamesWithCtx(context.Background(), requestParams)
}
// SuggestNamesWithCtx try to return suggest names by requestParams
func (c *Client) SuggestNamesWithCtx(ctx context.Context, requestParams SuggestRequestParams) (ret []ResponseName, err error) {
var result = &SuggestNameResponse{}
if err = c.sendSuggestRequest(ctx, "fio", requestParams, result); err != nil {
return
}
ret = result.Suggestions
return
}
// SuggestBanks try to return suggest banks by requestParams
func (c *Client) SuggestBanks(requestParams SuggestRequestParams) ([]ResponseBank, error) {
return c.SuggestBanksWithCtx(context.Background(), requestParams)
}
// SuggestBanksWithCtx try to return suggest banks by requestParams
func (c *Client) SuggestBanksWithCtx(ctx context.Context, requestParams SuggestRequestParams) (ret []ResponseBank, err error) {
var result = &SuggestBankResponse{}
if err = c.sendSuggestRequest(ctx, "bank", requestParams, result); err != nil {
return
}
ret = result.Suggestions
return
}
// SuggestParties try to return suggest parties by requestParams
func (c *Client) SuggestParties(requestParams SuggestRequestParams) ([]ResponseParty, error) {
return c.SuggestPartiesWithCtx(context.Background(), requestParams)
}
// SuggestPartiesWithCtx try to return suggest parties by requestParams
func (c *Client) SuggestPartiesWithCtx(ctx context.Context, requestParams SuggestRequestParams) (ret []ResponseParty, err error) {
var result = &SuggestPartyResponse{}
if err = c.sendSuggestRequest(ctx, "party", requestParams, result); err != nil {
return
}
ret = result.Suggestions
return
}
// SuggestEmails try to return suggest emails by requestParams
func (c *Client) SuggestEmails(requestParams SuggestRequestParams) ([]ResponseEmail, error) {
return c.SuggestEmailsWithCtx(context.Background(), requestParams)
}
// SuggestEmailsWithCtx try to return suggest emails by requestParams
func (c *Client) SuggestEmailsWithCtx(ctx context.Context, requestParams SuggestRequestParams) (ret []ResponseEmail, err error) {
var result = &SuggestEmailResponse{}
if err = c.sendSuggestRequest(ctx, "email", requestParams, result); err != nil {
return
}
ret = result.Suggestions
return
}
// SuggestCountries try to return suggest countries by requestParams
func (c *Client) SuggestCountries(requestParams SuggestRequestParams) ([]ResponseCountry, error) {
return c.SuggestCountriesWithCtx(context.Background(), requestParams)
}
// SuggestCountriesWithCtx try to return suggest countries by requestParams
func (c *Client) SuggestCountriesWithCtx(ctx context.Context, requestParams SuggestRequestParams) (ret []ResponseCountry, err error) {
var result = &SuggestCountryResponse{}
if err = c.sendSuggestRequest(ctx, "country", requestParams, result); err != nil {
return
}
ret = result.Suggestions
return
}

114
suggest_test.go Normal file
View File

@ -0,0 +1,114 @@
package dadata
import (
"context"
"fmt"
"time"
)
func ExampleClient_SuggestAddresses() {
daData := NewClient()
addresses, err := daData.SuggestAddresses(SuggestRequestParams{Query: "Преснен", Count: 2})
if nil != err {
fmt.Println(err)
}
for _, address := range addresses {
fmt.Println(address.UnrestrictedValue)
fmt.Println(address.Data.Street)
fmt.Println(address.Data.FiasLevel)
}
}
func ExampleClient_SuggestAddresses_granular() {
daData := NewClient()
var req SuggestRequestParams
req.Query = "лен"
req.Locations = append(req.Locations, SuggestRequestParamsLocation{
RegionFiasID: "df3d7359-afa9-4aaa-8ff9-197e73906b1c",
CityFiasID: "e9e684ce-7d60-4480-ba14-ca6da658188b",
})
req.FromBound = SuggestBound{SuggestBoundStreet}
req.ToBound = SuggestBound{SuggestBoundStreet}
req.RestrictValue = true
req.Count = 2
addresses, err := daData.SuggestAddresses(req)
if nil != err {
fmt.Println(err)
}
for _, address := range addresses {
fmt.Println(address.UnrestrictedValue)
fmt.Println(address.Data.Street)
}
}
func ExampleClient_SuggestAddressesWithCtx() {
daData := NewClient()
var req SuggestRequestParams
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
req.Query = "лен"
req.Locations = append(req.Locations, SuggestRequestParamsLocation{
RegionFiasID: "df3d7359-afa9-4aaa-8ff9-197e73906b1c",
CityFiasID: "e9e684ce-7d60-4480-ba14-ca6da658188b",
})
req.FromBound = SuggestBound{SuggestBoundStreet}
req.ToBound = SuggestBound{SuggestBoundStreet}
req.RestrictValue = true
req.Count = 2
addresses, err := daData.SuggestAddressesWithCtx(ctx, req)
if nil != err {
fmt.Println(err)
}
for _, address := range addresses {
fmt.Println(address.UnrestrictedValue)
}
cancel()
// if ctx is exited (by cancel or timeout) we must catch err
_, err = daData.SuggestAddressesWithCtx(ctx, req)
fmt.Println(err)
}
func ExampleClient_SuggestBanks() {
daData := NewClient()
banks, err := daData.SuggestBanks(SuggestRequestParams{Query: "Кредитный", Count: 3})
if nil != err {
fmt.Println(err)
}
for _, bank := range banks {
fmt.Println(bank.Data.Name.Full)
fmt.Println(bank.Data.Bic)
}
}
func ExampleClient_SuggestParties() {
daData := NewClient()
parties, err := daData.SuggestParties(SuggestRequestParams{Query: "Агрохолд", Count: 3})
if nil != err {
fmt.Println(err)
}
for _, party := range parties {
fmt.Println(party.Data.Name.Full)
fmt.Println(party.Data.Ogrn)
}
}

73
suggest_types.go Normal file
View File

@ -0,0 +1,73 @@
package dadata
// SuggestRequestParamsLocation constraints for suggestion
// full documentation https://confluence.hflabs.ru/pages/viewpage.action?pageId=204669108
type SuggestRequestParamsLocation struct {
FiasID string `json:"fias_id,omitempty"`
KladrID string `json:"kladr_id,omitempty"`
Region string `json:"region,omitempty"`
RegionFiasID string `json:"region_fias_id,omitempty"`
RegionKladrID string `json:"region_kladr_id,omitempty"`
RegionTypeFull string `json:"region_type_full,omitempty"`
City string `json:"city,omitempty"`
CityFiasID string `json:"city_fias_id,omitempty"` // search only in this area
CityKladrID string `json:"city_kladr_id,omitempty"`
CityTypeFull string `json:"city_type_full,omitempty"`
CityDistrictTypeFull string `json:"city_district_type_full,omitempty"`
Settlement string `json:"settlement,omitempty"`
SettlementFiasID string `json:"settlement_fias_id,omitempty"`
SettlementKladrID string `json:"settlement_kladr_id,omitempty"`
SettlementTypeFull string `json:"settlement_type_full,omitempty"`
Street string `json:"street,omitempty"`
StreetFiasID string `json:"street_fias_id,omitempty"`
StreetKladrID string `json:"street_kladr_id,omitempty"`
StreetTypeFull string `json:"street_type_full,omitempty"`
AreaTypeFull string `json:"area_type_full,omitempty"`
}
// SuggestBound for granular sugestion
// full documentation https://confluence.hflabs.ru/pages/viewpage.action?pageId=222888017
type SuggestBound struct {
Value BoundValue `json:"value"`
}
// SuggestRequestParams Request struct
type SuggestRequestParams struct {
Query string `json:"query"` // user input for suggestion
Count int `json:"count"` // ligmit for results
Locations []SuggestRequestParamsLocation `json:"locations"`
RestrictValue bool `json:"restrict_value"` // don't show restricts (region) on results
FromBound SuggestBound `json:"from_bound"`
ToBound SuggestBound `json:"to_bound"`
}
// SuggestAddressResponse result slice for address suggestions
type SuggestAddressResponse struct {
Suggestions []ResponseAddress `json:"suggestions"`
}
// SuggestNameResponse result slice for name suggestions
type SuggestNameResponse struct {
Suggestions []ResponseName `json:"suggestions"`
}
// SuggestBankResponse result slice for bank suggestions
type SuggestBankResponse struct {
Suggestions []ResponseBank `json:"suggestions"`
}
// SuggestPartyResponse result slice for party suggestions
type SuggestPartyResponse struct {
Suggestions []ResponseParty `json:"suggestions"`
}
// SuggestEmailResponse result slice for email suggestions
type SuggestEmailResponse struct {
Suggestions []ResponseEmail `json:"suggestions"`
}
// SuggestCountryResponse result slice for country suggestions
type SuggestCountryResponse struct {
Suggestions []ResponseCountry `json:"suggestions"`
}