From 09347c2846d52f53e9f17ac4faa156b6a0db8471 Mon Sep 17 00:00:00 2001 From: yosei-ito Date: Thu, 26 Jun 2025 15:58:28 +0900 Subject: [PATCH 1/3] add instl support --- modules/fluctBidAdapter.js | 4 ++++ test/spec/modules/fluctBidAdapter_spec.js | 25 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/modules/fluctBidAdapter.js b/modules/fluctBidAdapter.js index 68206d4af38..a4a9568aa84 100644 --- a/modules/fluctBidAdapter.js +++ b/modules/fluctBidAdapter.js @@ -97,6 +97,10 @@ export const spec = { data.schain = request.schain; } + if (request.ortb2Imp?.instl === 1) { + data.instl = request.ortb2Imp?.instl; + } + const searchParams = new URLSearchParams({ dfpUnitCode: request.params.dfpUnitCode, tagId: request.params.tagId, diff --git a/test/spec/modules/fluctBidAdapter_spec.js b/test/spec/modules/fluctBidAdapter_spec.js index 3335e8b23cc..1051ce5a63b 100644 --- a/test/spec/modules/fluctBidAdapter_spec.js +++ b/test/spec/modules/fluctBidAdapter_spec.js @@ -432,6 +432,31 @@ describe('fluctAdapter', function () { expect(request.data.regs.gpp.string).to.eql('gpp-consent-string'); expect(request.data.regs.gpp.sid).to.eql([1, 2, 3]); }); + + it('sends no instl default', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.data.instl).to.eql(undefined); + }) + + it('sends ortb2Imp.instl as instl = 0', function () { + const request = spec.buildRequests(bidRequests.map((req) => ({ + ...req, + ortb2Imp: { + instl: 0, + }, + })), bidderRequest)[0]; + expect(request.data.instl).to.eql(undefined); + }); + + it('sends ortb2Imp.instl as instl', function () { + const request = spec.buildRequests(bidRequests.map((req) => ({ + ...req, + ortb2Imp: { + instl: 1, + }, + })), bidderRequest)[0]; + expect(request.data.instl).to.eql(1); + }); }); describe('should interpretResponse', function() { From f9588e20080b7d9ce767ab2830ed49db22194af8 Mon Sep 17 00:00:00 2001 From: yosei-ito Date: Thu, 10 Jul 2025 17:32:54 +0900 Subject: [PATCH 2/3] =?UTF-8?q?false=E3=81=AE=E5=A0=B4=E5=90=88=E3=82=82?= =?UTF-8?q?=E6=98=8E=E7=A4=BA=E7=9A=84=E3=81=AB=E9=80=81=E4=BF=A1=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/fluctBidAdapter.js | 6 ++---- test/spec/modules/fluctBidAdapter_spec.js | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/modules/fluctBidAdapter.js b/modules/fluctBidAdapter.js index a4a9568aa84..c782d3665ba 100644 --- a/modules/fluctBidAdapter.js +++ b/modules/fluctBidAdapter.js @@ -1,4 +1,4 @@ -import { _each, deepSetValue, isEmpty } from '../src/utils.js'; +import { _each, deepAccess, deepSetValue, isEmpty } from '../src/utils.js'; import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; @@ -97,9 +97,7 @@ export const spec = { data.schain = request.schain; } - if (request.ortb2Imp?.instl === 1) { - data.instl = request.ortb2Imp?.instl; - } + data.instl = deepAccess(request, 'ortb2Imp.instl') === 1 || request.params.instl === 1 ? 1 : 0; const searchParams = new URLSearchParams({ dfpUnitCode: request.params.dfpUnitCode, diff --git a/test/spec/modules/fluctBidAdapter_spec.js b/test/spec/modules/fluctBidAdapter_spec.js index 1051ce5a63b..70613a328e7 100644 --- a/test/spec/modules/fluctBidAdapter_spec.js +++ b/test/spec/modules/fluctBidAdapter_spec.js @@ -433,9 +433,9 @@ describe('fluctAdapter', function () { expect(request.data.regs.gpp.sid).to.eql([1, 2, 3]); }); - it('sends no instl default', function () { + it('sends no instl as instl = 0', function () { const request = spec.buildRequests(bidRequests, bidderRequest)[0]; - expect(request.data.instl).to.eql(undefined); + expect(request.data.instl).to.eql(0); }) it('sends ortb2Imp.instl as instl = 0', function () { @@ -445,7 +445,7 @@ describe('fluctAdapter', function () { instl: 0, }, })), bidderRequest)[0]; - expect(request.data.instl).to.eql(undefined); + expect(request.data.instl).to.eql(0); }); it('sends ortb2Imp.instl as instl', function () { From a34f8705a42330d74e3d51b51b16cde828dfa682 Mon Sep 17 00:00:00 2001 From: s103ng Date: Mon, 11 May 2026 10:54:38 +0900 Subject: [PATCH 3/3] fluct Bid Adapter: add ortb2Imp.ext.data and rwdd signals Pass impression-level custom targeting data (ortb2Imp.ext.data) and rewarded ad flag (ortb2Imp.rwdd) to the fluct server to improve signal coverage and enable rewarded inventory differentiation. Co-Authored-By: Claude Sonnet 4.6 --- modules/fluctBidAdapter.js | 35 ++++- test/spec/modules/fluctBidAdapter_spec.js | 160 ++++++++++++++++++++++ 2 files changed, 194 insertions(+), 1 deletion(-) diff --git a/modules/fluctBidAdapter.js b/modules/fluctBidAdapter.js index e1be0488885..02286d0c125 100644 --- a/modules/fluctBidAdapter.js +++ b/modules/fluctBidAdapter.js @@ -10,7 +10,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'fluct'; const END_POINT = 'https://hb.adingo.jp/prebid'; -const VERSION = '1.3'; +const VERSION = '1.5'; const NET_REVENUE = true; const TTL = 300; const DEFAULT_CURRENCY = 'JPY'; @@ -118,6 +118,20 @@ export const spec = { const data = {}; data.page = page; + + const ortb2Site = bidderRequest.ortb2?.site; + if (ortb2Site) { + data.site = {}; + if (ortb2Site.cat) data.site.cat = ortb2Site.cat; + if (ortb2Site.sectioncat) data.site.sectioncat = ortb2Site.sectioncat; + if (ortb2Site.pagecat) data.site.pagecat = ortb2Site.pagecat; + if (ortb2Site.keywords) data.site.keywords = ortb2Site.keywords; + if (ortb2Site.content) data.site.content = ortb2Site.content; + if (ortb2Site.domain) data.site.domain = ortb2Site.domain; + if (ortb2Site.ref) data.site.ref = ortb2Site.ref; + if (ortb2Site.ext?.data) data.site.ext = { data: ortb2Site.ext.data }; + } + data.adUnitCode = request.adUnitCode; data.bidId = request.bidId; data.user = { @@ -131,6 +145,9 @@ export const spec = { if (impExt) { data.transactionId = impExt.tid; data.gpid = impExt.gpid ?? impExt.data?.adserver?.adslot; + if (impExt.data) { + deepSetValue(data, 'imp.ext.data', impExt.data); + } } if (bidderRequest.gdprConsent) { deepSetValue(data, 'regs.gdpr', { @@ -187,6 +204,22 @@ export const spec = { data.instl = deepAccess(request, 'ortb2Imp.instl') === 1 || request.params.instl === 1 ? 1 : 0; + if (deepAccess(request, 'ortb2Imp.rwdd') === 1) data.rwdd = 1; + + const pos = deepAccess(request, 'mediaTypes.banner.pos') ?? deepAccess(request, 'ortb2Imp.ext.data.pos'); + if (pos != null) data.pos = pos; + + const ortb2Device = bidderRequest.ortb2?.device; + if (ortb2Device) { + data.device = {}; + if (ortb2Device.sua) data.device.sua = ortb2Device.sua; + if (ortb2Device.ua) data.device.ua = ortb2Device.ua; + if (ortb2Device.w) data.device.w = ortb2Device.w; + if (ortb2Device.h) data.device.h = ortb2Device.h; + if (ortb2Device.language) data.device.language = ortb2Device.language; + if (ortb2Device.devicetype) data.device.devicetype = ortb2Device.devicetype; + } + // Set top-level bidfloor to the highest floor across all sizes const highestFloorData = getHighestBidFloor(request); if (highestFloorData) { diff --git a/test/spec/modules/fluctBidAdapter_spec.js b/test/spec/modules/fluctBidAdapter_spec.js index b59b1e99e98..4dcdd10270a 100644 --- a/test/spec/modules/fluctBidAdapter_spec.js +++ b/test/spec/modules/fluctBidAdapter_spec.js @@ -439,6 +439,166 @@ describe('fluctAdapter', function () { expect(request.data.regs.gpp.sid).to.eql([1, 2, 3]); }); + it('includes no data.site by default', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.data.site).to.eql(undefined); + }); + + it('includes data.site if ortb2.site exists', function () { + const request = spec.buildRequests(bidRequests, Object.assign({}, bidderRequest, { + ortb2: { + site: { + cat: ['IAB1', 'IAB2'], + sectioncat: ['IAB1-1'], + pagecat: ['IAB1-2'], + keywords: 'sports,news', + content: { language: 'ja' }, + domain: 'example.com', + ref: 'https://referrer.example.com', + ext: { data: { customKey: 'customValue' } }, + }, + }, + }))[0]; + expect(request.data.site).to.eql({ + cat: ['IAB1', 'IAB2'], + sectioncat: ['IAB1-1'], + pagecat: ['IAB1-2'], + keywords: 'sports,news', + content: { language: 'ja' }, + domain: 'example.com', + ref: 'https://referrer.example.com', + ext: { data: { customKey: 'customValue' } }, + }); + }); + + it('includes only specified fields in data.site', function () { + const request = spec.buildRequests(bidRequests, Object.assign({}, bidderRequest, { + ortb2: { + site: { + cat: ['IAB1'], + domain: 'example.com', + }, + }, + }))[0]; + expect(request.data.site).to.eql({ + cat: ['IAB1'], + domain: 'example.com', + }); + }); + + it('includes no data.pos by default', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.data.pos).to.eql(undefined); + }); + + it('includes data.pos from mediaTypes.banner.pos', function () { + const request = spec.buildRequests(bidRequests.map((req) => ({ + ...req, + mediaTypes: { banner: { sizes: [[300, 250]], pos: 1 } }, + })), bidderRequest)[0]; + expect(request.data.pos).to.eql(1); + }); + + it('includes data.pos from ortb2Imp.ext.data.pos as fallback', function () { + const request = spec.buildRequests(bidRequests.map((req) => ({ + ...req, + ortb2Imp: { ext: { data: { pos: 3 } } }, + })), bidderRequest)[0]; + expect(request.data.pos).to.eql(3); + }); + + it('prefers mediaTypes.banner.pos over ortb2Imp.ext.data.pos', function () { + const request = spec.buildRequests(bidRequests.map((req) => ({ + ...req, + mediaTypes: { banner: { sizes: [[300, 250]], pos: 1 } }, + ortb2Imp: { ext: { data: { pos: 3 } } }, + })), bidderRequest)[0]; + expect(request.data.pos).to.eql(1); + }); + + it('includes no data.device by default', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.data.device).to.eql(undefined); + }); + + it('includes data.device if ortb2.device exists', function () { + const sua = { browsers: [{ brand: 'Chrome', version: ['120'] }] }; + const request = spec.buildRequests(bidRequests, Object.assign({}, bidderRequest, { + ortb2: { + device: { + sua, + ua: 'Mozilla/5.0', + w: 1920, + h: 1080, + language: 'ja', + devicetype: 2, + }, + }, + }))[0]; + expect(request.data.device).to.eql({ + sua, + ua: 'Mozilla/5.0', + w: 1920, + h: 1080, + language: 'ja', + devicetype: 2, + }); + }); + + it('includes only specified fields in data.device', function () { + const sua = { browsers: [{ brand: 'Chrome', version: ['120'] }] }; + const request = spec.buildRequests(bidRequests, Object.assign({}, bidderRequest, { + ortb2: { + device: { sua }, + }, + }))[0]; + expect(request.data.device).to.eql({ sua }); + }); + + it('includes no data.imp by default', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.data.imp).to.eql(undefined); + }); + + it('includes data.imp.ext.data from ortb2Imp.ext.data', function () { + const request = spec.buildRequests(bidRequests.map((req) => ({ + ...req, + ortb2Imp: { + ext: { + data: { + section: 'sports', + contentType: 'article', + }, + }, + }, + })), bidderRequest)[0]; + expect(request.data.imp.ext.data).to.eql({ + section: 'sports', + contentType: 'article', + }); + }); + + it('sends no rwdd by default', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.data.rwdd).to.eql(undefined); + }); + + it('sends ortb2Imp.rwdd as rwdd', function () { + const request = spec.buildRequests(bidRequests.map((req) => ({ + ...req, + ortb2Imp: { rwdd: 1 }, + })), bidderRequest)[0]; + expect(request.data.rwdd).to.eql(1); + }); + + it('sends no rwdd when ortb2Imp.rwdd is 0', function () { + const request = spec.buildRequests(bidRequests.map((req) => ({ + ...req, + ortb2Imp: { rwdd: 0 }, + })), bidderRequest)[0]; + expect(request.data.rwdd).to.eql(undefined); + }); + it('sends no instl as instl = 0', function () { const request = spec.buildRequests(bidRequests, bidderRequest)[0]; expect(request.data.instl).to.eql(0);