Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PAIR: Support for Generic TechLab Version #12146

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

therevoltingx
Copy link

@therevoltingx therevoltingx commented Aug 20, 2024

Clean Room Configuration

{
    userSync: {
      userIds: [{
        name: 'pairId',
        params: {
          mode: "open",
          liveramp: {
            storageKey: '_lr_pairId'
          },
          habu: {
            storageKey: '_habu_pairId'
          }
       },
    }]
  }
}

Bid Request

{
  "ext": {
    "eids": [
      "source": "iabtechlab.com",
      "uids": [
        {
          "id": "0x1234", // using clean room A
          "atype": 3
        },
        {
          "id": "0x456", // using clean room B
          "atype": 3
        }
      ]
    ]
  }
}

References

@therevoltingx therevoltingx marked this pull request as draft August 20, 2024 10:05
@patmmccann
Copy link
Collaborator

your general approach is breaking and unlikely to be merged. I would recommend chatting before continuing

return {'id': ids};
},
eids: {
'pairId': {
source: 'google.com',
source: 'iabtechlab.com',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In future IAB may support another Alt ID, so the source for PAIR should clearly mention what it is.
Can we mention it as "pair-iab-tl.com"?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't that what atype is for? like a single source can have multiple id types?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@pm-harshad-mane pm-harshad-mane Aug 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

atype identifies the user agent types a user identifier is from.
IABTechLab may have another ID with same atype in future hence we should have clear identification in source that tells its a PAIR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@therevoltingx mentioned we will have a unique atype to identify eid as a PAIR

* used to specify vendor id
* @type {number}
*/
gvlid: 755,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this configurable. Should be the DSP

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not included in the bid request at all. this is the owner of the module. we need prebid to confirm.
the prefered thing to do is to leave as-is.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use VENDORLESS_GVLID here

return {'id': ids};
},
eids: {
'pairId': {
source: 'google.com',
source: 'iabtechlab.com',
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this? I assumed it was the ID type which is now a techlab id type

return {'id': ids};
},
eids: {
'pairId': {
source: 'google.com',
source: 'iabtechlab.com',
atype: 571187
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

confirm that this value was assigned to pair protocol

@jdwieland8282
Copy link
Member

Identity PMC recommend that the IAB TL close this PR and open a new PR for their cleanroom implementation.

@pm-harshad-mane
Copy link
Contributor

Sorry for the late update :-)

The code in the original pairIdSystem and the new module (which will have a PR created) will have a lot of duplication.

We talked about needing to change some parameter values, like switching the source from “google.com” to “iabtechlab.com” and updating or removing the GVL.

To avoid duplicating code, we can add a new optional “mode” parameter to the module. This parameter will adjust these values based on its setting.

If the “mode” parameter isn’t specified, the module will work the same as before with the original values (backward compatible). However, if the “mode” is set to “open,” the module will slightly change its behavior, like altering the source parameter to "iabtechlab.com" and the GVL reference.

@therevoltingx
Copy link
Author

therevoltingx commented Sep 6, 2024

@pm-harshad-mane

I really like the idea. But for the eids method, how can I access the configuration? Gonna dig in but if you could point me to how I can access the config there that would be very helpful

@pm-harshad-mane
Copy link
Contributor

@therevoltingx the getId method has access t o config and it is returning the id in eids format.
Which eids method are you referring to?

@therevoltingx
Copy link
Author

therevoltingx commented Sep 10, 2024

@pm-harshad-mane I'm referring to line 103 in pairIdSystem.js. You can see the line here: https://github.com/prebid/Prebid.js/blob/master/modules/pairIdSystem.js#L103

The eids module attribute is called by modules/userId/eids.js: https://github.com/prebid/Prebid.js/blob/master/modules/userId/eids.js#L67

Which helps construct the final bid request object. That does not seem to have access to config.

@pm-harshad-mane
Copy link
Contributor

@dgirardi can you help us? we are trying to avoid code duplication

@jlquaccia
Copy link
Collaborator

hey @therevoltingx @pm-harshad-mane, was taking a look into the code around the prebid pair module a bit from my end. what about something like this?

pbjs.setConfig:

userSync: {
  userIds: [{
    name: "pairId",
    storage: {
      type: "html5",
      name: "pairId"
    },
    params: {
      mode: "open"
    }
  }],
}

and then the following modifications to the pairIdSystem.js file (which could be further modified as needed):

/**
 * This module adds PAIR Id to the User ID module
 * The {@link module:modules/userId} module is required
 * @module modules/pairIdSystem
 * @requires module:modules/userId
 */

import { submodule } from '../src/hook.js';
import {getStorageManager} from '../src/storageManager.js'
import { logInfo } from '../src/utils.js';
import {MODULE_TYPE_UID} from '../src/activities/modules.js';

/**
 * @typedef {import('../modules/userId/index.js').Submodule} Submodule
 */

const MODULE_NAME = 'pairId';
const PAIR_ID_KEY = 'pairId';
const DEFAULT_LIVERAMP_PAIR_ID_KEY = '_lr_pairId';

const MODE_CONFIG = {
  default: {
    source: 'google.com',
    gvlid: 755
  },
  open: {
    source: 'iabtechlab.com',
    gvlid: 999
  },
};

export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME});

function pairIdFromLocalStorage(key) {
  return storage.localStorageIsEnabled() ? storage.getDataFromLocalStorage(key) : null;
}

function pairIdFromCookie(key) {
  return storage.cookiesAreEnabled() ? storage.getCookie(key) : null;
}

/** @type {Submodule} */
export const pairIdSubmodule = {
  /**
   * used to link submodule with config
   * @type {string}
   */
  name: MODULE_NAME,
  /**
   * used to specify vendor id
   * @type {number}
   */
  gvlid: MODE_CONFIG.default.gvlid,
  /**
   * decode the stored id value for passing to bid requests
   * @function
   * @param { string | undefined } value
   * @returns {{pairId:string} | undefined }
   */
  decode(value) {
    return value && Array.isArray(value) ? {'pairId': value} : undefined
  },
  /**
   * Performs action to obtain ID and return a value in the callback's response argument.
   * @function getId
   * @param {Object} config - The configuration object.
   * @param {Object} config.params - The parameters from the configuration.
   * @returns {{id: string[] | undefined}} The obtained IDs or undefined if no IDs are found.
   */
  getId(config) {
    const mode = (config && config.params && config.params.mode) || 'default';
    const selectedModeConfig = MODE_CONFIG[mode] || MODE_CONFIG.default;
    const source = selectedModeConfig.source;

    this.gvlid = selectedModeConfig.gvlid;
    this.eids.pairId.source = source;

    const pairIdsString = pairIdFromLocalStorage(PAIR_ID_KEY) || pairIdFromCookie(PAIR_ID_KEY)
    let ids = []
    if (pairIdsString && typeof pairIdsString == 'string') {
      try {
        ids = ids.concat(JSON.parse(atob(pairIdsString)))
      } catch (error) {
        logInfo(error)
      }
    }

    const configParams = (config && config.params) || {};
    if (configParams && configParams.liveramp) {
      let LRStorageLocation = configParams.liveramp.storageKey || DEFAULT_LIVERAMP_PAIR_ID_KEY;
      const liverampValue = pairIdFromLocalStorage(LRStorageLocation) || pairIdFromCookie(LRStorageLocation);

      if (liverampValue) {
        try {
          const parsedValue = atob(liverampValue);
          if (parsedValue) {
            const obj = JSON.parse(parsedValue);

            if (obj && typeof obj === 'object' && obj.envelope) {
              ids = ids.concat(obj.envelope);
            } else {
              logInfo('Pairid: Parsed object is not valid or does not contain envelope');
            }
          } else {
            logInfo('Pairid: Decoded value is empty');
          }
        } catch (error) {
          logInfo('Pairid: Error parsing JSON: ', error);
        }
      } else {
        logInfo('Pairid: liverampValue for pairId from storage is empty or null');
      }
    }

    if (ids.length == 0) {
      logInfo('PairId not found.')
      return undefined;
    }

    return {'id': ids};
  },
  eids: {
    'pairId': {
      source: MODE_CONFIG.default.source,
      atype: 571187
    },
  }
};

submodule('userId', pairIdSubmodule);

@jlquaccia
Copy link
Collaborator

@dgirardi wondering what your thoughts on the suggestion above might be? seems to work fine locally, but curious if there are any other elements i am not considering..

@therevoltingx
Copy link
Author

@jlquaccia
My main concern here is that the caller must call getId() first in order for eids to return the correct values.

@bmilekic
Copy link

@dgirardi wondering what your thoughts on the suggestion above might be? seems to work fine locally, but curious if there are any other elements i am not considering..

For the open version, I think it would be better to use a correct atype (atype: 3) as shown in the Bid Request section at the top of here instead of the magic number 571187. This assumes that source: "iabtechlab.com" is used exclusively to signal a publisher open PAIR ID, which I believe is the intention.

@pm-harshad-mane
Copy link
Contributor

Looks like the progress to have a sub-module to support open version of PAIR has derailed since my suggestion for avoiding the code duplication...
@therevoltingx considering the complexities that we will need to handle, I have no issues if we create a separate module...

@therevoltingx
Copy link
Author

@pm-harshad-mane @bmilekic @jlquaccia

After some internal discussion we've gone aheand and decided not to make it backward compatible. We also chose to remove the gvlid as that didn't make much sense to have.

Please review and let me know your thoughts.

Comment on lines +72 to +77
for (let i = 0; i < cleanRooms.length; i++) {
const cleanRoom = cleanRooms[i];
const cleanRoomParams = configParams[cleanRoom];

const cleanRoomStorageLocation = cleanRoomParams.storageKey || DEFAULT_STORAGE_PAID_ID_KEYS[cleanRoom];
const cleanRoomValue = pairIdFromLocalStorage(cleanRoomStorageLocation) || pairIdFromCookie(cleanRoomStorageLocation);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (let i = 0; i < cleanRooms.length; i++) {
const cleanRoom = cleanRooms[i];
const cleanRoomParams = configParams[cleanRoom];
const cleanRoomStorageLocation = cleanRoomParams.storageKey || DEFAULT_STORAGE_PAID_ID_KEYS[cleanRoom];
const cleanRoomValue = pairIdFromLocalStorage(cleanRoomStorageLocation) || pairIdFromCookie(cleanRoomStorageLocation);
Object.entries(configParams).forEach( ([cleanRoom, cleanRoomParams]) => {
const cleanRoomStorageLocation = cleanRoomParams.storageKey || DEFAULT_STORAGE_PAID_ID_KEYS[cleanRoom];
const cleanRoomValue = pairIdFromLocalStorage(cleanRoomStorageLocation) || pairIdFromCookie(cleanRoomStorageLocation);

JS made this a little more convienent 😄
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries

Suggestion won't fully work as there's probably a closing brace missing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants