@@ -0,0 +1,821 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ErrNotFound = errors.New("not found")
|
||||
|
||||
type Config struct {
|
||||
BaseURL string `envconfig:"BACKEND__BASE_URL" default:"http_v1://localhost:8000"`
|
||||
LoginURL string `envconfig:"LOGIN_URL" required:"true"`
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
http *http.Client
|
||||
baseURL string
|
||||
loginURL string
|
||||
}
|
||||
|
||||
func New(cfg Config) *Client {
|
||||
return &Client{
|
||||
http: &http.Client{
|
||||
Timeout: 60 * time.Second,
|
||||
},
|
||||
baseURL: cfg.BaseURL,
|
||||
loginURL: cfg.LoginURL,
|
||||
}
|
||||
}
|
||||
|
||||
func withBearer(token string) func(*http.Request) {
|
||||
return func(r *http.Request) {
|
||||
r.Header.Set("Authorization", "Bearer "+token)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) do(ctx context.Context, method string, path string, in any, out any, opts ...func(*http.Request)) error {
|
||||
var body io.Reader
|
||||
|
||||
if in != nil {
|
||||
b, err := json.Marshal(in)
|
||||
if err != nil {
|
||||
return fmt.Errorf("json.Marshal: %w", err)
|
||||
}
|
||||
body = bytes.NewReader(b)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, method, c.baseURL+"/"+path, body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("http_v1.NewRequest: %w", err)
|
||||
}
|
||||
|
||||
if in != nil {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(req)
|
||||
}
|
||||
|
||||
resp, err := c.http.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("client.Do: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return ErrNotFound
|
||||
}
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
b, _ := io.ReadAll(io.LimitReader(resp.Body, 8<<10))
|
||||
return fmt.Errorf("request failed: %s: %s", resp.Status, b)
|
||||
}
|
||||
|
||||
if out != nil {
|
||||
if err := json.NewDecoder(resp.Body).Decode(out); err != nil {
|
||||
return fmt.Errorf("json.Decode: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) LoginURL(token string) string {
|
||||
return c.loginURL + token
|
||||
}
|
||||
|
||||
func (c *Client) CreateLoginToken(ctx context.Context, telegramID int64) (string, error) {
|
||||
req := struct {
|
||||
TelegramID int64 `json:"telegram_id"`
|
||||
}{
|
||||
TelegramID: telegramID,
|
||||
}
|
||||
|
||||
var token string
|
||||
err := c.do(ctx, http.MethodPost, "api/v1/internal/auth/login-token", req, &token)
|
||||
|
||||
return token, err
|
||||
}
|
||||
|
||||
func (c *Client) AttachLoginTokenMessage(ctx context.Context, token string, messageID int) error {
|
||||
req := struct {
|
||||
Token string `json:"token"`
|
||||
MessageID int `json:"message_id"`
|
||||
}{
|
||||
Token: token,
|
||||
MessageID: messageID,
|
||||
}
|
||||
|
||||
return c.do(ctx, http.MethodPost, "api/v1/internal/auth/login-token/message", req, nil)
|
||||
}
|
||||
|
||||
func (c *Client) GetJWTByTelegramID(
|
||||
ctx context.Context,
|
||||
telegramID int64,
|
||||
) (string, error) {
|
||||
q := url.Values{}
|
||||
q.Set("telegram_id", fmt.Sprint(telegramID))
|
||||
|
||||
path := "api/v1/internal/auth/jwt?" + q.Encode()
|
||||
|
||||
var resp struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
|
||||
err := c.do(ctx, http.MethodGet, path, nil, &resp)
|
||||
return resp.AccessToken, err
|
||||
}
|
||||
|
||||
func (c *Client) GetJWTByTelegramUser(
|
||||
ctx context.Context,
|
||||
telegramID int64,
|
||||
username *string,
|
||||
firstName *string,
|
||||
lastName *string,
|
||||
) (string, error) {
|
||||
q := url.Values{}
|
||||
q.Set("telegram_id", fmt.Sprint(telegramID))
|
||||
if username != nil && *username != "" {
|
||||
q.Set("username", *username)
|
||||
}
|
||||
if firstName != nil && *firstName != "" {
|
||||
q.Set("first_name", *firstName)
|
||||
}
|
||||
if lastName != nil && *lastName != "" {
|
||||
q.Set("last_name", *lastName)
|
||||
}
|
||||
|
||||
path := "api/v1/internal/auth/jwt?" + q.Encode()
|
||||
|
||||
var resp struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
|
||||
err := c.do(ctx, http.MethodGet, path, nil, &resp)
|
||||
return resp.AccessToken, err
|
||||
}
|
||||
|
||||
type Workspace struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type Project struct {
|
||||
ID string `json:"id"`
|
||||
TelegramID int64 `json:"telegram_id"`
|
||||
Title string `json:"title"`
|
||||
Username *string `json:"username"`
|
||||
Status string `json:"status"`
|
||||
PurchaseInviteTypeDefault string `json:"purchase_invite_type_default"`
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
Items []Project `json:"items"`
|
||||
Total int `json:"total"`
|
||||
Page int `json:"page"`
|
||||
Size int `json:"size"`
|
||||
Pages int `json:"pages"`
|
||||
}
|
||||
|
||||
func (c *Client) GetWorkspaces(
|
||||
ctx context.Context,
|
||||
jwt string,
|
||||
) ([]Workspace, error) {
|
||||
var resp struct {
|
||||
Items []Workspace `json:"items"`
|
||||
}
|
||||
|
||||
err := c.do(
|
||||
ctx,
|
||||
http.MethodGet,
|
||||
"api/v1/workspaces",
|
||||
nil,
|
||||
&resp,
|
||||
withBearer(jwt),
|
||||
)
|
||||
|
||||
return resp.Items, err
|
||||
}
|
||||
|
||||
func (c *Client) CreateWorkspace(ctx context.Context, jwt string, name string) (Workspace, error) {
|
||||
req := struct {
|
||||
Name string `json:"name"`
|
||||
}{
|
||||
Name: name,
|
||||
}
|
||||
|
||||
var workspace Workspace
|
||||
err := c.do(ctx, http.MethodPost, "api/v1/workspaces", req, &workspace, withBearer(jwt))
|
||||
|
||||
return workspace, err
|
||||
}
|
||||
|
||||
func (c *Client) GetProjects(
|
||||
ctx context.Context,
|
||||
jwt string,
|
||||
workspaceID string,
|
||||
page, size int,
|
||||
) (*Page, error) {
|
||||
q := url.Values{}
|
||||
q.Set("page", fmt.Sprint(page))
|
||||
q.Set("size", fmt.Sprint(size))
|
||||
|
||||
path := fmt.Sprintf("api/v1/workspaces/%s/projects?%s", workspaceID, q.Encode())
|
||||
|
||||
var resp Page
|
||||
err := c.do(
|
||||
ctx,
|
||||
http.MethodGet,
|
||||
path,
|
||||
nil,
|
||||
&resp,
|
||||
withBearer(jwt),
|
||||
)
|
||||
|
||||
return &resp, err
|
||||
}
|
||||
|
||||
func (c *Client) GetProject(
|
||||
ctx context.Context,
|
||||
jwt string,
|
||||
workspaceID string,
|
||||
projectID string,
|
||||
) (*Project, error) {
|
||||
path := fmt.Sprintf("api/v1/workspaces/%s/projects/%s", workspaceID, projectID)
|
||||
|
||||
var project Project
|
||||
err := c.do(
|
||||
ctx,
|
||||
http.MethodGet,
|
||||
path,
|
||||
nil,
|
||||
&project,
|
||||
withBearer(jwt),
|
||||
)
|
||||
|
||||
return &project, err
|
||||
}
|
||||
|
||||
func (c *Client) UpdateProjectInviteLinkType(
|
||||
ctx context.Context,
|
||||
jwt string,
|
||||
workspaceID string,
|
||||
projectID string,
|
||||
inviteLinkType string, // "public" или "approval"
|
||||
) (*Project, error) {
|
||||
path := fmt.Sprintf("api/v1/workspaces/%s/projects/%s/invite-link-type", workspaceID, projectID)
|
||||
|
||||
payload := map[string]string{
|
||||
"purchase_invite_type_default": inviteLinkType,
|
||||
}
|
||||
|
||||
var project Project
|
||||
err := c.do(
|
||||
ctx,
|
||||
http.MethodPatch,
|
||||
path,
|
||||
payload,
|
||||
&project,
|
||||
withBearer(jwt),
|
||||
)
|
||||
|
||||
return &project, err
|
||||
}
|
||||
|
||||
func (c *Client) SendEvent(ctx context.Context, payload any) error {
|
||||
return c.do(
|
||||
ctx,
|
||||
http.MethodPost,
|
||||
"api/v1/internal/events",
|
||||
payload,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Client) SearchChannels(
|
||||
ctx context.Context,
|
||||
jwt string,
|
||||
username string,
|
||||
) ([]Channel, error) {
|
||||
path := fmt.Sprintf("api/v1/channels?username=%s", username)
|
||||
|
||||
var response struct {
|
||||
Items []Channel `json:"items"`
|
||||
}
|
||||
err := c.do(
|
||||
ctx,
|
||||
http.MethodGet,
|
||||
path,
|
||||
nil,
|
||||
&response,
|
||||
withBearer(jwt),
|
||||
)
|
||||
|
||||
return response.Items, err
|
||||
}
|
||||
|
||||
func (c *Client) AttachChannelToWorkspace(
|
||||
ctx context.Context,
|
||||
channelID string,
|
||||
workspaceID string,
|
||||
userTelegramID int64,
|
||||
) (*Project, error) {
|
||||
input := map[string]any{
|
||||
"channel_id": channelID,
|
||||
"workspace_id": workspaceID,
|
||||
"user_telegram_id": userTelegramID,
|
||||
}
|
||||
|
||||
var project Project
|
||||
err := c.do(
|
||||
ctx,
|
||||
http.MethodPost,
|
||||
"api/v1/internal/projects",
|
||||
input,
|
||||
&project,
|
||||
)
|
||||
|
||||
return &project, err
|
||||
}
|
||||
|
||||
func (c *Client) AcceptWorkspaceInvite(
|
||||
ctx context.Context,
|
||||
jwt string,
|
||||
inviteID string,
|
||||
) error {
|
||||
path := fmt.Sprintf("api/v1/invites/%s/accept", inviteID)
|
||||
|
||||
return c.do(
|
||||
ctx,
|
||||
http.MethodPost,
|
||||
path,
|
||||
nil,
|
||||
nil,
|
||||
withBearer(jwt),
|
||||
)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Creatives
|
||||
// ============================================================================
|
||||
|
||||
type Creative struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Text string `json:"text"`
|
||||
MediaItems []CreativeMediaItem `json:"media_items"`
|
||||
Buttons []CreativeButton `json:"buttons"`
|
||||
ProjectID string `json:"project_id"`
|
||||
ProjectChannelTitle string `json:"project_channel_title"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
Status string `json:"status"`
|
||||
Tag string `json:"tag"`
|
||||
PlacementsCount int `json:"placements_count"`
|
||||
}
|
||||
|
||||
type CreativeButton struct {
|
||||
Text string `json:"text"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type CreativeMediaItem struct {
|
||||
MediaType string `json:"media_type"`
|
||||
MediaFileID string `json:"media_file_id"`
|
||||
Position int `json:"position"`
|
||||
S3URL *string `json:"s3_url,omitempty"`
|
||||
}
|
||||
|
||||
type CreativeMediaInput struct {
|
||||
MediaType string `json:"media_type"`
|
||||
MediaFileID string `json:"media_file_id"`
|
||||
MediaData []byte `json:"media_data,omitempty"`
|
||||
}
|
||||
|
||||
type CreativesPage struct {
|
||||
Items []Creative `json:"items"`
|
||||
Total int `json:"total"`
|
||||
Page int `json:"page"`
|
||||
Size int `json:"size"`
|
||||
Pages int `json:"pages"`
|
||||
}
|
||||
|
||||
func (c *Client) GetCreatives(
|
||||
ctx context.Context,
|
||||
jwt string,
|
||||
workspaceID string,
|
||||
projectID *string,
|
||||
includeArchived bool,
|
||||
page, size int,
|
||||
) (*CreativesPage, error) {
|
||||
q := url.Values{}
|
||||
if projectID != nil {
|
||||
q.Set("project_id", *projectID)
|
||||
}
|
||||
q.Set("include_archived", fmt.Sprint(includeArchived))
|
||||
q.Set("page", fmt.Sprint(page))
|
||||
q.Set("size", fmt.Sprint(size))
|
||||
|
||||
path := fmt.Sprintf("api/v1/workspaces/%s/creatives?%s", workspaceID, q.Encode())
|
||||
|
||||
var resp CreativesPage
|
||||
err := c.do(
|
||||
ctx,
|
||||
http.MethodGet,
|
||||
path,
|
||||
nil,
|
||||
&resp,
|
||||
withBearer(jwt),
|
||||
)
|
||||
|
||||
return &resp, err
|
||||
}
|
||||
|
||||
func (c *Client) GetCreative(
|
||||
ctx context.Context,
|
||||
jwt string,
|
||||
workspaceID string,
|
||||
creativeID string,
|
||||
) (*Creative, error) {
|
||||
path := fmt.Sprintf("api/v1/workspaces/%s/creatives/%s", workspaceID, creativeID)
|
||||
|
||||
var creative Creative
|
||||
err := c.do(
|
||||
ctx,
|
||||
http.MethodGet,
|
||||
path,
|
||||
nil,
|
||||
&creative,
|
||||
withBearer(jwt),
|
||||
)
|
||||
|
||||
return &creative, err
|
||||
}
|
||||
|
||||
type CreateCreativeInput struct {
|
||||
Name string `json:"name"`
|
||||
Text string `json:"text"`
|
||||
MediaItems []CreativeMediaInput `json:"media_items,omitempty"`
|
||||
Buttons []CreativeButton `json:"buttons,omitempty"`
|
||||
Tag *string `json:"tag,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Client) CreateCreative(
|
||||
ctx context.Context,
|
||||
jwt string,
|
||||
workspaceID string,
|
||||
projectID string,
|
||||
input CreateCreativeInput,
|
||||
) (*Creative, error) {
|
||||
q := url.Values{}
|
||||
q.Set("project_id", projectID)
|
||||
|
||||
path := fmt.Sprintf("api/v1/workspaces/%s/creatives?%s", workspaceID, q.Encode())
|
||||
|
||||
var creative Creative
|
||||
err := c.do(
|
||||
ctx,
|
||||
http.MethodPost,
|
||||
path,
|
||||
input,
|
||||
&creative,
|
||||
withBearer(jwt),
|
||||
)
|
||||
|
||||
return &creative, err
|
||||
}
|
||||
|
||||
type UpdateCreativeInput struct {
|
||||
Name *string `json:"name,omitempty"`
|
||||
Text *string `json:"text,omitempty"`
|
||||
MediaItems *[]CreativeMediaInput `json:"media_items,omitempty"`
|
||||
Buttons *[]CreativeButton `json:"buttons,omitempty"`
|
||||
Status *string `json:"status,omitempty"`
|
||||
Tag *string `json:"tag,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Client) UpdateCreative(
|
||||
ctx context.Context,
|
||||
jwt string,
|
||||
workspaceID string,
|
||||
creativeID string,
|
||||
input UpdateCreativeInput,
|
||||
) (*Creative, error) {
|
||||
path := fmt.Sprintf("api/v1/workspaces/%s/creatives/%s", workspaceID, creativeID)
|
||||
|
||||
var creative Creative
|
||||
err := c.do(
|
||||
ctx,
|
||||
http.MethodPatch,
|
||||
path,
|
||||
input,
|
||||
&creative,
|
||||
withBearer(jwt),
|
||||
)
|
||||
|
||||
return &creative, err
|
||||
}
|
||||
|
||||
func (c *Client) DeleteCreative(
|
||||
ctx context.Context,
|
||||
jwt string,
|
||||
workspaceID string,
|
||||
creativeID string,
|
||||
) error {
|
||||
path := fmt.Sprintf("api/v1/workspaces/%s/creatives/%s", workspaceID, creativeID)
|
||||
|
||||
return c.do(
|
||||
ctx,
|
||||
http.MethodDelete,
|
||||
path,
|
||||
nil,
|
||||
nil,
|
||||
withBearer(jwt),
|
||||
)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Placements (Размещения)
|
||||
// ============================================================================
|
||||
|
||||
type Channel struct {
|
||||
ID string `json:"id"`
|
||||
TelegramID *int64 `json:"telegram_id"`
|
||||
Title *string `json:"title"`
|
||||
Username *string `json:"username"`
|
||||
InviteLink *string `json:"invite_link"`
|
||||
}
|
||||
|
||||
type CreateChannelInput struct {
|
||||
Username *string `json:"username,omitempty"`
|
||||
InviteLink *string `json:"invite_link,omitempty"`
|
||||
}
|
||||
|
||||
type CreateChannelsInput struct {
|
||||
Channels []CreateChannelInput `json:"channels"`
|
||||
}
|
||||
|
||||
type CreateChannelResult struct {
|
||||
Index int `json:"index"`
|
||||
Status string `json:"status"`
|
||||
Channel *Channel `json:"channel,omitempty"`
|
||||
Error *string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type ProjectOutput struct {
|
||||
ID string `json:"id"`
|
||||
TelegramID int64 `json:"telegram_id"`
|
||||
Title string `json:"title"`
|
||||
Username *string `json:"username"`
|
||||
Status string `json:"status"`
|
||||
PurchaseInviteTypeDefault string `json:"purchase_invite_type_default"`
|
||||
Channel Channel `json:"channel"`
|
||||
}
|
||||
|
||||
type CreateChannelsOutput struct {
|
||||
Results []CreateChannelResult `json:"results"`
|
||||
}
|
||||
|
||||
type CostInfo struct {
|
||||
Type string `json:"type"`
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
|
||||
type PlacementDetails struct {
|
||||
PlacementAt *string `json:"placement_at,omitempty"`
|
||||
PaymentAt *string `json:"payment_at,omitempty"`
|
||||
Cost *CostInfo `json:"cost,omitempty"`
|
||||
CostBeforeBargain *CostInfo `json:"cost_before_bargain,omitempty"`
|
||||
PlacementType *string `json:"placement_type,omitempty"`
|
||||
Format *string `json:"format,omitempty"`
|
||||
TopTimeMinutes *int `json:"top_time_minutes,omitempty"`
|
||||
FeedTimeMinutes *int `json:"feed_time_minutes,omitempty"`
|
||||
Comment *string `json:"comment,omitempty"`
|
||||
CreativeID *string `json:"creative_id,omitempty"`
|
||||
CreativeName *string `json:"creative_name,omitempty"`
|
||||
InviteLinkType *string `json:"invite_link_type,omitempty"`
|
||||
}
|
||||
|
||||
type PlacementOutput struct {
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
CreativeID *string `json:"creative_id,omitempty"`
|
||||
CreativeName *string `json:"creative_name,omitempty"`
|
||||
Comment *string `json:"comment,omitempty"`
|
||||
InviteLink *string `json:"invite_link,omitempty"`
|
||||
InviteLinkType string `json:"invite_link_type"`
|
||||
Channel Channel `json:"channel"`
|
||||
Project *ProjectOutput `json:"project"`
|
||||
ShortID string `json:"short_id"`
|
||||
Details *PlacementDetails `json:"details,omitempty"`
|
||||
PlacementPost *PlacementPostOutput `json:"placement_post,omitempty"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
type PlacementPostOutput struct {
|
||||
SubscriptionsCount int `json:"subscriptions_count"`
|
||||
ViewsCount *int `json:"views_count,omitempty"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
TimeOnTop *int `json:"time_on_top,omitempty"`
|
||||
Post PostOutput `json:"post"`
|
||||
}
|
||||
|
||||
type PostOutput struct {
|
||||
ID string `json:"id"`
|
||||
MessageID int `json:"message_id"`
|
||||
Text string `json:"text"`
|
||||
URL *string `json:"url,omitempty"`
|
||||
DeletedFromChannelAt *string `json:"deleted_from_channel_at,omitempty"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
type CreatePlacementChannelInput struct {
|
||||
ChannelID string `json:"channel_id"`
|
||||
Status *string `json:"status,omitempty"`
|
||||
Comment *string `json:"comment,omitempty"`
|
||||
Details *PlacementDetails `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
type CreatePlacementsInput struct {
|
||||
CreativeID *string `json:"creative_id,omitempty"`
|
||||
Channels []CreatePlacementChannelInput `json:"channels"`
|
||||
}
|
||||
|
||||
type GetPlacementsOutput struct {
|
||||
Placements []PlacementOutput `json:"placements"`
|
||||
}
|
||||
|
||||
type PlacementsPage struct {
|
||||
Items []PlacementOutput `json:"items"`
|
||||
Total int `json:"total"`
|
||||
Page int `json:"page"`
|
||||
Size int `json:"size"`
|
||||
Pages int `json:"pages"`
|
||||
}
|
||||
|
||||
type CreativePreviewOutput struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Text string `json:"text"`
|
||||
MediaItems []CreativeMediaItem `json:"media_items"`
|
||||
Buttons []CreativeButton `json:"buttons"`
|
||||
}
|
||||
|
||||
func (c *Client) CreatePlacements(
|
||||
ctx context.Context,
|
||||
jwt string,
|
||||
workspaceID string,
|
||||
projectID string,
|
||||
input CreatePlacementsInput,
|
||||
) (*GetPlacementsOutput, error) {
|
||||
path := fmt.Sprintf("api/v1/workspaces/%s/projects/%s/placements", workspaceID, projectID)
|
||||
|
||||
var placements GetPlacementsOutput
|
||||
err := c.do(
|
||||
ctx,
|
||||
http.MethodPost,
|
||||
path,
|
||||
input,
|
||||
&placements,
|
||||
withBearer(jwt),
|
||||
)
|
||||
|
||||
return &placements, err
|
||||
}
|
||||
|
||||
func (c *Client) CreateChannels(
|
||||
ctx context.Context,
|
||||
jwt string,
|
||||
input CreateChannelsInput,
|
||||
) (*CreateChannelsOutput, error) {
|
||||
var response CreateChannelsOutput
|
||||
err := c.do(
|
||||
ctx,
|
||||
http.MethodPost,
|
||||
"api/v1/channels",
|
||||
input,
|
||||
&response,
|
||||
withBearer(jwt),
|
||||
)
|
||||
|
||||
return &response, err
|
||||
}
|
||||
|
||||
func (c *Client) GetPlacements(
|
||||
ctx context.Context,
|
||||
jwt string,
|
||||
workspaceID string,
|
||||
projectID string,
|
||||
page, size int,
|
||||
) (*PlacementsPage, error) {
|
||||
q := url.Values{}
|
||||
q.Set("page", fmt.Sprint(page))
|
||||
q.Set("size", fmt.Sprint(size))
|
||||
|
||||
path := fmt.Sprintf("api/v1/workspaces/%s/projects/%s/placements?%s", workspaceID, projectID, q.Encode())
|
||||
|
||||
var resp PlacementsPage
|
||||
|
||||
err := c.do(
|
||||
ctx,
|
||||
http.MethodGet,
|
||||
path,
|
||||
nil,
|
||||
&resp,
|
||||
withBearer(jwt),
|
||||
)
|
||||
|
||||
return &resp, err
|
||||
}
|
||||
|
||||
func (c *Client) GetPlacement(
|
||||
ctx context.Context,
|
||||
jwt string,
|
||||
workspaceID string,
|
||||
projectID string,
|
||||
placementID string,
|
||||
) (*PlacementOutput, error) {
|
||||
path := fmt.Sprintf("api/v1/workspaces/%s/projects/%s/placements/%s", workspaceID, projectID, placementID)
|
||||
|
||||
var placement PlacementOutput
|
||||
err := c.do(
|
||||
ctx,
|
||||
http.MethodGet,
|
||||
path,
|
||||
nil,
|
||||
&placement,
|
||||
withBearer(jwt),
|
||||
)
|
||||
|
||||
return &placement, err
|
||||
}
|
||||
|
||||
func (c *Client) BuildPlacementCreative(
|
||||
ctx context.Context,
|
||||
jwt string,
|
||||
workspaceID string,
|
||||
projectID string,
|
||||
placementID string,
|
||||
) (*CreativePreviewOutput, error) {
|
||||
path := fmt.Sprintf(
|
||||
"api/v1/workspaces/%s/projects/%s/placements/%s/creative",
|
||||
workspaceID,
|
||||
projectID,
|
||||
placementID,
|
||||
)
|
||||
|
||||
var resp CreativePreviewOutput
|
||||
err := c.do(
|
||||
ctx,
|
||||
http.MethodPost,
|
||||
path,
|
||||
nil,
|
||||
&resp,
|
||||
withBearer(jwt),
|
||||
)
|
||||
|
||||
return &resp, err
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Workspace Members
|
||||
// ============================================================================
|
||||
|
||||
type WorkspaceMember struct {
|
||||
ID string `json:"id"`
|
||||
WorkspaceID string `json:"workspace_id"`
|
||||
UserID string `json:"user_id"`
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
func (c *Client) GetWorkspaceMembers(
|
||||
ctx context.Context,
|
||||
jwt string,
|
||||
workspaceID string,
|
||||
) ([]WorkspaceMember, error) {
|
||||
path := fmt.Sprintf("api/v1/workspaces/%s/members", workspaceID)
|
||||
|
||||
var resp struct {
|
||||
Items []WorkspaceMember `json:"items"`
|
||||
}
|
||||
|
||||
err := c.do(
|
||||
ctx,
|
||||
http.MethodGet,
|
||||
path,
|
||||
nil,
|
||||
&resp,
|
||||
withBearer(jwt),
|
||||
)
|
||||
|
||||
return resp.Items, err
|
||||
}
|
||||
Reference in New Issue
Block a user