npm package: @schibsted/niche-advertory-adapter
Description: Package bridging inventory's advertory package with niche next.js apps
Latest version: 3.1.1

# `niche-advertory-adapter`

Library that bridges the gap between `@sch-inventory/advertory` and Niche projects working on Next.js. `@sch-inventory/advertory` is published only on Artifactory.

[Link to Advertory](https://schibsted.ghe.com/inventory/advertory)

By design this library is not directly using `@sch-inventory/advertory` so that for most cases you will have do to changes in at most 2 projects from:

- `@schibsted/niche-advertory-adapter`
- `@sch-inventory/advertory`
- web frontent you are working on

API of this library is largely compatible with `@schibsted/niche-ads` library to simplify the switch.

## Usage

### Creating instance

```js
import { initAdvertoryPackage } from '@sch-inventory/advertory/core/minmote';
import { Device } from '@schibsted/niche-advertory-adapter';
import type { invCodeBuilderSignature } from '@schibsted/niche-advertory-adapter';
import { AdvertoryAdapter } from '@schibsted/niche-advertory-adapter';
import { adsConfig } from '@constants/ads';

const mapDevice = (device: Device) => {
    switch (device) {
        case Device.Desktop:
            return 'wde';
        case Device.Mobile:
            return 'wph';
        case Device.Tablet:
            return 'wtb';
        default:
            throw new Error('Unknown device');
    }
};

const nicheInvCodeBuilder: invCodeBuilderSignature = (placement, device, pageType) =>
    `no-minmote-${mapDevice(device)}-${pageType}_${
        Array.isArray(placement.adFormat) ? placement.adFormat[0] : placement.adFormat
    }`;

export const ads = new AdvertoryAdapter({
    advertoryConfig: {
        country: 'no',
        defaultThreshold: 200,
        fullscreenScroll: {
            ...
        },
        prebid: {
            ...
        },
        publisher: 'minmote',
        tcf: {
            enabled: false,
        },
    },
    glimr: {
        clientId: 'H3IYO2A4LD43YPFZIJLN',
    },
    initAdvertoryPackage,
    nicheAdsConfig: adsConfig,
    nicheInvCodeBuilder,
});
```

#### Configuration

- `advertoryConfig` - configuration passed to advertory's `initAdvertoryPackage` function, check [advertory documentation](https://schibsted.ghe.com/inventory/advertory) for more details
- `finnBlink` - optional configuration for finn blink ad, should be object with `channel` property
- `glimr` - configuration for glimr, only required parameter is `clientId` (it's disabled by default, at this point of initialization we don't know if we can use glimr due to user consents)
- `initAdvertoryPackage` - function that initializes advertory package, it's different for each project (different import from advertory package)
- `nicheAdsConfig` -
    - `keywords` - keyboards to be set on the whole ad call
    - `pageOptions` - passed to advertory, look [there](https://schibsted.ghe.com/inventory/advertory#pageoptions) for documentation
    - `placements` - placement configuration, not advertory format, our is much shorter
- `nicheInvCodeBuilder` - function building invCode based on placement config, device and pageType, example implementation is provided above
- `pulseClientId` - required

`nicheAdsConfig` example:

```js
{
    keywords: {
        common: {
            'no-sno-publishergroup': 'schibsted',
        },
        desktop: {
            article: {
                'aa-sch-page_type': 'article',
            },
            common: {
                'aa-sch-supply_type': 'web_desktop',
            },
            front: {
                'aa-sch-page_type': 'front',
            },
            other: {
                'aa-sch-page_type': 'other',
            },
        },
        mobile: {
            article: {
                'aa-sch-page_type': 'article',
            },
            common: {
                'aa-sch-supply_type': 'web_phone',
            },
            front: {
                'aa-sch-page_type': 'front',
            },
            other: {
                'aa-sch-page_type': 'other',
            },
        },
        tablet: {
            article: {
                'aa-sch-page_type': 'article',
            },
            common: {
                'aa-sch-supply_type': 'web_tablet',
            },
            front: {
                'aa-sch-page_type': 'front',
            },
            other: {
                'aa-sch-page_type': 'other',
            },
        },
    }
    pageOptions: {
        member: 9700, // 9700 - regular member, 9826 - test seat
        disablePsa: true,
        enableSafeFrame: true,
    },
    placements: {
        common: {
            allowedFormats: ['banner', 'video', 'native'],
            native: {
                desc2: { required: false },
                icon: { required: false, sizes: [{}] },
                image: { required: false, sizes: [{}] },
                sponsoredBy: { required: false },
                title: { required: false },
                video: { min_duration: 0, required: false },
            },
            noBidIfUnsold: true,
        },
        desktop: {
            article: [
                {
                    adFormat: 'netboard_1',
                    sizes: [...netboardSizes, nativeSize],
                    targetId: 'text_ad_1',
                },
            ],
            front: [
                {
                    adFormat: ['brandboard_1', 'fullscreen'],
                    sizes: [...brandboardSizes, takeoverSize],
                    targetId: 'front_ad_1',
                },
            ],
         },
        mobile: {
            article: [
                {
                    adFormat: 'board_1',
                    sizes: [...boardSizes, nativeSize],
                    targetId: 'text_ad_1',
                },
            ],
            front: [
                {
                    adFormat: ['board_1', 'board_fullscreen'],
                    sizes: [...boardSizes, takeoverSize, nativeSize],
                    targetId: 'front_ad_1',
                },
            ],
        },
        get tablet() { //usually the same as desktop
            return adsConfig.placements.desktop;
        },
    }
```

If you want to add finn blink to a placement you need to add `finnBlink` object to placement config, e.g.:

```js
{
    adFormat: 'board_1',
    sizes: [...boardSizes, nativeSize],
    targetId: 'text_ad_1',
    finnBlink: {
        enabled: true,
        position: 'hayabusa_text_ad_1',
    },
}
```

### Initalization

Before you initialize ads you need to know if you can use glimr and have pulse instance.

Initialize adapter:

```js
function adsInitialization(pulseInstance) {
    return ads.initialize({
        glimr: {
            enabled: findGlimrPermissions(),
        },
        pulseInstance,
    });
}
```

If you want finn blink ads it should look like this:

```js
async function adsInitialization(pulseInstance, user) {
    const hasGlimrPermissions = await findGlimrPermissions();

    return ads.initialize({
        finnBlink: {
            enabled: Boolean(hasBlinkAdPermission() && user?.userId && user?.uuid),
            userId: user?.userId,
            uuid: user?.uuid,
        },
        glimr: {
            enabled: hasGlimrPermissions,
        },
        pulseInstance,
    });
}
```

Where `hasBlinkAdPermission` looks like this:

```js
const hasBlinkAdPermission = () =>
    currentPrivacyPermissions.some(
        (permission) =>
            permission.purpose?.match('processingpurpose:targeted_advertising') &&
            permission.data?.some((entry) => entry.match('datacategory:interests'))
    );
```

If glimr consent changes at any point you can update it like this:

```js
ads.glimr = {
    enabled: findGlimrPermissions(),
};
```

If finn blink consent or user changes you can update it like this:

```js
ads.finnBlink = {
    enabled: hasBlinkAdPermission() && currentUser?.userId && currentUser?.uuid,
    userId: currentUser?.userId,
    uuid: currentUser?.uuid,
};
```

You can skip any fields if you don't have them and update just `enabled` for example.

If device changes you can update it like this:

```js
ads.device = device;
```

### Loading ads

You are ready to load ads. On any page that you want ads to show you can call `loadAds` like this:

```js
ads.loadAds({
    keywords: {
        'aa-sch-context_keyword': adsKeywords,
        'no-sno-news-sponsors': adTags,
    },
    pageType: 'article',
});
```

`pageType` is required but you can skip `keywords` if you don't want to pass any additional keywords.

## Warning

- at the moment there is no way to clear already loaded ads, this functionality was not needed for MinMote but likely will be introduced with Tek and Pent
- **you must use `_` character as a delimiter in target ids** (e.g. `front-ad-1` must be changed to `front_ad_1`) this is currently a limitation of `@sch-inventory/advertory`