From 8d6170484a2ec1c0d7cd3e66fe29d60c6d170530 Mon Sep 17 00:00:00 2001 From: Harshita SIngh Date: Fri, 5 Jun 2026 13:27:07 +0530 Subject: [PATCH] Updated Missena Pacement --- modules/missenaBidAdapter.js | 106 +++++++++++++++++++- test/spec/modules/missenaBidAdapter_spec.js | 106 +++++++++++++++++++- 2 files changed, 207 insertions(+), 5 deletions(-) diff --git a/modules/missenaBidAdapter.js b/modules/missenaBidAdapter.js index 0d2054ca09f..fbe1d94d69c 100644 --- a/modules/missenaBidAdapter.js +++ b/modules/missenaBidAdapter.js @@ -29,8 +29,94 @@ const BIDDER_CODE = 'missena'; const ENDPOINT_URL = 'https://bid.missena.io/'; const EVENTS_DOMAIN = 'events.missena.io'; const EVENTS_DOMAIN_DEV = 'events.staging.missena.xyz'; +const DEFAULT_PLACEMENT = 'sticky'; +const STICKY_AD_UNIT_PREFIX = 'STICKY_ADP'; + +export const PLACEMENT_CONFIG = { + sticky: { + defaultFormats: ['sticky-banner'], + allowedFormats: ['sticky-banner'], + adUnitCodePrefix: STICKY_AD_UNIT_PREFIX, + }, + // header: { defaultFormats: ['header-banner'], allowedFormats: ['header-banner'], adUnitCodePrefix: 'HEADER_ADP' }, + // footer: { defaultFormats: ['footer-banner'], allowedFormats: ['footer-banner'], adUnitCodePrefix: 'FOOTER_ADP' }, + // prestitial: { defaultFormats: ['prestitial-banner'], allowedFormats: ['prestitial-banner'] }, + // postitial: { defaultFormats: ['postitial-banner'], allowedFormats: ['postitial-banner'] }, + // infeed: { defaultFormats: ['infeed-banner'], allowedFormats: ['infeed-banner'] }, + // 'infeed.s': { defaultFormats: ['infeed-s-banner'], allowedFormats: ['infeed-s-banner'] }, +}; export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); + +function getPlacement(params = {}) { + return params.placement || DEFAULT_PLACEMENT; +} + +function getPlacementConfig(placement) { + return PLACEMENT_CONFIG[placement]; +} + +function hasValidFormats(formats, placementConfig) { + if (formats == null) { + return true; + } + + if (!Array.isArray(formats)) { + return false; + } + + const allowedFormats = + placementConfig.allowedFormats ?? placementConfig.defaultFormats; + + return formats.some((format) => allowedFormats.includes(format)); +} + +export function isStickyAdUnitCode(adUnitCode) { + return typeof adUnitCode === 'string' && adUnitCode.startsWith(STICKY_AD_UNIT_PREFIX); +} + +function matchesPlacementAdUnit(placement, adUnitCode) { + const prefix = getPlacementConfig(placement)?.adUnitCodePrefix; + + if (!prefix) { + return true; + } + + return typeof adUnitCode === 'string' && adUnitCode.startsWith(prefix); +} + +function isSupportedPlacementBidRequest(bid) { + if (!isStickyAdUnitCode(bid?.adUnitCode)) { + return false; + } + + const placement = getPlacement(bid?.params); + const placementConfig = getPlacementConfig(placement); + + if (!placementConfig) { + return false; + } + + return ( + matchesPlacementAdUnit(placement, bid?.adUnitCode) && + hasValidFormats(bid?.params?.formats, placementConfig) + ); +} + +function getNormalizedBidParams(params = {}) { + const placement = getPlacement(params); + const placementConfig = getPlacementConfig(placement); + + if (!placementConfig) { + return params; + } + + return { + ...params, + placement, + formats: params.formats ?? placementConfig.defaultFormats, + }; +} window.msna_ik = window.msna_ik || generateUUID(); /* Get Floor price information */ @@ -59,7 +145,7 @@ function toPayload(bidRequest, bidderRequest) { }; const baseUrl = bidRequest.params.baseUrl || ENDPOINT_URL; - payload.params = bidRequest.params; + payload.params = getNormalizedBidParams(bidRequest.params); payload.userEids = bidRequest.userIdAsEids || []; payload.version = '$prebid.version$'; @@ -95,7 +181,11 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function (bid) { - return typeof bid == 'object' && !!bid.params.apiKey; + return ( + typeof bid === 'object' && + !!bid.params?.apiKey && + isSupportedPlacementBidRequest(bid) + ); }, /** @@ -118,9 +208,17 @@ export const spec = { return []; } - this.msnaApiKey = validBidRequests[0]?.params.apiKey; + const stickyBidRequests = validBidRequests.filter((bidRequest) => + isStickyAdUnitCode(bidRequest.adUnitCode), + ); + + if (!stickyBidRequests.length) { + return []; + } + + this.msnaApiKey = stickyBidRequests[0]?.params.apiKey; - return validBidRequests.map((bidRequest) => + return stickyBidRequests.map((bidRequest) => toPayload(bidRequest, bidderRequest), ); }, diff --git a/test/spec/modules/missenaBidAdapter_spec.js b/test/spec/modules/missenaBidAdapter_spec.js index e95903d4791..e9f22da6118 100644 --- a/test/spec/modules/missenaBidAdapter_spec.js +++ b/test/spec/modules/missenaBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec, storage } from 'modules/missenaBidAdapter.js'; +import { spec, storage, PLACEMENT_CONFIG, isStickyAdUnitCode } from 'modules/missenaBidAdapter.js'; import { BANNER } from '../../../src/mediaTypes.js'; import { config } from 'src/config.js'; import * as autoplay from 'libraries/autoplayDetection/autoplay.js'; @@ -10,6 +10,9 @@ const REFERRER2 = 'https://referer2'; const COOKIE_DEPRECATION_LABEL = 'test'; const CONSENT_STRING = 'AAAAAAAAA=='; const API_KEY = 'PA-XXXXXX'; +const STICKY_AD_UNIT_CODE = + 'STICKY_ADP_38170_970X90_76d1a660-e1b2-4281-9d26-3ad376e1e9e4'; +const NON_STICKY_AD_UNIT_CODE = 'MANUAL_ADP_38170_300X250_abc123'; describe('Missena Adapter', function () { $$PREBID_GLOBAL$$.bidderSettings = { @@ -26,6 +29,7 @@ describe('Missena Adapter', function () { const bid = { bidder: 'missena', bidId: bidId, + adUnitCode: STICKY_AD_UNIT_CODE, mediaTypes: { banner: { sizes: [[1, 1]] } }, ortb2: { device: { @@ -57,6 +61,7 @@ describe('Missena Adapter', function () { const bidWithoutFloor = { bidder: 'missena', bidId: bidId, + adUnitCode: STICKY_AD_UNIT_CODE, mediaTypes: { banner: { sizes: [1, 1] } }, params: { apiKey: API_KEY, @@ -111,6 +116,74 @@ describe('Missena Adapter', function () { spec.isBidRequestValid(Object.assign(bid, { params: { apiKey: '' } })), ).to.equal(false); }); + + it('should return true if placement is omitted and defaults to sticky', function () { + expect( + spec.isBidRequestValid({ + ...bid, + params: { + apiKey: API_KEY, + formats: ['sticky-banner'], + }, + }), + ).to.equal(true); + }); + + it('should return false if adUnitCode does not match sticky prefix', function () { + expect(PLACEMENT_CONFIG.sticky.adUnitCodePrefix).to.equal('STICKY_ADP'); + expect(isStickyAdUnitCode(STICKY_AD_UNIT_CODE)).to.equal(true); + expect(isStickyAdUnitCode(NON_STICKY_AD_UNIT_CODE)).to.equal(false); + expect(isStickyAdUnitCode('TOG_ADP_41722_970X250_test')).to.equal(false); + expect( + spec.isBidRequestValid({ + ...bid, + adUnitCode: NON_STICKY_AD_UNIT_CODE, + }), + ).to.equal(false); + expect( + spec.isBidRequestValid({ + ...bid, + adUnitCode: 'TOG_ADP_41722_970X250_test', + }), + ).to.equal(false); + }); + + it('should return false if adUnitCode is missing for sticky placement', function () { + expect( + spec.isBidRequestValid({ + ...bid, + adUnitCode: undefined, + }), + ).to.equal(false); + }); + + it('should return false for placements not enabled in PLACEMENT_CONFIG', function () { + expect(PLACEMENT_CONFIG.header).to.equal(undefined); + expect( + spec.isBidRequestValid({ + ...bid, + adUnitCode: 'HEADER_ADP_38170_970X90_test', + params: { + apiKey: API_KEY, + placement: 'header', + formats: ['header-banner'], + }, + }), + ).to.equal(false); + }); + + it('should return false if formats are not allowed for the placement', function () { + expect( + spec.isBidRequestValid({ + ...bid, + params: { + apiKey: API_KEY, + placement: 'sticky', + formats: ['banner'], + }, + }), + ).to.equal(false); + }); }); describe('buildRequests', function () { @@ -145,6 +218,20 @@ describe('Missena Adapter', function () { expect(requests.length).to.equal(2); }); + it('should not build requests for non-sticky ad units', function () { + const nonStickyRequests = spec.buildRequests( + [ + { + ...bid, + adUnitCode: 'TOG_ADP_41722_970X250_test', + }, + ], + bidderRequest, + ); + + expect(nonStickyRequests.length).to.equal(0); + }); + it('should have a post method', function () { expect(request.method).to.equal('POST'); }); @@ -161,6 +248,23 @@ describe('Missena Adapter', function () { expect(payload.params.formats).to.eql(['sticky-banner']); }); + it('should default placement and formats from PLACEMENT_CONFIG', function () { + const requestsWithoutPlacement = spec.buildRequests( + [ + { + ...bidWithoutFloor, + adUnitCode: STICKY_AD_UNIT_CODE, + params: { apiKey: API_KEY }, + }, + ], + bidderRequest, + ); + const payloadWithoutPlacement = JSON.parse(requestsWithoutPlacement[0].data); + + expect(payloadWithoutPlacement.params.placement).to.equal('sticky'); + expect(payloadWithoutPlacement.params.formats).to.eql(['sticky-banner']); + }); + it('should send viewport', function () { expect(payload.viewport.width).to.equal(viewport.width); expect(payload.viewport.height).to.equal(viewport.height);