megaplan

package module
v3.4.1 Latest Latest
Warning

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

Go to latest
Published: Aug 21, 2024 License: MIT Imports: 16 Imported by: 0

README

Пример использования

Иниализация клиента + опция включения заголовка "Accept-Encoding":"gzip", ответ будет возвращаться сжатым, реализована декомпрессия тела ответа внутри вызова.

    import (
        "github.com/stvoidit/megaplan/v3"
    )
    const (
        domain = `https://yourdomain.ru`
        token  = `token`
    )
    func main() {
        client := megaplan.NewClient(domain, token, megaplan.OptionEnableAcceptEncodingGzip(true))
    }

Пример создания задачами

https://demo.megaplan.ru/api/v3/docs#entityTask Для удобства составления json для тела запроса есть функция megaplan.BuildQueryParams. Её единственное назначение - собрать параметры в правильном формате. Некоторые сущности требуют специального формата (например Дата и Время, Интервал, Дата, Сдвиг дат), то функция megaplan.BuildQueryParams корректно сформирует структуру этих сущностей.

func CreateTask(c *megaplan.ClientV3) {
    const endpoint = "/api/v3/task"
    var qp = megaplan.BuildQueryParams(
        megaplan.SetRawField("contentType", "Task"),
        megaplan.SetRawField("isUrgent", false),
        megaplan.SetRawField("isTemplate", false),
        megaplan.SetRawField("name", "library test"),
        megaplan.SetRawField("subject", "subject library test"),
        megaplan.SetRawField("statement", "statement library test"),
        megaplan.SetEntityField("owner", "Employee", 1000129),
        megaplan.SetEntityField("responsible", "Employee", 1000129),
        megaplan.SetEntityField("deadline", "DateOnly", time.Now().Add(time.Hour*72)),
        megaplan.SetEntityField("plannedWork", "DateInterval", time.Hour*13),
    )
    r, err := qp.ToReader()
    if err != nil {
        panic(err)
    }
    rc, err := c.DoRequestAPI(http.MethodPost, endpoint, nil, r)
    if err != nil {
        panic(err)
    }
    defer rc.Close()
    os.Stdout.ReadFrom(rc)
}

Пример запроса с параметрами URL

Так как параметры запроса на api "Мегаплан" передаются в нетипичном формате ("*?json=?"), то необходимо их экранировать через url.QueryEscape. Для удобства составления этих параметров можно так же использовать тип megaplan.QueryParams.

    func testGetWithFilters(c *megaplan.ClientV3) {
        const endpoint = "/api/v3/task"
        var requestedFiled = [...]string{
            "id",
            "name",
            "status",
            "deadline",
            "actualWork",
            "responsible",
            "timeCreated",
        }
        // параметры верхнего уровня
        var searchParams = megaplan.BuildQueryParams(
            megaplan.SetRawField("limit", 50),
            megaplan.SetRawField("onlyRequestedFields", true),
            megaplan.SetRawField("fields", requestedFiled),
        )

        // пример составления параметров без megaplan.BuildQueryParams (т.к. есть большая вложенность параметров)
        // megaplan.QueryParams - это просто алиас к типа megaplan.QueryParams, но с доп. методами,
        // поэтому для корректного составления json в параметрах URL необходимо передавать в DoRequestAPI именно megaplan.QueryParams
        now := time.Now()
        from := time.Date(now.Year(), time.January, 1, 0, 0, 0, 0, time.Local)
        var filterParams = map[string]interface{}{
            "contentType": "TaskFilter",
            "id":          nil,
            "config": megaplan.QueryParams{
                "contentType": "FilterConfig",
                "termGroup": megaplan.QueryParams{
                    "contentType": "FilterTermGroup",
                    "join":        "and",
                    "terms": [...]megaplan.QueryParams{
                        {
                            "contentType": "FilterTermEnum",
                            "field":       "status",
                            "comparison":  "equals",
                            "value":       [...]string{"filter_any"},
                        },
                        {
                            "comparison":  "equals",
                            "field":       "responsible",
                            "contentType": "FilterTermRef",
                            "value": [...]megaplan.QueryParams{
                                {"id": 1000129, "contentType": "Employee"},
                            },
                        },
                        {
                            "comparison":  "equals",
                            "field":       "statusChangeTime",
                            "contentType": "FilterTermDate",
                            "value": megaplan.QueryParams{
                                "contentType": "IntervalDates",
                                "from":        megaplan.CreateEnity("DateOnly", from),
                                "to": megaplan.QueryParams{
                                    "contentType": "DateOnly",
                                    "year":        now.Year(),
                                    "month":       int(now.Month()) - 1,
                                    "day":         now.Day(),
                                },
                            },
                        },
                    },
                },
            },
        }
        searchParams["filter"] = filterParams

        {
            // вариант отправки через DoRequestAPI, внутри формируется корректный http.Request, если http.Response был сжат, то будет разархивирован
            rc, err := c.DoRequestAPI(http.MethodGet, endpoint, searchParams, nil)
            if err != nil {
                panic(err)
            }
            defer rc.Close()
            os.Stdout.ReadFrom(rc)
        }
        {
            // пример с использование Do, внучную собирается http.Request (добавляются необходимые заголовки, http.Response никак не обрабатывается перед возвратом)
            c.SetOptions(megaplan.OptionEnableAcceptEncodingGzip(false))
            request, err := http.NewRequest(http.MethodGet, domain, nil)
            if err != nil {
                panic(err)
            }
            request.URL.Path = endpoint
            request.URL.RawQuery = searchParams.QueryEscape() // параметры будут правильно экранированы
            response, err := c.Do(request)
            if err != nil {
                panic(err)
            }
            defer response.Body.Close()
            os.Stdout.ReadFrom(response.Body)
        }
    }

Чтение ответа

С появлением дженериков улучшена функция для чтения ответов от api. Внутри функции есть проверка на Content-Type, если это не json, то в 99% это html с ошибкой. В этом случае функция вернет ошибку с текстом в виде html строки.

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	"github.com/stvoidit/megaplan/v3"
)

const (
	DOMAIN      = `https://example.ru`
	TOKEN       = `TOKEN`
	ACCOUNTINFO = `/api/v3/accountInfo`
)

type AccountInfo struct {
	ID                string         `json:"id"`
	ContentType       string         `json:"contentType"`
	PermanentHost     string         `json:"permanentHost"`
	AccountName       string         `json:"accountName"`
	BuildVersion      string         `json:"buildVersion"`
	SystemProductName string         `json:"systemProductName"`
	TarifId           string         `json:"tarifId"`
	LicenceEndDate    map[string]any `json:"licenceEndDate"`
	MobileEndDate     map[string]any `json:"mobileEndDate"`
	PaidToDate        map[string]any `json:"paidToDate"`
	LicenseExpired    bool           `json:"licenseExpired"`
	MegamailDomain    string         `json:"megamailDomain"`
	DaysRemaining     int            `json:"daysRemaining"`
	TimeCreated       string         `json:"timeCreated"`
}

func (ai AccountInfo) String() string {
	var sb strings.Builder
	e := json.NewEncoder(&sb)
	e.SetIndent("", "  ")
	e.Encode(&ai)
	return sb.String()
}

func main() {
	c := megaplan.NewClient(DOMAIN, TOKEN,
		megaplan.OptionEnableAcceptEncodingGzip(true),
		megaplan.OptionInsecureSkipVerify(true))
	res, err := c.DoRequestAPI(http.MethodGet, ACCOUNTINFO, nil, nil)
	if err != nil {
		panic(err)
	}
    // Вы можете указать типа как "any", если вам нужно стандартное поведение json.Decode - возврат в виде map[string]any
	body, err := megaplan.ParseResponse[AccountInfo](res)
	if err != nil {
		panic(err)
	}
	fmt.Println(body)
}

! Про типы и сущности "мегаплана" !

* не актуально с появлением дженериков

Многие реализации библиотек для API "Мегаплана" пытаются строго типизировать и описать полностью сущности, которыми оперирует "Мегаплан". Однако это подход влечет за собой обязанность этих библиотек поддерживать согласованность с версиями "Мегаплана", а так же каким-то образом поддерживать кастомные варианты полей. Данная библиотека является просто оберткой для использования API v3 и включает минимальное кол-во вспомогательных функций для составления запросов и парсинга ответов.

В силу специфики строения сущностей "Мегаплана" некоторые типы могут некорретно собираться функцией megaplan.BuildQueryParams, поэтому выше даны примере, как можно "дособрать" необходимые объекты.

Documentation

Index

Constants

View Source
const ISO8601 = `2006-01-02T15:04:05-07:00`

ISO8601 - формат даты для api

Variables

View Source
var (
	DefaultTransport = &http.Transport{
		Proxy:               http.ProxyFromEnvironment,
		MaxIdleConns:        cpus,
		MaxConnsPerHost:     cpus,
		MaxIdleConnsPerHost: cpus,
	}
	DefaultClient = &http.Client{
		Transport: DefaultTransport,
		Timeout:   time.Minute,
	}
	// DefaultHeaders - заголовок по умолчанию - версия go. Используется при инициализации клиента в NewClient.
	DefaultHeaders = http.Header{"User-Agent": {runtime.Version()}}
)

DefaultClient - клиент по умаолчанию для API.

View Source
var ErrUnknownCompressionMethod = errors.New("unknown compression method")

ErrUnknownCompressionMethod - неизвестное значение в заголовке "Content-Encoding" не является фатальной ошибкой, должна возвращаться вместе с http.Response.Body, чтобы пользователь мог реализовать свой метод обработки сжатого сообщения

Functions

This section is empty.

Types

type ClientOption

type ClientOption func(*ClientV3)

ClientOption - функция применения настроект

func OptionDisableCookie added in v3.4.0

func OptionDisableCookie(yes bool) ClientOption

func OptionDisablekeepAlive added in v3.4.0

func OptionDisablekeepAlive(yes bool) ClientOption

func OptionEnableAcceptEncodingGzip

func OptionEnableAcceptEncodingGzip(yes bool) ClientOption

OptionEnableAcceptEncodingGzip - доабвить заголов Accept-Encoding=gzip к запросу т.е. объекм трафика на хуках может быть большим, то удобно запрашивать сжатый ответ

func OptionForceAttemptHTTP2 added in v3.4.0

func OptionForceAttemptHTTP2(yes bool) ClientOption

func OptionInsecureSkipVerify

func OptionInsecureSkipVerify(yes bool) ClientOption

OptionInsecureSkipVerify - переключение флага bool в http.Client.Transport.TLSClientConfig.InsecureSkipVerify - отключать или нет проверку сертификтов Если домен использует самоподписанные сертифика, то удобно включать на время отладки и разработки

func OptionSetClientHTTP

func OptionSetClientHTTP(client *http.Client) ClientOption

OptionSetClientHTTP - установить свой экземпляр httpClient

func OptionSetXUserID

func OptionSetXUserID(userID int) ClientOption

OptionSetXUserID - добавить заголовок "X-User-Id" - запросы будут выполнятся от имени указанного пользователя. Если передано значение <= 0, то заголовок будет удален

func OptionsSetHTTPTransport

func OptionsSetHTTPTransport(tr http.RoundTripper) ClientOption

OptionsSetHTTPTransport - установить своб настройку http.Transport

type ClientV3

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

ClientV3 - клиент

func NewClient

func NewClient(domain, token string, opts ...ClientOption) (c *ClientV3)

NewClient - обертка над http.Client для удобной работы с API v3

func (*ClientV3) Close added in v3.4.0

func (c *ClientV3) Close()

func (*ClientV3) Do

func (c *ClientV3) Do(req *http.Request) (*http.Response, error)

Do - http.Do + установка обязательных заголовков + декомпрессия ответа, если ответ сжат

func (ClientV3) DoCtxRequestAPI

func (c ClientV3) DoCtxRequestAPI(ctx context.Context, method, endpoint string, search QueryParams, body io.Reader) (*http.Response, error)

DoRequestAPI - т.к. в v3 параметры запроса для GET (json маршализируется и будет иметь вид: "*?{params}=")

func (ClientV3) DoRequestAPI

func (c ClientV3) DoRequestAPI(method, endpoint string, search QueryParams, body io.Reader) (*http.Response, error)

DoRequestAPI - т.к. в v3 параметры запроса для GET (json маршализируется и будет иметь вид: "*?{params}=")

func (*ClientV3) SetOptions

func (c *ClientV3) SetOptions(opts ...ClientOption)

SetOptions - применить опции

func (*ClientV3) SetToken

func (c *ClientV3) SetToken(token string)

SetToken - установить или изменить токен доступа

func (*ClientV3) UploadFile

