Skip to content

Instantly share code, notes, and snippets.

@megaherz
Created December 5, 2019 14:56
Show Gist options
  • Save megaherz/c3fa078f6658addf79bb2cbc267b4e81 to your computer and use it in GitHub Desktop.
Save megaherz/c3fa078f6658addf79bb2cbc267b4e81 to your computer and use it in GitHub Desktop.
Ironsource https://www.ironsrc.com/ API Golang client
package ironsource
import (
"encoding/csv"
"encoding/json"
"fmt"
"github.com/pkg/errors"
"io"
"io/ioutil"
"net/http"
"strconv"
"strings"
"time"
)
type Client struct {
client http.Client
secretKey string
refreshToken string
token string
}
type httpError struct {
Code int
Body string
}
func (e httpError) Error() string {
return fmt.Sprintf("unexpected status code=%d body=%s", e.Code, e.Body)
}
var ErrRevenueNotFound = errors.New("revenue not found")
type AdRevenue struct {
// The ad unit from which the revenue was generate from
AdUnit string
// The unique ID of the user’s device
AdvertisingID string
// The unique ID type of the user’s device
AdvertisingIDType string
// The user ID being set by the developer
UserID string
// The segment the user is related to as defined on the platform
Segment string
// The placement the user is related to as defined on the platform
Placement string
// The placement the user is related to as defined on the platform
AdNetwork string
// A/B testing group the user was allocated to (can be ‘A’ or ‘B’)
ABTesting string
// The number of ads displayed to the user
Impressions int64
// The Revenue generated by the user (USD)
Revenue float64
}
type Application struct {
AppKey string `json:"appKey"`
AppName string `json:"appName"`
AppStatus string `json:"appStatus"`
Platform string `json:"platform"`
BundleID string `json:"bundleId"`
CreationDate string `json:"creationDate"`
Icon string `json:"icon"`
AdUnits map[string]interface{} `json:"adUnits"`
NetworkReportingAPI interface{} `json:"networkReportingApi"`
}
func NewClient(secretKey, refreshToken string) (*Client, error) {
var err error
c := &Client{
client: http.Client{Timeout: 30 * time.Second},
secretKey: secretKey,
refreshToken: refreshToken,
}
c.token, err = c.requestToken()
if err != nil {
return nil, errors.Wrap(err, "failed to request auth token")
}
return c, nil
}
func (c *Client) requestToken() (string, error) {
req, err := http.NewRequest("GET", "https://platform.ironsrc.com/partners/publisher/auth", nil)
if err != nil {
return "", err
}
req.Header.Add("secretkey", c.secretKey)
req.Header.Add("refreshToken", c.refreshToken)
resp, err := c.client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := ioutil.ReadAll(resp.Body)
return "", fmt.Errorf("unexpected status code=%d body=%s", resp.StatusCode, body)
}
token, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
return strings.Replace(string(token), "\"", "", -1), nil
}
func (c *Client) exec(req *http.Request) ([]byte, error) {
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", c.token))
resp, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode == http.StatusUnauthorized {
// IronSource does not have any specific code to detect expired token
if strings.Contains(string(body), "Unauthorized request, access token is expired") {
//token expired, request a new one
c.token, err = c.requestToken()
if err != nil {
return nil, err
}
req.Header.Del("Authorization")
return c.exec(req)
}
}
return nil, httpError{
Code: resp.StatusCode,
Body: string(body)}
}
return ioutil.ReadAll(resp.Body)
}
func (c *Client) AdRevenue(appKey string, date time.Time) ([]*AdRevenue, error) {
req, err := http.NewRequest("GET", fmt.Sprintf("https://platform.ironsrc.com/partners/userAdRevenue/v3?appKey=%s&date=%s&reportType=1",
appKey, date.Format("2006-01-02")), nil)
if err != nil {
return nil, err
}
reportUrl, err := c.exec(req)
if err != nil {
return nil, err
}
if hErr, ok := err.(httpError); ok && hErr.Code == http.StatusNotFound {
return nil, ErrRevenueNotFound
}
reportResponse, err := c.client.Get(string(reportUrl))
if err != nil {
return nil, err
}
defer reportResponse.Body.Close()
var out []*AdRevenue
reader := csv.NewReader(reportResponse.Body)
for {
record, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
return nil, errors.Wrap(err, "failed to read csv record")
}
if len(record) != 10 {
return nil, fmt.Errorf("invalid records len=%d", len(record))
}
impressions, err := strconv.ParseInt(record[8], 10, 64)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse impression=%s", record[8])
}
revenue, err := strconv.ParseFloat(record[9], 10)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse revenue=%s", record[9])
}
out = append(out, &AdRevenue{
AdUnit: record[0],
AdvertisingID: record[1],
AdvertisingIDType: record[2],
UserID: record[3],
Segment: record[4],
Placement: record[5],
AdNetwork: record[6],
ABTesting: record[7],
Impressions: impressions,
Revenue: revenue,
})
}
return out, nil
}
func (c *Client) Applications() ([]*Application, error) {
req, err := http.NewRequest("GET", "https://platform.ironsrc.com/partners/publisher/applications/v4", nil)
if err != nil {
return nil, err
}
data, err := c.exec(req)
if err != nil {
return nil, err
}
var apps []*Application
if err := json.Unmarshal(data, &apps); err != nil {
return nil, err
}
return apps, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment