• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

prebid / Prebid.js / 16809528788

07 Aug 2025 03:57PM UTC coverage: 96.254%. Remained the same
16809528788

push

github

web-flow
Risemediatech Bid Adapter : New Bidder Adapter (#13600)

* RM-845 : Initial implementation for risemediatech bid adapter

* RM-845 : Added bidder parameter documentation for risemediatech Bid Adapter

* RM-845 : minor modifications

* RM-845 : Handled es lint errors

* RM-847 : Unit Test for Risemediatech Bid Adapter

* Updated unit tests

* Modified the bid adapter code and unit tests

* Modified prebid js code to remove validations and also added bidfloor to the request.

* added the vastxml field in the response for the video media type

* Fixed incorrect media type issue

* Added test mode impressions support

* Added test mode for video ad units

* Added bidfloor for example video ad unit

* Updated default TTL

* Minro fixes

* Update docs

* Minor changes

* Minor changes

* Code cleanup

* Changes as per review

* Semantic changes

* Added support for Http Status 204 No Bids Scenarios

* Updated failing unit tests.

* Modified the check for no bids

* Reverted the status check

* linter modifications

* Updated the documentation for the adapter and formatted adapter

* Modified the documentation as per discussion

* Resolved linter errors from upstream repo PR

39576 of 48646 branches covered (81.36%)

237 of 244 new or added lines in 2 files covered. (97.13%)

148 existing lines in 17 files now uncovered.

195874 of 203497 relevant lines covered (96.25%)

123.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.33
/modules/debugging/bidInterceptor.js
1
import makeResponseResolvers from './responses.js';
2

3
/**
4
 * @typedef {Number|String|boolean|null|undefined} Scalar
5
 */
6

7
export function makebidInterceptor({utils, BANNER, NATIVE, VIDEO, Renderer}) {
51✔
8
  const {deepAccess, deepClone, delayExecution, hasNonSerializableProperty, mergeDeep} = utils;
51✔
9
  const responseResolvers = makeResponseResolvers({Renderer, BANNER, NATIVE, VIDEO});
51✔
10
  function BidInterceptor(opts = {}) {
51!
11
    ({setTimeout: this.setTimeout = window.setTimeout.bind(window)} = opts);
51✔
12
    this.logger = opts.logger;
51✔
13
    this.rules = [];
51✔
14
  }
15

16
  Object.assign(BidInterceptor.prototype, {
51✔
17
    DEFAULT_RULE_OPTIONS: {
18
      delay: 0
19
    },
20
    serializeConfig(ruleDefs) {
21
      const isSerializable = (ruleDef, i) => {
7✔
22
        const serializable = !hasNonSerializableProperty(ruleDef);
21✔
23
        if (!serializable && !deepAccess(ruleDef, 'options.suppressWarnings')) {
21✔
24
          this.logger.logWarn(`Bid interceptor rule definition #${i + 1} contains non-serializable properties and will be lost after a refresh. Rule definition: `, ruleDef);
7✔
25
        }
26
        return serializable;
21✔
27
      }
28
      return ruleDefs.filter(isSerializable);
7✔
29
    },
30
    updateConfig(config) {
31
      this.rules = (config.intercept || []).map((ruleDef, i) => this.rule(ruleDef, i + 1))
51✔
32
    },
33
    /**
34
     * @typedef {Object} RuleOptions
35
     * @property {Number} [delay=0] delay between bid interception and mocking of response (to simulate network delay)
36
     * @property {boolean} [suppressWarnings=false] if enabled, do not warn about unserializable rules
37
     *
38
     * @typedef {Object} Rule
39
     * @property {Number} no rule number (used only as an identifier for logging)
40
     * @property {function({}, {}): boolean} match a predicate function that tests a bid against this rule
41
     * @property {ReplacerFn} replacer generator function for mock bid responses
42
     * @property {RuleOptions} options
43
     */
44

45
    /**
46
     * @param {{}} ruleDef
47
     * @param {Number} ruleNo
48
     * @returns {Rule}
49
     */
50
    rule(ruleDef, ruleNo) {
51
      return {
49✔
52
        no: ruleNo,
53
        match: this.matcher(ruleDef.when, ruleNo),
54
        replace: this.replacer(ruleDef.then, ruleNo),
55
        options: Object.assign({}, this.DEFAULT_RULE_OPTIONS, ruleDef.options),
56
        paapi: this.paapiReplacer(ruleDef.paapi || [], ruleNo)
87✔
57
      }
58
    },
59
    /**
60
     * @typedef {Function} MatchPredicate
61
     * @param {*} candidate a bid to match, or a portion of it if used inside an ObjectMather.
62
     * e.g. matcher((bid, bidRequest) => ....) or matcher({property: (property, bidRequest) => ...})
63
     * @param {*} bidRequest the request `candidate` belongs to
64
     * @returns {boolean}
65
     *
66
     */
67

68
    /**
69
     * @param {*} matchDef matcher definition
70
     * @param {Number} ruleNo
71
     * @returns {MatchPredicate} a predicate function that matches a bid against the given `matchDef`
72
     */
73
    matcher(matchDef, ruleNo) {
74
      if (typeof matchDef === 'function') {
49✔
75
        return matchDef;
15✔
76
      }
77
      if (typeof matchDef !== 'object') {
34!
78
        this.logger.logError(`Invalid 'when' definition for debug bid interceptor (in rule #${ruleNo})`);
×
UNCOV
79
        return () => false;
×
80
      }
81
      function matches(candidate, {ref = matchDef, args = []}) {
46!
82
        return Object.entries(ref).map(([key, val]) => {
46✔
83
          const cVal = candidate[key];
25✔
84
          if (val instanceof RegExp) {
25✔
85
            return val.exec(cVal) != null;
4✔
86
          }
87
          if (typeof val === 'function') {
21✔
88
            return !!val(cVal, ...args);
3✔
89
          }
90
          if (typeof val === 'object') {
18✔
91
            return matches(cVal, {ref: val, args});
7✔
92
          }
93
          return cVal === val;
11✔
94
        }).every((i) => i);
24✔
95
      }
96
      return (candidate, ...args) => matches(candidate, {args});
78✔
97
    },
98
    /**
99
     * @typedef {Function} ReplacerFn
100
     * @param {*} bid a bid that was intercepted
101
     * @param {*} bidRequest the request `bid` belongs to
102
     * @returns {*} the response to mock for `bid`, or a portion of it if used inside an ObjectReplacer.
103
     * e.g. replacer((bid, bidRequest) => mockResponse) or replacer({property: (bid, bidRequest) => mockProperty})
104
     *
105
     */
106

107
    /**
108
     * @param {*} replDef replacer definition
109
     * @param ruleNo
110
     * @return {ReplacerFn}
111
     */
112
    replacer(replDef, ruleNo) {
49✔
113
      if (replDef === null) {
49✔
114
        return () => null
1✔
115
      }
116
      replDef = replDef || {};
48✔
117
      let replFn;
118
      if (typeof replDef === 'function') {
48✔
119
        replFn = ({args}) => replDef(...args);
14✔
120
      } else if (typeof replDef !== 'object') {
34!
121
        this.logger.logError(`Invalid 'then' definition for debug bid interceptor (in rule #${ruleNo})`);
×
UNCOV
122
        replFn = () => ({});
×
123
      } else {
124
        replFn = ({args, ref = replDef}) => {
34✔
125
          const result = Array.isArray(ref) ? [] : {};
16✔
126
          Object.entries(ref).forEach(([key, val]) => {
16✔
127
            if (typeof val === 'function') {
15✔
128
              result[key] = val(...args);
4✔
129
            } else if (val != null && typeof val === 'object') {
11✔
130
              result[key] = replFn({args, ref: val})
8✔
131
            } else {
132
              result[key] = val;
3✔
133
            }
134
          });
135
          return result;
16✔
136
        }
137
      }
138
      return (bid, ...args) => {
48✔
139
        const response = this.responseDefaults(bid);
18✔
140
        mergeDeep(response, replFn({args: [bid, ...args]}));
18✔
141
        const resolver = responseResolvers[response.mediaType];
18✔
142
        resolver && resolver(bid, response);
18✔
143
        response.isDebug = true;
18✔
144
        return response;
18✔
145
      }
146
    },
147

148
    paapiReplacer(paapiDef, ruleNo) {
149
      function wrap(configs = []) {
20✔
150
        return configs.map(config => {
20✔
151
          return Object.keys(config).some(k => !['config', 'igb'].includes(k))
14✔
152
            ? {config}
153
            : config
154
        });
155
      }
156
      if (Array.isArray(paapiDef)) {
49✔
157
        return () => wrap(paapiDef);
44✔
158
      } else if (typeof paapiDef === 'function') {
5!
159
        return (...args) => wrap(paapiDef(...args))
5✔
160
      } else {
UNCOV
161
        this.logger.logError(`Invalid 'paapi' definition for debug bid interceptor (in rule #${ruleNo})`);
×
162
      }
163
    },
164

165
    responseDefaults(bid) {
166
      const response = {
18✔
167
        requestId: bid.bidId,
168
        cpm: 3.5764,
169
        currency: 'EUR',
170
        ttl: 360,
171
        creativeId: 'mock-creative-id',
172
        netRevenue: false,
173
        meta: {}
174
      };
175

176
      if (!bid.mediaType) {
18✔
177
        response.mediaType = Object.keys(bid.mediaTypes ?? {})[0] ?? BANNER;
18✔
178
      }
179
      let size;
180
      if (response.mediaType === BANNER) {
18!
181
        size = bid.mediaTypes?.banner?.sizes?.[0] ?? [300, 250];
18✔
182
      } else if (response.mediaType === VIDEO) {
×
UNCOV
183
        size = bid.mediaTypes?.video?.playerSize?.[0] ?? [600, 500];
×
184
      }
185
      if (Array.isArray(size)) {
18✔
186
        ([response.width, response.height] = size);
18✔
187
      }
188
      return response;
18✔
189
    },
190
    /**
191
     * Match a candidate bid against all registered rules.
192
     *
193
     * @param {{}} candidate
194
     * @param args
195
     * @returns {Rule|undefined} the first matching rule, or undefined if no match was found.
196
     */
197
    match(candidate, ...args) {
116✔
198
      return this.rules.find((rule) => rule.match(candidate, ...args));
65✔
199
    },
200
    /**
201
     * Match a set of bids against all registered rules.
202
     *
203
     * @param bids
204
     * @param bidRequest
205
     * @returns {[{bid: *, rule: Rule}[], *[]]} a 2-tuple for matching bids (decorated with the matching rule) and
206
     * non-matching bids.
207
     */
208
    matchAll(bids, bidRequest) {
209
      const [matches, remainder] = [[], []];
11✔
210
      bids.forEach((bid) => {
11✔
211
        const rule = this.match(bid, bidRequest);
22✔
212
        if (rule != null) {
22✔
213
          matches.push({rule: rule, bid: bid});
10✔
214
        } else {
215
          remainder.push(bid);
12✔
216
        }
217
      })
218
      return [matches, remainder];
11✔
219
    },
220
    /**
221
     * Run a set of bids against all registered rules, filter out those that match,
222
     * and generate mock responses for them.
223
     *
224
     * {{}[]} bids?
225
     * {*} bidRequest
226
     * {function(*)} addBid called once for each mock response
227
     * addPaapiConfig called once for each mock PAAPI config
228
     * {function()} done called once after all mock responses have been run through `addBid`
229
     * returns {{bids: {}[], bidRequest: {}} remaining bids that did not match any rule (this applies also to
230
     * bidRequest.bids)
231
     */
232
    intercept({bids, bidRequest, addBid, addPaapiConfig, done}) {
11✔
233
      if (bids == null) {
11✔
234
        bids = bidRequest.bids;
8✔
235
      }
236
      const [matches, remainder] = this.matchAll(bids, bidRequest);
11✔
237
      if (matches.length > 0) {
11✔
238
        const callDone = delayExecution(done, matches.length);
6✔
239
        matches.forEach((match) => {
6✔
240
          const mockResponse = match.rule.replace(match.bid, bidRequest);
10✔
241
          const mockPaapi = match.rule.paapi(match.bid, bidRequest);
10✔
242
          const delay = match.rule.options.delay;
10✔
243
          this.logger.logMessage(`Intercepted bid request (matching rule #${match.rule.no}), mocking response in ${delay}ms. Request, response, PAAPI configs:`, match.bid, mockResponse, mockPaapi)
10✔
244
          this.setTimeout(() => {
10✔
245
            mockResponse && addBid(mockResponse, match.bid);
10✔
246
            mockPaapi.forEach(cfg => addPaapiConfig(cfg, match.bid, bidRequest));
10✔
247
            callDone();
10✔
248
          }, delay)
249
        });
250
        bidRequest = deepClone(bidRequest);
6✔
251
        bids = bidRequest.bids = remainder;
6✔
252
      } else {
253
        this.setTimeout(done, 0);
5✔
254
      }
255
      return {bids, bidRequest};
11✔
256
    }
257
  });
258
  return BidInterceptor;
51✔
259
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc