Skip to content

Commit 2b575b5

Browse files
committed
Floxis Bid Adapter: redesign to seat-based architecture with ortbConverter
Major rewrite replacing teqblazeUtils with ortbConverter for ORTB 2.x compliance. Changes: - New params: seat (required), region (required), partner (required) - Endpoint URL: https://{subdomain}.floxis.tech/pbjs?seat={seat} - subdomain = region for 'floxis' partner - subdomain = {partner}-{region} for white-label partners - ORTB-native implementation with Floors Module support - 40 comprehensive tests with full code coverage - Updated documentation with examples Addresses all PR #13934 review comments from @osazos
1 parent c0fc991 commit 2b575b5

File tree

3 files changed

+458
-333
lines changed

3 files changed

+458
-333
lines changed

modules/FloxisBidAdapter.md

Lines changed: 19 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -2,84 +2,31 @@
22

33
```
44
Module Name: Floxis Bidder Adapter
5-
Module Type: Floxis Bidder Adapter
5+
Module Type: Bidder Adapter
66
Maintainer: admin@floxis.tech
77
```
8+
89
# Description
910

10-
The Floxis Bid Adapter enables integration with the Floxis programmatic advertising platform via Prebid.js. It supports banner, video (instream and outstream), and native formats, and is designed for multi-partner, multi-region use.
11+
The Floxis Bid Adapter enables integration with the Floxis programmatic advertising platform via Prebid.js. It supports banner, video (instream and outstream), and native formats.
1112

1213
**Key Features:**
1314
- Banner, Video and Native ad support
1415
- OpenRTB 2.x compliant
15-
- Privacy regulation compliance
16-
17-
## Required Params
18-
- `partner` (string): Partner name
19-
- `placementId` (integer): Placement identifier
20-
21-
## OpenRTB Blocking Params Support
22-
FloxisBidAdapter supports OpenRTB blocking parameters. You can pass the following optional params in your ad unit config:
23-
- `bcat` (array): Blocked categories
24-
- `badv` (array): Blocked advertiser domains
25-
- `bapp` (array): Blocked app bundle IDs
26-
- `battr` (array): Blocked creative attributes
16+
- Privacy regulation compliance (GDPR, USP, GPP, COPPA)
17+
- Prebid.js Floors Module support
2718

28-
These will be included in the OpenRTB request and imp objects as appropriate.
29-
30-
**Example:**
31-
```javascript
32-
pbjs.addAdUnits([
33-
{
34-
code: 'adunit-20',
35-
mediaTypes: { banner: { sizes: [[300, 250]] } },
36-
bids: [{
37-
bidder: 'floxis',
38-
params: {
39-
partner: 'floxis',
40-
placementId: 555,
41-
bcat: ['IAB1-1', 'IAB1-2'],
42-
badv: ['example.com', 'test.com'],
43-
bapp: ['com.example.app'],
44-
battr: [1, 2, 3]
45-
}
46-
}]
47-
}
48-
]);
49-
```
19+
## Supported Media Types
20+
- Banner
21+
- Video
22+
- Native
5023

