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

Michao Bid Adapter: Initial release #12507

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ee93a3f
Michao Bid Adapter: Initial release
hogekai Nov 26, 2024
2b0d4fd
Michao Bid Adapter: Fix incomprehensible integration tests
hogekai Nov 27, 2024
ae817a3
Michao Bid Adapter: Explicitly specify VAST XML
hogekai Nov 27, 2024
27d6259
Michao Bid Adapter: Support for rewarded advertising
hogekai Nov 27, 2024
522b79d
Michao Bid Adapter: Re-run E2e test
hogekai Nov 27, 2024
d8313fc
Michao Bid Adapter: Support for native format
hogekai Dec 2, 2024
2228613
Michao Bid Adapter: Change renderer URL
hogekai Dec 2, 2024
08d9ce2
Michao Bid Adapter: Support for blocked categories and blocked advert…
hogekai Dec 3, 2024
dfd29c9
Michao Bid Adapter: Change placement to string type
hogekai Dec 4, 2024
1fb9d7e
Michao Bid Adapter: Add minimum bid price
hogekai Dec 4, 2024
0c0d5e4
Michao Bid Adapter: Added log system validation to integration testin…
hogekai Dec 4, 2024
b869d1c
Michao Bid Adapter: Add partner ID parameter
hogekai Dec 12, 2024
3292094
Michao Bid Adapter: Refactoring
hogekai Dec 13, 2024
a35a9a6
Michao Bid Adapter: Change the method used by the property validation…
hogekai Dec 13, 2024
7457a83
Michao Bid Adapter: Remove video property assertion
hogekai Dec 13, 2024
71b0f01
Michao Bid Adapter: Remove assertions on video properties that you fo…
hogekai Dec 13, 2024
2049dd8
Michao Bid Adapter: Explicitly delete native objects
hogekai Dec 13, 2024
3358914
Michao Bid Adapter: Rename Renderer URL to outstream renderer URL
hogekai Dec 13, 2024
f8ee3aa
Michao Bid Adapter: Swap the order in which bid requests are pushed
hogekai Dec 23, 2024
ac07297
Michao Bid Adapter: Explicitly specify version of outstream renderer URL
hogekai Dec 28, 2024
2d6f8b5
Michao Bid Adapter: Tests are supported for out-stream renderer URL v…
hogekai Dec 29, 2024
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
352 changes: 352 additions & 0 deletions modules/michaoBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,352 @@
import { ortbConverter } from '../libraries/ortbConverter/converter.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { config } from '../src/config.js';
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
import { Renderer } from '../src/Renderer.js';
import {
deepSetValue,
isArray,
isBoolean,
isNumber,
isStr,
logError,
replaceAuctionPrice,
triggerPixel,
} from '../src/utils.js';

const ENV = {
BIDDER_CODE: 'michao',
SUPPORTED_MEDIA_TYPES: [BANNER, VIDEO, NATIVE],
ENDPOINT: 'https://rtb.michao-ssp.com/openrtb/prebid',
NET_REVENUE: true,
DEFAULT_CURRENCY: 'USD',
OUTSTREAM_RENDERER_URL:
hogekai marked this conversation as resolved.
Show resolved Hide resolved
'https://cdn.jsdelivr.net/npm/in-renderer-js@latest/dist/in-video-renderer.umd.min.js',
};

