Skip to content

Commit 519c874

Browse files
committed
implemented adserver
1 parent 7e5f5f5 commit 519c874

14 files changed

+609
-119
lines changed

ad-server-svc/Dockerfile

-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ FROM golang:1.21-alpine3.19 AS builder
33
WORKDIR /app
44
COPY . .
55
RUN go build -o main main.go
6-
RUN apk add curl
7-
RUN curl -L https://github.com/golang-migrate/migrate/releases/download/v4.15.2/migrate.linux-amd64.tar.gz | tar xvz
86

97
# Run stage
108
FROM alpine:3.16.2

ad-server-svc/api/adserve.go

+94-50
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,120 @@
11
package api
22

33
import (
4+
"adserver/cache"
5+
"adserver/util"
6+
"encoding/json"
7+
"fmt"
8+
"log"
49
"net/http"
510

611
"github.com/gin-gonic/gin"
12+
"github.com/google/uuid"
713
)
814

9-
type RequestParams struct {
10-
AdUnitId string `json:"adunit_id"`
11-
PublisherId string `json:"publisher_id"`
12-
}
13-
14-
type Native struct {
15-
Request struct {
16-
Ver string `json:"ver"`
17-
Assets []struct {
18-
ID int `json:"id"`
19-
Required int `json:"required"`
20-
Img struct {
21-
Type int `json:"type"`
22-
W int `json:"w"`
23-
H int `json:"h"`
24-
} `json:"img"`
25-
} `json:"assets"`
26-
} `json:"request"`
27-
Ver string `json:"ver"`
28-
}
29-
30-
type Impression struct {
31-
BidFloorCur string `json:"bidfloorcur"`
32-
ID string `json:"id"`
33-
Native Native `json:"native"`
34-
}
35-
36-
type RequestBody struct {
37-
DPL string `json:"dpl"`
38-
ID string `json:"id"`
39-
Imp []Impression `json:"imp"`
40-
}
41-
4215
func (server *Server) adserve(ctx *gin.Context) {
43-
var reqParams RequestParams
16+
var reqParams cache.RequestParams
4417
if err := ctx.ShouldBindQuery(&reqParams); err != nil {
4518
ctx.JSON(http.StatusBadRequest, errResponse(err))
4619
return
4720
}
48-
49-
var reqBody RequestBody
21+
// fmt.Println("reqParams: ", reqParams)
22+
var reqBody cache.RequestBody
5023
if err := ctx.ShouldBindJSON(&reqBody); err != nil {
5124
ctx.JSON(http.StatusBadRequest, errResponse(err))
5225
return
5326
}
54-
adUnitAdress := server.config.AdManagerAddress + "/adunit?" + "adunit_id=" + reqParams.AdUnitId
55-
publisherAdress := server.config.AdManagerAddress + "/publisher?" + "publisher_id=" + reqParams.PublisherId
27+
// adUnitAdress := server.config.AdManagerAddress + "/adunit?" + "adunit_id=" + reqParams.AdUnitId
28+
// publisherAdress := server.config.AdManagerAddress + "/publisher?" + "publisher_id=" + reqParams.PublisherId
5629

57-
resp, err := http.Get(publisherAdress)
58-
if err != nil {
59-
ctx.JSON(http.StatusInternalServerError, errResponse(err))
60-
return
61-
}
62-
defer resp.Body.Close()
30+
// resp, err := http.Get(publisherAdress)
31+
// if err != nil {
32+
// ctx.JSON(http.StatusInternalServerError, errResponse(err))
33+
// return
34+
// }
35+
// defer resp.Body.Close()
6336

64-
if resp.StatusCode != http.StatusOK {
65-
ctx.JSON(http.StatusNotFound, errResponse(err))
66-
return
67-
}
37+
// if resp.StatusCode != http.StatusOK {
38+
// ctx.JSON(http.StatusNotFound, errResponse(err))
39+
// return
40+
// }
41+
42+
// resp, err = http.Get(adUnitAdress)
43+
// if err != nil {
44+
// ctx.JSON(http.StatusInternalServerError, errResponse(err))
45+
// return
46+
// }
47+
// defer resp.Body.Close()
6848

69-
resp, err = http.Get(adUnitAdress)
49+
campaignKey := "campaigns"
50+
var campaignList []cache.Campaign
51+
campaigns, err := server.store.Get(campaignKey)
7052
if err != nil {
7153
ctx.JSON(http.StatusInternalServerError, errResponse(err))
7254
return
7355
}
74-
defer resp.Body.Close()
56+
json.Unmarshal([]byte(campaigns), &campaignList)
57+
var activeCampaigns []cache.Campaign
58+
var activeAds []cache.Ad
59+
var rankedAds []cache.Ad
60+
var bids *[]cache.Bid
61+
for _, campaign := range campaignList {
62+
startDate, err := util.GetTime(campaign.StartDate)
63+
if err != nil {
64+
ctx.JSON(http.StatusInternalServerError, errResponse(err))
65+
return
66+
}
67+
endDate, err := util.GetTime(campaign.EndDate)
68+
if err != nil {
69+
ctx.JSON(http.StatusInternalServerError, errResponse(err))
70+
return
71+
}
72+
if util.WithinDuration(startDate, endDate) {
73+
activeCampaigns = append(activeCampaigns, campaign)
74+
hkey := campaign.CampaignID
75+
ads, err := server.store.HGetAll(hkey)
76+
if err != nil {
77+
ctx.JSON(http.StatusInternalServerError, errResponse(err))
78+
return
79+
}
80+
// fmt.Println("number of ads in campaign: ", len(ads))
81+
for _, ad := range ads {
82+
var adObj cache.Ad
83+
json.Unmarshal([]byte(ad), &adObj)
84+
// fmt.Println("checking ad: ", adObj.AdID)
85+
adAvailable, err := util.IsAdAvailable(adObj, reqParams, reqBody)
86+
if err != nil {
87+
ctx.JSON(http.StatusInternalServerError, errResponse(err))
88+
return
89+
}
90+
fmt.Println("can ad ", adObj.AdID, " be served: ", adAvailable)
91+
if adAvailable {
92+
activeAds = append(activeAds, adObj)
93+
}
94+
}
7595

96+
// fmt.Println("no of active ads: ", len(activeAds))
97+
rankedAds = util.RankAds(activeAds)
98+
fmt.Println("ranked ads: ", rankedAds)
99+
bidParams := cache.BidParams{
100+
RankedAds: &rankedAds,
101+
RequestBody: reqBody,
102+
}
103+
bids, err = server.getBids(bidParams)
104+
if err != nil {
105+
log.Println(err.Error())
106+
ctx.JSON(http.StatusInternalServerError, errResponse(err))
107+
return
108+
}
109+
}
110+
}
111+
adServeResponse := cache.AdServeResponse{
112+
Id: uuid.New().String(),
113+
Bid: *bids,
114+
Bidid: uuid.New().String(),
115+
Cur: "USD",
116+
}
117+
// fmt.Println("activeAds: ", activeAds)
118+
// fmt.Println("activeCampaigns: ", activeCampaigns)
119+
ctx.JSON(http.StatusOK, adServeResponse)
76120
}