5124
## Floors Module Support
52-
FloxisBidAdapter supports Prebid.js Floors Module. If a bid request provides a floor value via the Floors Module (`getFloor` function), it will be sent in the OpenRTB request as `imp.bidfloor` and `imp.bidfloorcur`. If not, you can also set a static floor using `params.bidFloor`.
53-
54-
**Example with Floors Module:**
55-
```javascript
56-
pbjs.addAdUnits([
57-
{
58-
code: 'adunit-1',
59-
mediaTypes: { banner: { sizes: [[300, 250]] } },
60-
bids: [{
61-
bidder: 'floxis',
62-
params: {
63-
partner: 'floxis',
64-
placementId: 1,
65-
bidFloor: 2.5 // optional static floor
66-
},
67-
getFloor: function({currency, mediaType, size}) {
68-
return { floor: 2.5, currency: 'USD' };
69-
}
70-
}]
71-
}
72-
]);
73-
```
25+
The Floxis Bid Adapter supports the Prebid.js [Floors Module](https://docs.prebid.org/dev-docs/modules/floors.html). Floor values are automatically included in the OpenRTB request as `imp.bidfloor` and `imp.bidfloorcur`.
7426

7527
## Privacy
7628
Privacy fields (GDPR, USP, GPP, COPPA) are handled by Prebid.js core and automatically included in the OpenRTB request.
7729

78-
## Supported Media Types
79-
- Banner
80-
- Video
81-
- Native
82-
8330
## Example Usage
8431
```javascript
8532
pbjs.addAdUnits([
@@ -89,22 +36,24 @@ pbjs.addAdUnits([
8936
bids: [{
9037
bidder: 'floxis',
9138
params: {
92-
partner: 'floxis',
93-
placementId: 1
39+
seat: 'testSeat',
40+
region: 'us-e',
41+
partner: 'floxis'
9442
}
9543
}]
9644
}
9745
]);
9846
```
9947

100-
10148
# Configuration
102-
## Required Parameters
49+
50+
## Parameters
10351

10452
| Name | Scope | Description | Example | Type |
10553
| --- | --- | --- | --- | --- |
106-
| `partner` | required | Partner identifier provided by Floxis | `floxis` | `string` |
107-
| `placementId` | required | Placement identifier provided by Floxis | `1` | `int` |
54+
| `seat` | required | Seat identifier | `'testSeat'` | `string` |
55+
| `region` | required | Region identifier for routing | `'us-e'` | `string` |
56+
| `partner` | required | Partner identifier | `'floxis'` | `string` |
10857

10958
## Testing
110-
Unit tests are provided in `test/spec/modules/floxisBidAdapter_spec.js` and cover validation, request building, and response interpretation.
59+
Unit tests are provided in `test/spec/modules/floxisBidAdapter_spec.js` and cover validation, request building, response interpretation, and bid-won notifications.

modules/floxisBidAdapter.js

Lines changed: 58 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
1-
import { ortbConverter } from '../libraries/ortbConverter/converter.js';
21
import { registerBidder } from '../src/adapters/bidderFactory.js';
32
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
4-
import * as utils from '../src/utils.js';
3+
import { ortbConverter } from '../libraries/ortbConverter/converter.js';
4+
import { triggerPixel, mergeDeep } from '../src/utils.js';
55

66
const BIDDER_CODE = 'floxis';
7-
8-
const DEFAULT_REGION = 'us';
9-
const DEFAULT_PARTNER = 'floxis';
107
const DEFAULT_BID_TTL = 300;
118
const DEFAULT_CURRENCY = 'USD';
129
const DEFAULT_NET_REVENUE = true;
1310

11+
12+
function getEndpointUrl(seat, region, partner) {
13+
const subdomain = partner === BIDDER_CODE ? region : `${partner}-${region}`;
14+
return `https://${subdomain}.floxis.tech/pbjs?seat=${encodeURIComponent(seat)}`;
15+
}
16+
1417
const CONVERTER = ortbConverter({
1518
context: {
1619
netRevenue: DEFAULT_NET_REVENUE,
1720
ttl: DEFAULT_BID_TTL,
1821
currency: DEFAULT_CURRENCY
1922
},
2023
imp(buildImp, bidRequest, context) {
21-
let imp = buildImp(bidRequest, context);
24+
const imp = buildImp(bidRequest, context);
2225
imp.secure = bidRequest.ortb2Imp?.secure ?? 1;
23-
// Floors Module support
26+
2427
let floorInfo;
2528
if (typeof bidRequest.getFloor === 'function') {
2629
try {
@@ -31,132 +34,79 @@ const CONVERTER = ortbConverter({
3134
});
3235
} catch (e) { }
3336
}
34-
const floor = floorInfo && typeof floorInfo.floor === 'number' ? floorInfo.floor : bidRequest.params?.bidFloor;
35-
const floorCur = floorInfo && typeof floorInfo.currency === 'string' ? floorInfo.currency : DEFAULT_CURRENCY;
37+
const floor = floorInfo?.floor;
38+
const floorCur = floorInfo?.currency || DEFAULT_CURRENCY;
3639
if (typeof floor === 'number' && !isNaN(floor)) {
3740
imp.bidfloor = floor;
3841
imp.bidfloorcur = floorCur;
3942
}
40-
// ORTB blocking params (imp-level)
41-
if (Array.isArray(bidRequest.params?.battr)) {
42-
imp.banner = imp.banner || {};
43-
imp.banner.battr = bidRequest.params.battr;
44-
}
43+
4544
return imp;
4645
},
4746
request(buildRequest, imps, bidderRequest, context) {
4847
const req = buildRequest(imps, bidderRequest, context);
49-
req.at = 1;
50-
req.ext = req.ext || {};
51-
req.ext.name = 'prebidjs';
52-
req.ext.version = '$prebid.version$';
53-
req.site = req.site || {};
54-
req.site.ext = req.site.ext || {};
55-
// Set placementId from first bid
56-
const firstBid = context.bidRequests[0];
57-
if (firstBid?.params?.placementId) {
58-
req.site.ext.placementId = firstBid.params.placementId;
59-
}
60-
// ORTB blocking params (request-level)
61-
const firstParams = firstBid?.params || {};
62-
if (Array.isArray(firstParams.bcat)) {
63-
req.bcat = firstParams.bcat;
64-
}
65-
if (Array.isArray(firstParams.badv)) {
66-
req.badv = firstParams.badv;
67-
}
68-
if (Array.isArray(firstParams.bapp)) {
69-
req.bapp = firstParams.bapp;
70-
}
48+
mergeDeep(req, {
49+
at: 1,
50+
ext: {
51+
prebid: {
52+
adapter: BIDDER_CODE,
53+
adapterVersion: '2.0.0'
54+
}
55+
}
56+
});
7157
return req;
7258
}
7359
});
7460

75-
function buildRequests(validBidRequests = [], bidderRequest = {}) {
76-
if (!validBidRequests || !validBidRequests.length) {
77-
return [];
78-
}
79-
const firstBid = validBidRequests[0];
80-
const partner = firstBid?.params?.partner || DEFAULT_PARTNER;
81-
const region = firstBid?.params?.region || DEFAULT_REGION;
61+
export const spec = {
62+
code: BIDDER_CODE,
63+
supportedMediaTypes: [BANNER, VIDEO, NATIVE],
8264

83-
const ortbRequest = CONVERTER.toORTB({ bidRequests: validBidRequests, bidderRequest });
84-
return [{
85-
method: 'POST',
86-
url: getFloxisUrl(partner, region),
87-
data: ortbRequest,
88-
options: {
89-
withCredentials: true,
90-
contentType: 'application/json;charset=UTF-8',
91-
}
92-
}];
93-
}
65+
isBidRequestValid(bid) {
66+
const params = bid?.params;
67+
if (!params) return false;
68+
if (typeof params.seat !== 'string' || !params.seat.length) return false;
69+
if (typeof params.region !== 'string' || !params.region.length) return false;
70+
if (typeof params.partner !== 'string' || !params.partner.length) return false;
71+
return true;
72+
},
9473

95-
function getFloxisUrl(partner, region = DEFAULT_REGION) {
96-
return `https://${partner}-${region}.floxis.tech/pbjs`;
97-
}
74+
buildRequests(validBidRequests = [], bidderRequest = {}) {
75+
if (!validBidRequests.length) return [];
9876

99-
// User sync not supported initially
100-
function getUserSyncs() {
101-
return [];
102-
}
77+
const firstBid = validBidRequests[0];
78+
const { seat, region, partner } = firstBid.params;
79+
const url = getEndpointUrl(seat, region, partner);
80+
const data = CONVERTER.toORTB({ bidRequests: validBidRequests, bidderRequest });
10381

104-
export const spec = {
105-
code: BIDDER_CODE,
106-
supportedMediaTypes: [BANNER, VIDEO, NATIVE],
107-
isBidRequestValid: function (bid) {
108-
const params = bid.params || {};
109-
if (typeof params.partner !== 'string' || !params.partner.length || !Number.isInteger(params.placementId)) {
110-
return false;
111-
}
112-
// Must have at least one media type
113-
if (!bid.mediaTypes || (!bid.mediaTypes.banner && !bid.mediaTypes.video && !bid.mediaTypes.native)) {
114-
return false;
115-
}
116-
// Banner size validation
117-
if (bid.mediaTypes.banner) {
118-
const sizes = bid.mediaTypes.banner.sizes;
119-
if (!Array.isArray(sizes) || !sizes.length || !sizes.every(size => Array.isArray(size) && size.length === 2 && size.every(Number.isInteger))) {
120-
return false;
121-
}
122-
}
123-
// Video validation
124-
if (bid.mediaTypes.video) {
125-
const v = bid.mediaTypes.video;
126-
if (!Array.isArray(v.playerSize) || !v.playerSize.length || !v.playerSize.every(size => Array.isArray(size) && size.length === 2 && size.every(Number.isInteger))) {
127-
return false;
128-
}
129-
// Check for required video params
130-
if (!Array.isArray(v.mimes) || !v.mimes.length) {
131-
return false;
132-
}
133-
if (!Array.isArray(v.protocols) || !v.protocols.length) {
134-
return false;
82+
return [{
83+
method: 'POST',
84+
url,
85+
data,
86+
options: {
87+
withCredentials: true,
88+
contentType: 'application/json'
13589
}
136-
}
137-
// Native validation (basic)
138-
if (bid.mediaTypes.native) {
139-
const n = bid.mediaTypes.native;
140-
// Require at least one asset (image, title, etc.)
141-
if (!n || Object.keys(n).length === 0) {
142-
return false;
143-
}
144-
}
145-
return true;
90+
}];
14691
},
147-
buildRequests,
92+
14893
interpretResponse(response, request) {
94+
if (!response?.body) return [];
14995
return CONVERTER.fromORTB({ request: request.data, response: response.body }).bids;
15096
},
151-
getUserSyncs,
152-
onBidWon: function (bid) {
97+
98+
getUserSyncs() {
99+
return [];
100+
},
101+
102+
onBidWon(bid) {
153103
if (bid.burl) {
154-
utils.triggerPixel(bid.burl);
104+
triggerPixel(bid.burl);
155105
}
156106
if (bid.nurl) {
157-
utils.triggerPixel(bid.nurl);
107+
triggerPixel(bid.nurl);
158108
}
159-
},
109+
}
160110
};
161111

162112
registerBidder(spec);

0 commit comments

Comments
 (0)