Skip to content

Commit

Permalink
Rediads Bid Adapter : initial release (#12525)
Browse files Browse the repository at this point in the history
* feature: Rediads bid adapter

* Test Cases updated | Bid Adapter improved

* Bug fix

* Readme updated

* Test cases fixed

* test cases updated

* test cases reset hash fixed

* Add semicolon

* remove semicolon

* Get site content from bidrequest instead of config

---------

Co-authored-by: symplorpro <[email protected]>
Co-authored-by: Symplor <[email protected]>
Co-authored-by: symplorpro <[email protected]>
  • Loading branch information
4 people authored Dec 17, 2024
1 parent e2a4631 commit b39d070
Show file tree
Hide file tree
Showing 3 changed files with 273 additions and 0 deletions.
102 changes: 102 additions & 0 deletions modules/rediadsBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
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';

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 || {};
const siteContent = bidRequests[0]?.site?.content || null;
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', siteContent);

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: [email protected]
```

# Description

Connect to RediAds exchange for bids.

The RediAds adapter requires setup and approval.
Please reach out to [email protected] 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']);
});
});
});

0 comments on commit b39d070

Please sign in to comment.