Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WORK-IN-PROGRESS] Implement multi-piece ads #266

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion services/ad-server/src/public/js/ad-server-lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ class AdServerLib {
paragraphEl.className = 'font-mono text-sm';
document.getElementById(divId).appendChild(paragraphEl);

if (type === 'image') {
if (type === 'image' || type === 'multi-piece') {
[containerFrameEl.width, containerFrameEl.height] = size;
} else if (type === 'video') {
// The video creative does not actually render anything, and it only contains
Expand Down
5 changes: 4 additions & 1 deletion services/ad-server/src/public/js/decision-logic.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ function scoreAd(
// If it's a video ad, there is no contextual video auction in this demo to compare
// the bid against, so we just return the desirability score as the bid itself,
// and the highest bid will win the auction
if (auctionConfig.sellerSignals.adType === 'video') {
if (
auctionConfig.sellerSignals.adType === 'video' ||
auctionConfig.sellerSignals.adType === 'multi-piece'
) {
desirability = bid;
} else {
// For an image ad, we compare the PA auction bid against the bid floor set by the
Expand Down
48 changes: 40 additions & 8 deletions services/dsp-a/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ app.use((req, res, next) => {
next();
});

app.use((req, res, next) => {
// opt-in fencedframe
if (req.get('sec-fetch-dest') === 'fencedframe') {
res.setHeader('Supports-Loading-Mode', 'fenced-frame');
}
next();
});

app.use(
express.static('src/public', {
setHeaders: (res: Response, path, stat) => {
Expand All @@ -94,14 +102,6 @@ app.use(
}),
);

app.use((req, res, next) => {
// opt-in fencedframe
if (req.get('sec-fetch-dest') === 'fencedframe') {
res.setHeader('Supports-Loading-Mode', 'fenced-frame');
}
next();
});

app.set('view engine', 'ejs');
app.set('views', 'src/views');

Expand Down Expand Up @@ -144,6 +144,25 @@ app.get('/interest-group.json', async (req: Request, res: Response) => {
imageCreative.searchParams.append('advertiser', advertiser as string);
imageCreative.searchParams.append('id', id as string);

const multiPieceCreativeContainer = new URL(
`https://${DSP_A_HOST}:${EXTERNAL_PORT}/html/multi-piece-ad/container.html`,
);
const multiPieceCreativeComponentA = new URL(
`https://${DSP_A_HOST}:${EXTERNAL_PORT}/html/multi-piece-ad/component-a.html`,
);
const multiPieceCreativeComponentB = new URL(
`https://${DSP_A_HOST}:${EXTERNAL_PORT}/html/multi-piece-ad/component-b.html`,
);
const multiPieceCreativeComponentC = new URL(
`https://${DSP_A_HOST}:${EXTERNAL_PORT}/html/multi-piece-ad/component-c.html`,
);
const multiPieceCreativeComponentD = new URL(
`https://${DSP_A_HOST}:${EXTERNAL_PORT}/html/multi-piece-ad/component-d.html`,
);
const multiPieceCreativeComponentE = new URL(
`https://${DSP_A_HOST}:${EXTERNAL_PORT}/html/multi-piece-ad/component-e.html`,
);

const videoCreativeForSspA = new URL(
`https://${DSP_A_HOST}:${EXTERNAL_PORT}/html/video-ad-creative.html?sspVastUrl=${SSP_A_VAST_URL}`,
);
Expand Down Expand Up @@ -207,6 +226,19 @@ app.get('/interest-group.json', async (req: Request, res: Response) => {
seller: SSP_B,
},
},
{
renderUrl: multiPieceCreativeContainer,
metadata: {
adType: 'multi-piece',
},
},
],
adComponents: [
{renderUrl: multiPieceCreativeComponentA},
{renderUrl: multiPieceCreativeComponentB},
{renderUrl: multiPieceCreativeComponentC},
{renderUrl: multiPieceCreativeComponentD},
{renderUrl: multiPieceCreativeComponentE},
],
});
});
Expand Down
21 changes: 21 additions & 0 deletions services/dsp-a/src/public/html/multi-piece-ad/component-a.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DSP-A component ad A</title>
<style>
body {
margin: 0;
padding: 0;
text-align: center;
font-family: sans-serif;
background-color: white;
}
</style>
</head>

<body>
A
</body>
</html>
21 changes: 21 additions & 0 deletions services/dsp-a/src/public/html/multi-piece-ad/component-b.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DSP-A component ad B</title>
<style>
body {
margin: 0;
padding: 0;
text-align: center;
font-family: sans-serif;
background-color: white;
}
</style>
</head>

<body>
B
</body>
</html>
21 changes: 21 additions & 0 deletions services/dsp-a/src/public/html/multi-piece-ad/component-c.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DSP-A component ad C</title>
<style>
body {
margin: 0;
padding: 0;
text-align: center;
font-family: sans-serif;
background-color: white;
}
</style>
</head>

<body>
C
</body>
</html>
21 changes: 21 additions & 0 deletions services/dsp-a/src/public/html/multi-piece-ad/component-d.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DSP-A component ad D</title>
<style>
body {
margin: 0;
padding: 0;
text-align: center;
font-family: sans-serif;
background-color: white;
}
</style>
</head>

<body>
D
</body>
</html>
21 changes: 21 additions & 0 deletions services/dsp-a/src/public/html/multi-piece-ad/component-e.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DSP-A component ad E</title>
<style>
body {
margin: 0;
padding: 0;
text-align: center;
font-family: sans-serif;
background-color: white;
}
</style>
</head>

<body>
E
</body>
</html>
89 changes: 89 additions & 0 deletions services/dsp-a/src/public/html/multi-piece-ad/container.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DSP-A ad container</title>
<style>
body {
height: 300px;
width: 250px;
overflow: hidden;
font-family: sans-serif;
background: #feb;
text-align: center;
margin: 0 auto;
}

#ad-label {
position: absolute;
top: 0;
left: 0;
margin: 5px;
padding: 5px;
background: #3f3f3f;
color: white;
}

#components-container--fenced-frame {
margin-top: 50px;
width: 100%;
padding: 5px;
border: 1px solid blue;
}

#components-container--iframe {
margin-top: 20px;
width: 100%;
padding: 5px;
border: 1px solid red;
}

p {
margin: 3px;
font-size: 14px;
}
</style>
</head>

<body>
<div id="adContainer">
<div id="ad-label">DSP-A multi-piece ad</div>
<div id="components-container--fenced-frame">
<p>Fenced frame components</p>
</div>
<div id="components-container--iframe"><p>Iframe components</p></div>
</div>
<script>
// For the DSP-B multi-piece, we display the components in sequential order

function renderFencedFrameAds(count) {
const containerEl = document.getElementById('components-container--fenced-frame')
const componentAdConfigs = window.fence.getNestedConfigs().slice(0, count);

for (const componentAdConfig of componentAdConfigs) {
const frame = document.createElement('fencedframe')
frame.config = componentAdConfig
frame.style.height = '20px'
frame.style.width = '20px'
containerEl.appendChild(frame)
}
}

function renderIframeAds(count) {
const containerEl = document.getElementById('components-container--iframe')

for (const componentAd of navigator.adAuctionComponents(count)) {
const frame = document.createElement('iframe')
frame.src = componentAd
frame.style.height = '20px'
frame.style.width = '20px'
containerEl.appendChild(frame)
}
}

renderFencedFrameAds(5)
renderIframeAds(5)
</script>
</body>
</html>
39 changes: 26 additions & 13 deletions services/dsp-a/src/public/js/bidding-logic.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,38 +14,51 @@
limitations under the License.
*/

const IMAGE_AD_TYPE = 'image';
const VIDEO_AD_TYPE = 'video';
const MULTI_PIECE_AD_TYPE = 'multi-piece';

function generateBid(
interestGroup,
auctionSignals,
perBuyerSignals,
trustedBiddingSignals,
browserSignals,
) {
const {ads} = interestGroup;
const {ads, adComponents} = interestGroup;
const {adType} = auctionSignals;
const {seller, topLevelSeller} = browserSignals;

let render;

if (adType === 'image') {
render = ads.find((ad) => ad.metadata.adType === 'image')?.renderUrl;
} else if (adType === 'video') {
// We look through the video ads passed in from the interest group and
// select the ad that matches the component seller's origin
render = ads.find(
(ad) =>
ad.metadata.adType === 'video' && ad.metadata.seller.includes(seller),
).renderUrl;
switch (adType) {
case IMAGE_AD_TYPE:
render = ads.find(
({metadata}) => metadata.adType === IMAGE_AD_TYPE,
)?.renderUrl;
break;
case VIDEO_AD_TYPE:
// We look through the video ads passed in from the interest group and
// select the ad that matches the component seller's origin
render = ads.find(
({metadata}) =>
metadata.adType === VIDEO_AD_TYPE && metadata.seller.includes(seller),
).renderUrl;
break;
case MULTI_PIECE_AD_TYPE:
render = ads.find(
({metadata}) => metadata.adType === MULTI_PIECE_AD_TYPE,
).renderUrl;
break;
}

const response = {
return {
// We return a random bid of 0 to 100
bid: Math.floor(Math.random() * 100, 10),
render,
adComponents: adComponents.map(({renderUrl}) => ({url: renderUrl})),
allowComponentAuction: !!topLevelSeller,
};

return response;
}

function reportWin(
Expand Down
Loading
Loading