ad-server-svc/api/bids.go

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package api
2+
3+
import (
4+
"adserver/cache"
5+
"encoding/base64"
6+
"encoding/json"
7+
"fmt"
8+
"time"
9+
10+
"github.com/google/uuid"
11+
)
12+
13+
func (server *Server) getBids(bidParams cache.BidParams) (*[]cache.Bid, error) {
14+
var bids []cache.Bid
15+
var impIDTaken = make(map[string]bool)
16+
currentTime := time.Now().Unix()
17+
ads := bidParams.RankedAds
18+
adBody := bidParams.RequestBody
19+
for _, ad := range *ads {
20+
bidAssigned := false
21+
creative, err := server.store.GetCreatives(ad.CreativeID)
22+
if err != nil {
23+
fmt.Println("Error getting creatives:", err)
24+
return nil, err
25+
}
26+
for _, asset := range creative.Assets {
27+
if asset.Type == "IMAGE" {
28+
for _, imp := range adBody.Imp {
29+
if _, ok := impIDTaken[imp.ID]; ok {
30+
// fmt.Println("impID already taken: ", imp.ID)
31+
continue
32+
}
33+
for _, img := range imp.Native.Request.Assets {
34+
// fmt.Println("checking image: ", img.Img.Type, img.Img.W == asset.Width, img.Img.H == asset.Height, imp.ID)
35+
if img.Img.Type == 3 && img.Img.W == asset.Width && img.Img.H == asset.Height {
36+
iidData := cache.IIDData{
37+
AdID: ad.AdID,
38+
CreativeID: ad.CreativeID,
39+
AdUnitId: adBody.ID,
40+
CampaignID: ad.CampaignID,
41+
RequestTimeStamp: currentTime,
42+
AdvertiserID: ad.AdvertiserID,
43+
}
44+
jsonData, err := json.Marshal(iidData)
45+
if err != nil {
46+
fmt.Println("Error marshalling JSON:", err)
47+
return nil, err
48+
}
49+
admJSON, err := json.Marshal(imp)
50+
if err != nil {
51+
fmt.Println("Error marshalling to JSON:", err)
52+
return nil, err
53+
}
54+
formClickUrl := server.config.ClickUrl + "?iid=" + base64.StdEncoding.EncodeToString([]byte(jsonData))
55+
formRenderUrl := server.config.RenderUrl + "?iid=" + base64.StdEncoding.EncodeToString([]byte(jsonData))
56+
newBid := cache.Bid{
57+
Id: uuid.New().String(),
58+
Impid: imp.ID,
59+
AdID: ad.AdID,
60+
Adm: string(admJSON),
61+
Cid: ad.CampaignID,
62+
Crid: ad.CreativeID,
63+
Ext: cache.Ext{
64+
ClickUrl: formClickUrl,
65+
AdType: "NATIVE",
66+
Kslotid: adBody.ID + "_" + imp.ID,
67+
AdEndTime: ad.EndDate,
68+
LandingUrl: ad.LandingURL,
69+
RenderUrl: formRenderUrl,
70+
},
71+
}
72+
bids = append(bids, newBid)
73+
impIDTaken[imp.ID] = true
74+
bidAssigned = true
75+
break
76+
}
77+
}
78+
if bidAssigned {
79+
break
80+
}
81+
}
82+
}
83+
if bidAssigned {
84+
break
85+
}
86+
}
87+
}
88+
if len(bids) == 0 {
89+
return nil, fmt.Errorf("no ads available")
90+
}
91+
return &bids, nil
92+
}

ad-server-svc/api/main_test.go

-23
This file was deleted.

ad-server-svc/api/server.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package api
22

33
import (
4+
"adserver/cache"
45
"adserver/util"
56

67
"github.com/gin-contrib/cors"
@@ -11,11 +12,13 @@ import (
1112
type Server struct {
1213
config util.Config
1314
router *gin.Engine
15+
store cache.Store
1416
}
1517

16-
func NewServer(config util.Config) (*Server, error) {
18+
func NewServer(config util.Config, store cache.Store) (*Server, error) {
1719
server := &Server{
1820
config: config,
21+
store: store,
1922
}
2023

2124
server.setupRouter()

ad-server-svc/app.env

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
DB_DRIVER=postgres
2-
DB_SOURCE=postgresql://postgres:postgres@localhost:5432/bitemap?sslmode=disable
2+
DB_SOURCE=postgresql://postgres:postgres@localhost:5432/adpulse?sslmode=disable
3+
REDIS_HOST = 'redis-10477.c331.us-west1-1.gce.cloud.redislabs.com'
4+
REDIS_PORT = 10477
5+
REDIS_USERNAME = 'adpulse-admin'
6+
REDIS_PASSWORD = 'AdpulseAdmin#123'
37
SERVER_ADDRESS=0.0.0.0:8080
4-
AD_MANAGER_ADDRESS=http://localhost:8000
8+
AD_MANAGER_ADDRESS=http://localhost:5001
9+
CLICK_URL=https://ads-testing.adpulse.com/apis/engagement/clk
10+
RENDER_URL=https://ads-testing.adpulse.com/apis/engagement/csc

0 commit comments

Comments
 (0)