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

Rediads bid adapter #12525

Open
wants to merge 10 commits into
base: master
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
103 changes: 103 additions & 0 deletions modules/rediadsBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { ortbConverter } from '../libraries/ortbConverter/converter.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { deepSetValue, logWarn, logError } from '../src/utils.js';
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
import { config } from '../src/config.js';

const BIDDER_CODE = 'rediads';
const ENDPOINT_URL = 'https://bidding.rediads.com/openrtb2/auction';
const STAGING_ENDPOINT_URL = 'https://stagingbidding.rediads.com/openrtb2/auction';
const DEFAULT_CURRENCY = 'USD';
const LOG_PREFIX = 'Rediads: ';

const MEDIA_TYPES = {
[BANNER]: 1,
[VIDEO]: 2,
[NATIVE]: 4,
};

const converter = ortbConverter({
context: {
netRevenue: true,
ttl: 300,
currency: DEFAULT_CURRENCY,
},
bidResponse(buildBidResponse, bid, context) {
let mediaType = 'banner'; // Default media type

if (bid.vastXml || bid.vastUrl || (bid.adm && bid.adm.startsWith('<VAST'))) {
mediaType = 'video';
} else if (bid.adm && bid.adm.includes('"native"') && bid.adm.includes('"assets"')) {
mediaType = 'native';
}

bid.mediaType = mediaType;
bid.mtype = MEDIA_TYPES[mediaType];

if (bid.mediaType === BANNER) {
bid.ad = bid.adm;
}
return buildBidResponse(bid, context);
},
});

export const spec = {
code: BIDDER_CODE,
supportedMediaTypes: [NATIVE, BANNER, VIDEO],
isBidRequestValid: function (bid) {
let isValid = false;
const accountID = bid?.params?.account_id
if (accountID && typeof accountID === 'string') {
isValid = true;
} else {
logError(`${LOG_PREFIX} account_id is missing from params or is not of type "string"`)
}
return isValid;
},
buildRequests(bidRequests, bidderRequest) {
const params = bidRequests[0]?.params || {};
let data = {};
let FINAL_ENDPOINT_URL = params.endpoint || ENDPOINT_URL
try {
data = converter.toORTB({ bidRequests, bidderRequest });
const testBidsRequested = location.hash.includes('rediads-test-bids');
const stagingEnvRequested = location.hash.includes('rediads-staging');

if (stagingEnvRequested) {
FINAL_ENDPOINT_URL = STAGING_ENDPOINT_URL;
}

deepSetValue(data, 'ext.rediads.params', params);
deepSetValue(data, 'site.content', config.getConfig('content'));

if (testBidsRequested) {
deepSetValue(data, 'test', 1);
logWarn(`${LOG_PREFIX} test bids are enabled as rediads-test-bids is present in page URL hash.`)
}
} catch (err) {
logError(`${LOG_PREFIX} encountered an error while building bid requests :: ${err}`)
}

return [
{
method: 'POST',
url: FINAL_ENDPOINT_URL,
data,
},
];
},
interpretResponse(response, request) {
let bids = [];
try {
bids = converter.fromORTB({
response: response.body,
request: request.data,
}).bids
} catch (err) {
logError(`${LOG_PREFIX} encountered an error while processing bid responses :: ${err}`)
}
return bids;
},
};

registerBidder(spec);
38 changes: 38 additions & 0 deletions modules/rediadsBidAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Overview

```
Module Name: RediAds Bid Adapter
Module Type: Bidder Adapter
Maintainer: support@rediads.com
```

# Description

Connect to RediAds exchange for bids.

The RediAds adapter requires setup and approval.
Please reach out to support@rediads.com for more information.

# Test Parameters
```
var adUnits = [
{
code: 'test-div',
mediaTypes: {
banner: {
sizes: [[300, 250]]
}
},
bids: [
{
bidder: "rediads",
params: {
account_id: '123',
site: 'rediads.com',
slot: '321'
}
}
]
}
];
```
133 changes: 133 additions & 0 deletions test/spec/modules/rediadsBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { expect } from 'chai';
import { spec } from '../../../modules/rediadsBidAdapter';

describe('rediads Bid Adapter', function () {
const BIDDER_CODE = 'rediads';
const STAGING_ENDPOINT_URL = 'https://stagingbidding.rediads.com/openrtb2/auction';

const bidRequest = {
bidder: BIDDER_CODE,
params: {
account_id: '12345',
},
mediaTypes: {
banner: {
sizes: [[300, 250], [728, 90]],
},
},
adUnitCode: 'adunit-code',
bidId: '2ab03f1234',
auctionId: '123456789',
};

const bidderRequest = {
bidderCode: BIDDER_CODE,
refererInfo: {
referer: 'http://example.com',
},
};

const resetHash = (originalHash) => {
// Reset the hash, ensuring no trailing #
if (originalHash) {
location.hash = originalHash;
} else {
history.replaceState(null, '', location.pathname + location.search);
}
}

describe('isBidRequestValid', function () {
it('should return true for valid bid requests', function () {
expect(spec.isBidRequestValid(bidRequest)).to.equal(true);
});

it('should return false if account_id is missing', function () {
const invalidBid = { ...bidRequest, params: {} };
expect(spec.isBidRequestValid(invalidBid)).to.equal(false);
});
});

describe('buildRequests', function () {
it('should build a valid request with correct data', function () {
const requests = spec.buildRequests([bidRequest], bidderRequest);
expect(requests).to.be.an('array').that.is.not.empty;

const request = requests[0];
expect(request.method).to.equal('POST');
expect(request.url).that.is.not.empty;
expect(request.data).to.have.property('ext');
expect(request.data.ext.rediads.params).to.deep.equal(bidRequest.params);
});

it('should include test flag if testBidsRequested is true', function () {
const originalHash = location.hash;
location.hash = '#rediads-test-bids';

const requests = spec.buildRequests([bidRequest], bidderRequest);
expect(requests[0].data.test).to.equal(1);

resetHash(originalHash);
});

it('should set staging environtment if stagingEnvRequested is true', function () {
const originalHash = location.hash;
location.hash = '#rediads-staging';

const requests = spec.buildRequests([bidRequest], bidderRequest);
expect(requests[0].url).to.equal(STAGING_ENDPOINT_URL);

resetHash(originalHash);
});
});

describe('interpretResponse', function () {
it('should interpret and return valid bid responses for banner bid', function () {
const serverResponse = {
body: {
seatbid: [
{
bid: [
{
price: 1.23,
impid: '2ab03f1234',
adm: '<div>Ad</div>',
crid: 'creative123',
w: 300,
h: 250,
},
],
},
],
},
};
const requestObj = spec.buildRequests([bidRequest], bidderRequest);
const bids = spec.interpretResponse(serverResponse, requestObj[0]);
expect(bids).to.be.an('array').that.is.not.empty;

const bid = bids[0];
expect(bid).to.include({
requestId: '2ab03f1234',
cpm: 1.23,
creativeId: 'creative123',
width: 300,
height: 250,
ad: '<div>Ad</div>',
});
expect(bid.mediaType).to.equal('banner');
});

it('should return an empty array for invalid responses', function () {
const invalidResponse = { body: {} };
const updatedBidRequest = {...bidRequest, params: undefined}
const requestObj = spec.buildRequests([updatedBidRequest], bidderRequest);
const bids = spec.interpretResponse(invalidResponse, requestObj[0]);
expect(bids).to.be.an('array').that.is.empty;
});
});

describe('Miscellaneous', function () {
it('should support multiple media types', function () {
expect(spec.supportedMediaTypes).to.include.members(['banner', 'native', 'video']);
});
});
});