func (c *ClientV3) UploadFile(filename string, fileReader io.Reader) (*http.Response, error)

UploadFile - загрузка файла, возвращает обычный http.Response, в ответе стандартная структура ответа + данные для базовой сущности

type DateOnly added in v3.4.0

type DateOnly struct {
	ContentType string `json:"contentType"`
	Day         int    `json:"day"`
	Month       int    `json:"month"`
	Year        int    `json:"year"`
}

func NewDateOnly added in v3.4.0

func NewDateOnly(t time.Time) DateOnly

func (DateOnly) ToTime added in v3.4.0

func (d DateOnly) ToTime() time.Time

type Meta

type Meta struct {
	Errors []struct {
		Fields  any `json:"field"`
		Message any `json:"message"`
	} `json:"errors"`
	Status     int        `json:"status"`
	Pagination Pagination `json:"pagination"`
}

Meta - metainfo

func (Meta) Error

func (m Meta) Error() (err error)

Error - если была ошибка, переданная в meta, то вернется ошибка с описание мегаплана, если нет, то вернется nil

type Pagination

type Pagination struct {
	Count       int64 `json:"count"`
	Limit       int64 `json:"limit"`
	CurrentPage int64 `json:"currentPage"`
	HasMoreNext bool  `json:"hasMoreNext"`
	HasMorePrev bool  `json:"hasMorePrev"`
}

Pagination - пагинация

func (Pagination) MarshalJSON

func (p Pagination) MarshalJSON() ([]byte, error)

MarshalJSON - json.Marshaler TODO: вообще обратный маршалинг на практике не нужен, поэтому нужно доделать позже

func (*Pagination) UnmarshalJSON

func (p *Pagination) UnmarshalJSON(b []byte) (err error)

UnmarshalJSON - json.Unmarshaler

type QueryBuildingFunc

type QueryBuildingFunc func(QueryParams)

QueryBuildingFunc - функция посттроения тела запроса (обычно json для post запроса)

func SetEntityArray

func SetEntityArray(field string, ents ...QueryBuildingFunc) QueryBuildingFunc

SetEntityArray - добавление массива сущностей в поле (например список аудиторов)

func SetEntityField

func SetEntityField(fieldName string, contentType string, value any) (qbf QueryBuildingFunc)

SetEntityField - добавить поле с сущностью

func SetRawField

func SetRawField(field string, value any) QueryBuildingFunc

SetRawField - добавить поле с простым типом значения (string, int, etc.)

type QueryParams

type QueryParams map[string]any

QueryParams - параметры запроса

func BuildQueryParams

func BuildQueryParams(opts ...QueryBuildingFunc) (qp QueryParams)

BuildQueryParams - сборка объекта для запроса

func CreateEnity

func CreateEnity(contentType string, value any) (qp QueryParams)

CreateEnity - создать базовую сущность в формате "Мегаплана" ! могут быть не описаны крайние или редкоиспользуемые типы

func (QueryParams) PrettyPrintJSON

func (qp QueryParams) PrettyPrintJSON(w io.Writer) error

PrettyPrintJSON - SetIndent для читабельного вывода

func (QueryParams) QueryEscape

func (qp QueryParams) QueryEscape() string

QueryEscape - urlencode для запроса в строке параметрво

func (QueryParams) ToJSON

func (qp QueryParams) ToJSON() ([]byte, error)

ToJSON - маршализация параметров в JSON

func (QueryParams) ToReader

func (qp QueryParams) ToReader() (io.Reader, error)

ToReader - получить io.Reader для добавление в body часть http.Request

type Response

type Response[T any] struct {
	Meta Meta `json:"meta"` // metainfo ответа
	Data T    `json:"data"` // поле для декодирования присвоенной структуры
}

Response - ответ API

func ParseResponse

func ParseResponse[T any](r *http.Response) (res Response[T], err error)

ParseResponse - utility-функция для упрощения чтения ответа API

func (*Response[T]) Decode

func (res *Response[T]) Decode(r *http.Response) (err error)

Decode - парсинг ответа API

func (Response[T]) Next

func (res Response[T]) Next() bool

Next - есть ли следующая страница

func (Response[T]) Prev

func (res Response[T]) Prev() bool

Prev - есть ли предыдущая страница

Jump to

Keyboard shortcuts

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