export const spec = {
code: ENV.BIDDER_CODE,
supportedMediaTypes: ENV.SUPPORTED_MEDIA_TYPES,

isBidRequestValid: function (bid) {
const params = bid.params;

if (!isNumber(params?.site)) {
domainLogger.invalidSiteError(params?.site);
return false;
}

if (!isStr(params?.placement)) {
domainLogger.invalidPlacementError(params?.placement);
return false;
}

if (params?.partner) {
if (!isNumber(params?.partner)) {
domainLogger.invalidPartnerError(params?.partner);
return false;
}
}

if (params?.badv) {
if (!isArray(params?.badv)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the following two params you must also support from ortb.

domainLogger.invalidBadvError(params?.badv);
return false;
}
}

if (params?.bcat) {
if (!isArray(params?.bcat)) {
domainLogger.invalidBcatError(params?.bcat);
return false;
}
}

if (bid.params?.reward) {
if (!isBoolean(params?.reward)) {
domainLogger.invalidRewardError(params?.reward);
return false;
}
}

const video = bid.mediaTypes?.video;
Copy link
Collaborator

@patmmccann patmmccann Jan 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these type checks are not appropriate in your adapter, the type check should have already happened somewhere else. If not, let's add it in that place so we don't have every adapter validating its input duplicatively

if (video) {
if (!video.context) {
domainLogger.invalidVideoContext();
return false;
}

if (!video.playerSize || !Array.isArray(video.playerSize)) {
domainLogger.invalidVideoPlayerSize();
return false;
}

if (!isNumber(video.minduration)) {
domainLogger.invalidVideoMinDuration();
return false;
}

if (!isNumber(video.maxduration)) {
domainLogger.invalidVideoMaxDuration();
return false;
}

if (!Array.isArray(video.mimes) || video.mimes.length === 0) {
domainLogger.invalidVideoMimes();
return false;
}

if (!Array.isArray(video.protocols) || video.protocols.length === 0) {
domainLogger.invalidVideoProtocols();
return false;
}
}

return true;
},

buildRequests: function (validBidRequests, bidderRequest) {
const bidRequests = [];

validBidRequests.forEach((validBidRequest) => {
let bidRequestEachFormat = [];

if (validBidRequest.mediaTypes?.banner) {
bidRequestEachFormat.push({
...validBidRequest,
mediaTypes: {
banner: validBidRequest.mediaTypes.banner,
},
});
}

if (validBidRequest.mediaTypes?.native) {
bidRequestEachFormat.push({
...validBidRequest,
mediaTypes: {
native: validBidRequest.mediaTypes.native,
},
});
}

if (validBidRequest.mediaTypes?.video) {
bidRequestEachFormat.push({
...validBidRequest,
mediaTypes: {
video: validBidRequest.mediaTypes.video,
},
});
}

bidRequests.push(buildRequest(bidRequestEachFormat, bidderRequest));
});

return bidRequests;
},

interpretResponse: function (serverResponse, request) {
return converter.fromORTB({
response: serverResponse.body,
request: request.data,
}).bids;
},

getUserSyncs: function (
syncOptions,
serverResponses,
gdprConsent,
uspConsent
) {
if (syncOptions.iframeEnabled) {
return [
{
type: 'iframe',
url:
'https://sync.michao-ssp.com/cookie-syncs?' +
generateGdprParams(gdprConsent),
},
];
}

return [];
},

onBidBillable: function (bid) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exciting to see this getting defined

if (bid.burl && isStr(bid.burl)) {
triggerPixel(generateBillableUrl(bid));
}
},
};

export const domainLogger = {
invalidSiteError(value) {
logError(
`Michao Bid Adapter: Invalid site ID. Expected number, got ${typeof value}. Value: ${value}`
);
},

invalidPlacementError(value) {
logError(
`Michao Bid Adapter: Invalid placement. Expected string, got ${typeof value}. Value: ${value}`
);
},

invalidPartnerError(value) {
logError(
`Michao Bid Adapter: Invalid partner ID. Expected number, got ${typeof value}. Value: ${value}`
);
},

invalidBadvError(value) {
logError(
`Michao Bid Adapter: Invalid badv. Expected array, got ${typeof value}`
);
},

invalidBcatError(value) {
logError(
`Michao Bid Adapter: Invalid bcat. Expected array, got ${typeof value}`
);
},

invalidRewardError(value) {
logError(
`Michao Bid Adapter: Invalid reward. Expected boolean, got ${typeof value}. Value: ${value}`
);
},

invalidVideoContext() {
logError('Michao Bid Adapter: Video context is not set');
},

invalidVideoPlayerSize() {
logError('Michao Bid Adapter: Video playerSize is not set or invalid');
},

invalidVideoMinDuration() {
logError('Michao Bid Adapter: Video minDuration is not set or invalid');
},

invalidVideoMaxDuration() {
logError('Michao Bid Adapter: Video maxDuration is not set or invalid');
},

invalidVideoMimes() {
logError('Michao Bid Adapter: Video mimes is not set or invalid');
},

invalidVideoProtocols() {
logError('Michao Bid Adapter: Video protocols is not set or invalid');
},
};

function buildRequest(bidRequests, bidderRequest) {
const openRTBBidRequest = converter.toORTB({
bidRequests: bidRequests,
bidderRequest,
});

return {
method: 'POST',
url: ENV.ENDPOINT,
data: openRTBBidRequest,
options: { contentType: 'application/json', withCredentials: true },
};
}

function generateGdprParams(gdprConsent) {
let gdprParams = '';

if (typeof gdprConsent === 'object') {
if (gdprConsent?.gdprApplies) {
gdprParams = `gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${
gdprConsent.consentString || ''
}`;
}
}

return gdprParams;
}

function generateBillableUrl(bid) {
return replaceAuctionPrice(bid.burl, bid.originalCpm || bid.cpm);
}

const converter = ortbConverter({
request(buildRequest, imps, bidderRequest, context) {
const bidRequest = context.bidRequests[0];
const openRTBBidRequest = buildRequest(imps, bidderRequest, context);
openRTBBidRequest.cur = [ENV.DEFAULT_CURRENCY];
openRTBBidRequest.test = config.getConfig('debug') ? 1 : 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit of a dangerous pattern, not all debug requests are test requests

openRTBBidRequest.bcat = bidRequest.params?.bcat || [];
Copy link
Collaborator

@patmmccann patmmccann Jan 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be defined on the ortb request object as well, am I reading correctly you only support it as a param?

openRTBBidRequest.badv = bidRequest.params?.badv || [];
deepSetValue(
openRTBBidRequest,
'site.id',
bidRequest.params.site.toString()
);
if (bidRequest?.schain) {
deepSetValue(openRTBBidRequest, 'source.schain', bidRequest.schain);
}

if (bidRequest.params.partner) {
deepSetValue(
openRTBBidRequest,
'site.publisher.ext.partner',
bidRequest.params.partner.toString()
);
}

return openRTBBidRequest;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your doc says you only support site and user, but you seem to support device and other parts of ortb as well?

},

imp(buildImp, bidRequest, context) {
const imp = buildImp(bidRequest, context);
deepSetValue(imp, 'ext.placement', bidRequest.params.placement.toString());
deepSetValue(imp, 'rwdd', bidRequest.params?.reward ? 1 : 0);
deepSetValue(
imp,
'bidfloor',
isNumber(bidRequest.params?.bidFloor) ? bidRequest.params?.bidFloor : 0
);

if (!bidRequest.mediaTypes?.native) {
delete imp.native;
}

return imp;
},

bidResponse(buildBidResponse, bid, context) {
const bidResponse = buildBidResponse(bid, context);
const { bidRequest } = context;
if (
bidResponse.mediaType === VIDEO &&
bidRequest.mediaTypes.video.context === 'outstream'
) {
bidResponse.vastXml = bid.adm;
const renderer = Renderer.install({
url: ENV.OUTSTREAM_RENDERER_URL,
id: bidRequest.bidId,
adUnitCode: bidRequest.adUnitCode,
});
renderer.render(() => {
bid.renderer.push(() => {
const inRenderer = new window.InVideoRenderer();
inRenderer.render(bid.adUnitCode, bid);
});
});
bidResponse.renderer = renderer;
}

return bidResponse;
},

context: {
netRevenue: ENV.NET_REVENUE,
currency: ENV.DEFAULT_CURRENCY,
ttl: 360,
},
});

registerBidder(spec);
Loading
Loading