diff --git a/modules/my6senseBidAdapter.js b/modules/my6senseBidAdapter.js
new file mode 100644
index 00000000000..5796d1a0383
--- /dev/null
+++ b/modules/my6senseBidAdapter.js
@@ -0,0 +1,197 @@
+import { BANNER, NATIVE } from 'src/mediaTypes';
+
+const {registerBidder} = require('../src/adapters/bidderFactory');
+const BIDDER_CODE = 'my6sense';
+const END_POINT = '//papi.mynativeplatform.com/pub2/web/v1.15.0/hbwidget.json';
+const END_POINT_METHOD = 'POST';
+
+// called first
+function isBidRequestValid(bid) {
+ return !(bid.bidder !== BIDDER_CODE || !bid.params || !bid.params.key);
+}
+
+function getUrl(url) {
+ if (!url) {
+ url = window.location.href;// "clean" url of current web page
+ }
+ var canonicalLink = null;
+ // first look for meta data with property "og:url"
+ var metaElements = document.getElementsByTagName('meta');
+ for (var i = 0; i < metaElements.length && !canonicalLink; i++) {
+ if (metaElements[i].getAttribute('property') == 'og:url') {
+ canonicalLink = metaElements[i].content;
+ }
+ }
+ if (!canonicalLink) {
+ var canonicalLinkContainer = document.querySelector("link[rel='canonical']");// html element containing the canonical link
+ if (canonicalLinkContainer) {
+ // get clean url from href of
+ canonicalLink = canonicalLinkContainer.href;
+ }
+ }
+ url = canonicalLink || url;
+ return encodeURIComponent(url).toString();
+}
+
+/**
+ * this function is used to fix param value before sending them to server, if user did not set it,
+ * default value for parameter will be returned
+ * example1: paidClicks: '[PAID_TRACKING_PIXEL]', will return {value: '', fromUser: false}
+ * example2: pageURL: 'www.my6sense.com', will return {value: 'www.my6sense.com', fromUser: true}
+ * @param key
+ * @param value
+ * @returns {{value: *, fromUser: boolean}}
+ */
+function fixRequestParamForServer(key, value) {
+ function isEmptyValue(key, value) {
+ return value === parametersMap[key].emptyValue;
+ }
+
+ const parametersMap = {
+ 'pageUrl': {
+ emptyValue: '[PAGE_URL]',
+ defaultValue: getUrl()
+ },
+ 'displayWithinIframe': {
+ emptyValue: '',
+ defaultValue: ''
+ },
+ 'dataParams': {
+ emptyValue: '[KEY_VALUES]',
+ defaultValue: ''
+ },
+ 'paidClicks': {
+ emptyValue: '[PAID_TRACKING_PIXEL]',
+ defaultValue: ''
+ },
+ 'organicClicks': {
+ emptyValue: '[ORGANIC_TRACKING_PIXEL]',
+ defaultValue: ''
+ },
+ 'dataView': {
+ emptyValue: '[VIEW_TRACKING_PIXEL]',
+ defaultValue: ''
+ },
+ // ZONE is not part of this object, handled on server side
+ };
+
+ // if param is not in list we do not change it (return it as is)
+ if (!parametersMap.hasOwnProperty(key)) {
+ return {
+ value: value,
+ fromUser: true
+ };
+ }
+
+ // if no value given by user set it to default
+ if (!value || isEmptyValue(key, value)) {
+ return {
+ value: parametersMap[key].defaultValue,
+ fromUser: false
+ };
+ }
+
+ return {
+ value: value,
+ fromUser: true
+ };
+}
+
+// called second
+
+function buildGdprServerProperty(bidderRequest) {
+ var gdprObj = {
+ gdpr_consent: null,
+ gdpr: null
+ };
+
+ if (bidderRequest && 'gdprConsent' in bidderRequest) {
+ gdprObj.gdpr_consent = bidderRequest.gdprConsent.consentString || null;
+
+ gdprObj.gdpr = gdprObj.gdpr === null && bidderRequest.gdprConsent.gdprApplies == true ? true : gdprObj.gdpr;
+ gdprObj.gdpr = gdprObj.gdpr === null && bidderRequest.gdprConsent.gdprApplies == false ? false : gdprObj.gdpr;
+ gdprObj.gdpr = gdprObj.gdpr === null && bidderRequest.gdprConsent.gdprApplies == 1 ? true : gdprObj.gdpr;
+ gdprObj.gdpr = gdprObj.gdpr === null && bidderRequest.gdprConsent.gdprApplies == 0 ? false : gdprObj.gdpr;
+ }
+
+ return gdprObj;
+}
+
+function buildRequests(validBidRequests, bidderRequest) {
+ let requests = [];
+
+ if (validBidRequests && validBidRequests.length) {
+ validBidRequests.forEach(bidRequest => {
+ bidRequest.widget_num = 1; // mandatory property for server side
+ let isDataUrlSetByUser = false;
+ let debug = false;
+
+ if (bidRequest.params) {
+ for (let key in bidRequest.params) {
+ // loop over params and remove empty/untouched values
+ if (bidRequest.params.hasOwnProperty(key)) {
+ // if debug we update url string to get core debug version
+ if (key === 'debug' && bidRequest.params[key] === true) {
+ debug = true;
+ delete bidRequest.params[key];
+ continue;
+ }
+
+ let fixedObj = fixRequestParamForServer(key, bidRequest.params[key]);
+ bidRequest.params[key] = fixedObj.value;
+
+ // if pageUrl is set by user we should update variable for query string param
+ if (key === 'pageUrl' && fixedObj.fromUser === true) {
+ isDataUrlSetByUser = true;
+ }
+
+ // remove empty params from request
+ if (!bidRequest.params[key]) {
+ delete bidRequest.params[key];
+ }
+ }
+ }
+ }
+
+ let url = `${END_POINT}?widget_key=${bidRequest.params.key}&is_data_url_set=${isDataUrlSetByUser}`; // mandatory query string for server side
+ if (debug) {
+ url = `${END_POINT}?env=debug&widget_key=${bidRequest.params.key}&is_data_url_set=${isDataUrlSetByUser}`; // this url is for debugging
+ }
+
+ bidRequest.gdpr = buildGdprServerProperty(bidderRequest);
+
+ requests.push({
+ url: url,
+ method: END_POINT_METHOD,
+ data: JSON.stringify(bidRequest)
+ });
+ });
+ }
+
+ return requests;
+}
+
+// called third
+
+function interpretResponse(serverResponse) {
+ const bidResponses = [];
+ // currently server returns a single response which is the body property
+ if (serverResponse.body) {
+ serverResponse.body.bidderCode = BIDDER_CODE;
+ bidResponses.push(serverResponse.body);
+ }
+
+ return bidResponses;
+}
+
+const spec = {
+ code: BIDDER_CODE,
+ supportedMediaTypes: [BANNER, NATIVE],
+ isBidRequestValid,
+ buildRequests,
+ interpretResponse
+};
+
+registerBidder(spec);
+
+module.exports = spec;
diff --git a/modules/my6senseBidAdapter.md b/modules/my6senseBidAdapter.md
new file mode 100644
index 00000000000..7f422e6fabf
--- /dev/null
+++ b/modules/my6senseBidAdapter.md
@@ -0,0 +1,36 @@
+# HeaderBiddingAdapter
+
+# Overview
+
+```
+Module Name: my6sense Bidder Adapter
+Module Type: Bidder Adapter
+```
+
+# Description
+
+Module that connects to my6sense demand sources.
+Banner formats are supported.
+
+# Test Parameters
+```
+ var adUnits = [
+
+ // {
+ //
+ // sizes: [[1000, 600]],
+ // bids: [{
+ // bidder: 'my6sense',
+ // params: {
+ // key: 'OAJJBW2LRYi2CxfhzqogkA',
+ // pageUrl: '[PAGE_URL]',
+ // zone: '[ZONE]',
+ // dataView: ''
+ // organicClicks:'',
+ // paidClicks:'',
+ // }
+ // }]
+ // }
+ ];
+
+```
diff --git a/test/spec/modules/my6senseBidAdapter_spec.js b/test/spec/modules/my6senseBidAdapter_spec.js
new file mode 100644
index 00000000000..ec8389acbb3
--- /dev/null
+++ b/test/spec/modules/my6senseBidAdapter_spec.js
@@ -0,0 +1,151 @@
+import { expect } from 'chai';
+import spec from 'modules/my6senseBidAdapter';
+
+describe('My6sense Bid adapter test', () => {
+ let bidRequests, serverResponses;
+ beforeEach(() => {
+ bidRequests = [
+ {
+ // valid 1
+ bidder: 'my6sense',
+ params: {
+ key: 'DTAeOJN67pCjY36dbhrM3G',
+ dataVersion: 3,
+ pageUrl: 'liran.com',
+ zone: '[ZONE]',
+ dataParams: '',
+ dataView: '',
+ organicClicks: '',
+ paidClicks: ''
+ }
+ },
+ {
+ // invalid 2- no params
+ bidder: 'my6sense'
+ },
+ {
+ // invalid 3 - no key in params
+ bidder: 'my6sense',
+ params: {
+ dataVersion: 3,
+ pageUrl: 'liran.com',
+ zone: '[ZONE]',
+ dataParams: '',
+ dataView: '',
+ organicClicks: '',
+ paidClicks: ''
+ }
+ },
+ {
+ // invalid 3 - wrong bidder name
+ bidder: 'test',
+ params: {
+ key: 'ZxA0bNhlO9tf5EZ1Q9ZYdS',
+ dataVersion: 3,
+ pageUrl: 'liran.com',
+ zone: '[ZONE]',
+ dataParams: '',
+ dataView: '',
+ organicClicks: '',
+ paidClicks: ''
+ }
+ }
+ ];
+ serverResponses = [
+ {
+ headers: {},
+ body: {
+ cpm: 1.5,
+ width: 300,
+ height: 250,
+ placement_id: 1,
+ adm: ''
+ }
+ },
+ {
+ headers: {},
+ body: {
+ cpm: 0,
+ width: 0,
+ height: 0,
+ placement_id: 1,
+ adm: ''
+ }
+ },
+ {
+ headers: {},
+ body: {
+ cpm: 0,
+ width: 0,
+ height: 0,
+ placement_id: 0,
+ adm: ''
+ }
+ },
+ {
+ headers: {},
+ body: {
+ cpm: 5,
+ creativeId: '5b29f5d1e4b086e3ee8de36b',
+ currency: 'USD',
+ height: 250,
+ netRevenue: false,
+ requestId: '2954a0957643bb',
+ ttl: 360,
+ width: 300,
+ adm: ''
+ }
+ }
+ ]
+ });
+
+ describe('test if requestIsValid function', () => {
+ it('with valid data 1', () => {
+ expect(spec.isBidRequestValid(bidRequests[0])).to.equal(true);
+ });
+ it('with invalid data 2', () => {
+ expect(spec.isBidRequestValid(bidRequests[1])).to.equal(false);
+ });
+ it('with invalid data 3', () => {
+ expect(spec.isBidRequestValid(bidRequests[2])).to.equal(false);
+ });
+ it('with invalid data 3', () => {
+ expect(spec.isBidRequestValid(bidRequests[3])).to.equal(false);
+ });
+ });
+
+ describe('test if buildRequests function', () => {
+ it('normal', () => {
+ var requests = spec.buildRequests([bidRequests[0]]);
+ expect(requests).to.be.lengthOf(1);
+ });
+ });
+ describe('test bid responses', () => {
+ it('success 1', () => {
+ var bids = spec.interpretResponse(serverResponses[0], {'bidRequest': bidRequests[0]});
+ expect(bids).to.be.lengthOf(1);
+ expect(bids[0].cpm).to.equal(1.5);
+ expect(bids[0].width).to.equal(300);
+ expect(bids[0].height).to.equal(250);
+ expect(bids[0].adm).to.have.length.above(1);
+ });
+ it('success 2', () => {
+ var bids = spec.interpretResponse(serverResponses[3]);
+ expect(bids).to.be.lengthOf(1);
+ expect(bids[0].cpm).to.equal(5);
+ expect(bids[0].width).to.equal(300);
+ expect(bids[0].height).to.equal(250);
+ expect(bids[0].netRevenue).to.equal(false);
+ expect(bids[0].ttl).to.equal(360);
+ expect(bids[0].currency).to.equal('USD');
+ });
+ it('fail 1 (cpm=0)', () => {
+ var bids = spec.interpretResponse(serverResponses[1]);
+ expect(bids).to.be.lengthOf(1);
+ });
+ it('fail 2 (no response)', () => {
+ var bids = spec.interpretResponse([]);
+ expect(bids).to.be.lengthOf(0);
+ });
+ });
+});