summaryrefslogtreecommitdiff
path: root/node_modules/@jet-app/app-store/tmp/src/common/metrics/helpers/models.js
blob: 3456e9ea992e9c5331889994844c91d7644083a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
import * as metricsLocation from "./location";
import * as metricsUtil from "./util";
import { isNothing, isSome } from "@jet/environment/types/optional";
import * as serverData from "../../../foundation/json-parsing/server-data";
import { attributeAsDictionary } from "../../../foundation/media/attributes";
import { shallowCopyOf } from "../../../foundation/util/objects";
import { productVariantDataForData, productVariantDataHasVariant } from "../../product-page/product-page-variants";
import { EventLinter } from "../event-linter";
import { eligibleSlotPositionsForAdPlacement } from "../../ads/ad-common";
import { FlattenedTodayItemType } from "../../today/today-parse-util";
import { getSelectedCustomCreativeId } from "../../search/custom-creative";
export const iAdURLParameterStringToken = "X-AppStore-iAdClickToken";
export const iAdURLLineItemParameterStringToken = "X-AppStore-iAdLineItem";
export const iAdDismissAdActionMetricsParameterStringToken = "X-AppStore-iAdDismissAdActionMetrics";
// User defined type guard for determining if an object conforms to ContentMetricsOptions interface.
export function isContentMetricsOptions(object) {
    return object && Object.prototype.hasOwnProperty.call(object, "id");
}
export class IAdSearchInformation {
    /**
     * Initialise a new `IAdSearchInformation`
     * @param objectGraph The Object Graph.
     * @param placementType The placement type for the ad this object is tracking.
     * @param baseSlotInformation The initial list of slotInfos for this ad placement
     * @param iAdId The unique id for the ad instance.
     * @param appStoreClientRequestId The unique id for the client requesting the ad.
     * @param wasOdmlSuccessful Whether native ODML processing was successful.
     * @param positionInfo The position info data describing the requested position of this ad.
     */
    constructor(objectGraph, placementType, baseSlotInformation, iAdId, appStoreClientRequestId, wasOdmlSuccessful, positionInfo) {
        this.placementType = placementType;
        this.placementId = placementType === null ? null : this.placementIdFromType(placementType);
        this.pageFields = {};
        this.clickFields = {};
        this.impressionsFields = {};
        this.fastImpressionFields = {};
        this.iAdClickEventFields = {};
        this._iAdApplied = false;
        this._iAdAdamId = undefined;
        this.positionInfo = positionInfo;
        this.slotInfo = baseSlotInformation;
        this.setInitialAdData(objectGraph, iAdId, appStoreClientRequestId);
        if (serverData.isDefinedNonNull(wasOdmlSuccessful)) {
            this.pageFields["iAdOdmlSuccess"] = wasOdmlSuccessful;
        }
        this.fastImpressionFields["iAdEligible"] = true;
    }
    /**
     * Construct an IAdSearchInformation from the given JSON representation.
     * This is necessary over and above standard JSON parsing to preserve our ability to call functions on this object.
     * @param objectGraph The Object Graph.
     * @param json The JSON representation of the object.
     * @returns A constructed IAdSearchInformation from the JSON.
     */
    static from(objectGraph, json) {
        var _a, _b, _c, _d;
        const iAdInfo = new IAdSearchInformation(objectGraph, serverData.asString(json.placementType), serverData.asArrayOrEmpty(json.slotInfo), (_a = serverData.asString(json.iAdId)) !== null && _a !== void 0 ? _a : undefined, (_b = serverData.asString(json.appStoreClientRequestId)) !== null && _b !== void 0 ? _b : undefined, (_c = serverData.asBoolean(json.wasOdmlSuccessful)) !== null && _c !== void 0 ? _c : undefined, serverData.asInterface(json.positionInfo));
        iAdInfo._iAdApplied = serverData.asBooleanOrFalse(json._iAdApplied);
        iAdInfo._iAdAdamId = (_d = serverData.asString(json._iAdAdamId)) !== null && _d !== void 0 ? _d : undefined;
        Object.assign(iAdInfo.pageFields, json.pageFields);
        Object.assign(iAdInfo.clickFields, json.clickFields);
        Object.assign(iAdInfo.impressionsFields, json.impressionsFields);
        Object.assign(iAdInfo.fastImpressionFields, json.fastImpressionFields);
        Object.assign(iAdInfo.iAdClickEventFields, json.iAdClickEventFields);
        iAdInfo.updateContainerId(serverData.asString(json.containerId));
        return iAdInfo;
    }
    /**
     * Create an array of `IAdSlotInformation` objects based on the current ad information available.
     * This isn't ideal, but we need to understand the available ad slots so we can report
     * on all slots, whether they have ads in them or not.
     */
    static createInitialSlotInfos(objectGraph, placementType, positionInfo, flattenedTodayFeed) {
        var _a;
        switch (placementType) {
            case "productPageYMAL":
            case "productPageYMALDuringDownload":
                const productPageContainerId = IAdSearchInformation.containerIdFromType(placementType);
                const slotIndex = (_a = positionInfo === null || positionInfo === void 0 ? void 0 : positionInfo.slot) !== null && _a !== void 0 ? _a : 0;
                const productPageSlot = {
                    slotId: `${productPageContainerId}_${slotIndex}`,
                    slotIndex: slotIndex,
                    hasAdData: false,
                };
                return [
                    {
                        containerId: productPageContainerId,
                        slots: [productPageSlot],
                    },
                ];
                break;
            case "today":
                const placementEligibleSlotPositions = eligibleSlotPositionsForAdPlacement(objectGraph, placementType);
                const eligibleAdSlots = isNothing(placementEligibleSlotPositions)
                    ? []
                    : placementEligibleSlotPositions.map((slotPosition) => slotPosition.slot);
                const containerSlotInfoMapping = {};
                /// iAd provides slots that are not zero based, adjust the slot
                const adjustedAdSlot = isSome(positionInfo) ? positionInfo.slot - 1 : null;
                let adPositionEncountered = false;
                eligibleAdSlots.forEach((eligibleIndex) => {
                    var _a;
                    // If we've reached the ad we need to subtract 1 from the index, to act as if the ad ad was
                    // actually part of the feed.
                    const augmentedFeedIndexIndex = adPositionEncountered ? eligibleIndex - 1 : eligibleIndex;
                    const todayItem = flattenedTodayFeed === null || flattenedTodayFeed === void 0 ? void 0 : flattenedTodayFeed.find((item) => item.containedAdSlots.includes(augmentedFeedIndexIndex));
                    const isAdSlotIndex = adjustedAdSlot === eligibleIndex;
                    const todayContainerId = iAdContainerIdForSlotInTodayItem(augmentedFeedIndexIndex, isAdSlotIndex, todayItem);
                    const containerSlotInformation = (_a = containerSlotInfoMapping[todayContainerId]) !== null && _a !== void 0 ? _a : {
                        containerId: todayContainerId,
                        slots: [],
                    };
                    containerSlotInfoMapping[todayContainerId] = containerSlotInformation;
                    const todaySlot = {
                        slotId: `${todayContainerId}_${eligibleIndex}`,
                        slotIndex: eligibleIndex,
                        hasAdData: false,
                    };
                    containerSlotInformation.slots.push(todaySlot);
                    adPositionEncountered = adPositionEncountered || isAdSlotIndex;
                });
                return Object.values(containerSlotInfoMapping);
            default:
                return null;
        }
    }
    get iAdIsPresent() {
        return this._iAdApplied;
    }
    get iAdAdamId() {
        return this._iAdAdamId;
    }
    /**
     * Update our information with a new ad response.
     * This is primarily used for asynchronous ad requests, where we need to update the metrics info subsequent to the initial page load.
     * @param objectGraph The Object Graph.
     * @param adResponse An ad response to use to update some fields.
     */
    updateForAdResponse(objectGraph, adResponse) {
        var _a;
        if (serverData.isNull(adResponse)) {
            return;
        }
        this.placementType = adResponse.placementType;
        this.placementId = this.placementIdFromType(this.placementType);
        this.positionInfo = (_a = adResponse.onDeviceAd) === null || _a === void 0 ? void 0 : _a.positionInfo;
        this.setInitialAdData(objectGraph, adResponse.iAdId, adResponse.clientRequestId);
    }
    /**
     * Set the initial ad data, if available.
     * This function requires that an ad fetch has been made, and can be called from two paths:
     * 1. Via the constructor, where the ad fetch is made at page load time, or
     * 2. Via `updateForAdResponse`, where the ad fetch was made asynchronously after the page load.
     * @param objectGraph The Object Graph.
     * @param iAdId The unique id provided for the ad instance.
     * @param appStoreClientRequestId The unique id representing the App Store ad request client.
     */
    setInitialAdData(objectGraph, iAdId, appStoreClientRequestId) {
        if (isNothing(appStoreClientRequestId)) {
            return;
        }
        // ToroID Suppression expects this to be replaced with -1 for figaro events
        const iAdIdValue = isNothing(iAdId) ? "-1" : iAdId;
        this.pageFields[EventLinter.hasIAdData] = true;
        switch (this.placementType) {
            case "today":
            case "productPageYMAL":
            case "productPageYMALDuringDownload":
                // Only set `hasIAdData` on impressions fields initially if it's a Chainlink placement.
                // This covers the "ad eligible" cases where we still want to check ad metrics when ads are not present.
                this.impressionsFields[EventLinter.hasIAdData] = true;
                break;
            default:
                break;
        }
        this.pageFields["iAdAppStoreClientRequestId"] = appStoreClientRequestId;
        switch (this.placementType) {
            case "today":
            case "productPageYMAL":
            case "productPageYMALDuringDownload":
                this.clickFields["iAdAppStoreClientRequestId"] = appStoreClientRequestId;
                this.impressionsFields["iAdAppStoreClientRequestId"] = appStoreClientRequestId;
                break;
            default:
                break;
        }
        this.pageFields["iAdId"] = iAdIdValue;
        this.impressionsFields["iAdId"] = iAdIdValue;
        this.clickFields["iAdId"] = iAdIdValue;
        this.updateContainerId(null);
        // Update slot info with data
        this.updateSlotInfo();
        if (serverData.isDefinedNonNullNonEmpty(this.slotInfo)) {
            this.pageFields["iAdSlotInfo"] = this.slotInfo;
            this.clickFields["iAdSlotInfo"] = this.slotInfo;
            this.impressionsFields["iAdSlotInfo"] = this.slotInfo;
        }
        if (this.placementId !== null && this.placementId.length > 0) {
            this.pageFields["iAdPlacementId"] = this.placementId;
            this.clickFields["iAdPlacementId"] = this.placementId;
            // Attach the `iAdPlacementId` to top-level impressions for v4 impressions.
            this.impressionsFields["iAdPlacementId"] = this.placementId;
            // For Chainlink placements, the `iAdPlacementId` sits within the impressions array.
            // We manually remove `iAdPlacementId` from the top-level fields for v5 impressions in `createMetricsFastImpressionsData`.
            switch (this.placementType) {
                case "today":
                case "productPageYMAL":
                case "productPageYMALDuringDownload":
                    this.fastImpressionFields["iAdPlacementId"] = this.placementId;
                    break;
                default:
                    break;
            }
        }
    }
    /**
     * Update the containerId based on the current placementType.
     */
    updateContainerId(containerId) {
        if (this.placementType === "today") {
            this.containerId = containerId !== null && containerId !== void 0 ? containerId : null;
            if (serverData.isDefinedNonNull(this.containerId)) {
                this.clickFields["iAdContainerId"] = this.containerId;
                this.fastImpressionFields["iAdContainerId"] = this.containerId;
            }
        }
        else {
            this.containerId =
                this.placementType === null ? null : IAdSearchInformation.containerIdFromType(this.placementType);
            if (serverData.isDefinedNonNull(this.containerId)) {
                this.pageFields["iAdContainerId"] = this.containerId;
                this.clickFields["iAdContainerId"] = this.containerId;
                this.fastImpressionFields["iAdContainerId"] = this.containerId;
            }
        }
    }
    /**
     * @param slotIndex The slot index to look for a container Id for
     * @returns The container Id for the given slot index, based off the slot infos
     */
    containerIdForSlotIndex(slotIndex) {
        if (isNothing(slotIndex) || isNothing(this.slotInfo)) {
            return null;
        }
        for (const slotInfo of this.slotInfo) {
            for (const slot of slotInfo.slots) {
                if (slot.slotIndex === slotIndex) {
                    return slotInfo.containerId;
                }
            }
        }
        return this.containerId;
    }
    apply(objectGraph, adData) {
        if (isNothing(adData) || serverData.isNullOrEmpty(adData)) {
            return;
        }
        const iAdAdamId = adData.id;
        const iAdConfigurationDictionary = attributeAsDictionary(adData, "iad");
        this._iAdAdamId = iAdAdamId;
        if (iAdConfigurationDictionary) {
            this.impressionsFields[EventLinter.hasIAdData] = true;
            this.clickFields[EventLinter.hasIAdData] = true;
            const impressionId = metricsUtil.emptyStringIfNullOrUndefined(iAdConfigurationDictionary["impressionId"]);
            this.fastImpressionFields["iAdImpressionId"] = impressionId;
            this.clickFields["iAdImpressionId"] = impressionId;
            const metadata = metricsUtil.emptyStringIfNullOrUndefined(iAdConfigurationDictionary["metadata"]);
            this.clickFields["iAdMetadata"] = metadata;
            this.fastImpressionFields["iAdMetadata"] = metadata;
            // Ads boldly populate `adamId` on pages. This is correct.
            this.pageFields["adamId"] = iAdAdamId;
            this.pageFields["iAd"] = {
                iAdFormat: metricsUtil.sanitizedMetricsDictionary(serverData.asInterface(serverData.asJSONValue(iAdConfigurationDictionary), "format")),
                iAdAlgoId: metricsUtil.emptyStringIfNullOrUndefined(iAdConfigurationDictionary["algoId"]),
                iAdImpressionId: metricsUtil.emptyStringIfNullOrUndefined(iAdConfigurationDictionary["impressionId"]),
                iAdMetadata: metricsUtil.emptyStringIfNullOrUndefined(iAdConfigurationDictionary["metadata"]),
            };
            const productVariantData = productVariantDataForData(objectGraph, adData);
            this.updateIAdMetricsFieldsForProductVariantData(productVariantData, this.clickFields);
            if (preprocessor.CARRY_BUILD || preprocessor.DEBUG_BUILD) {
                if (objectGraph.featureFlags.isEnabled("aligned_region_artwork_2025A")) {
                    const selectedCustomCreativeId = getSelectedCustomCreativeId(adData);
                    if (this.placementType === "today") {
                        this.updateIAdMetricsFieldsForAlignedRegion(selectedCustomCreativeId, this.fastImpressionFields);
                    }
                    else {
                        this.updateIAdMetricsFieldsForAlignedRegion(selectedCustomCreativeId, this.impressionsFields);
                    }
                    this.updateIAdMetricsFieldsForAlignedRegion(selectedCustomCreativeId, this.clickFields);
                }
            }
            Object.assign(this.iAdClickEventFields, iAdConfigurationDictionary);
            this._iAdApplied = true;
            // Clear out any prior missed opportunity reason if we have an ad.
            this.setMissedOpportunity(objectGraph, undefined, this.placementType);
        }
        // Update slot info after ad is applied.
        this.updateSlotInfo();
        if (serverData.isDefinedNonNullNonEmpty(this.slotInfo)) {
            this.pageFields["iAdSlotInfo"] = this.slotInfo;
            this.clickFields["iAdSlotInfo"] = this.slotInfo;
            this.impressionsFields["iAdSlotInfo"] = this.slotInfo;
        }
    }
    /**
     * Apply the click fields that were attached to the page request we're following.
     * This will generally be following on from click on a lockup with ad data attached, where we want to pull
     * some of the click fields from ad to be applied to this new page data.
     *
     * The goal here is to attach enough metadata into the incoming page that we can attribute any offer action
     * to the ad that was tapped on to reach this page. Whilst doing this we don't want to attach too much
     * ad data to the page, as this can start to interfere with metrics for other ad placements. This results in
     * us manually inserting and removing fields to achieve the right balance.
     * @param iAdAdamId The adamId for the ad.
     * @param fields The set of fields to apply to our clickFields.
     */
    applyClickFieldsFromPageRequest(iAdAdamId, fields) {
        this._iAdApplied = true;
        this._iAdAdamId = iAdAdamId;
        Object.assign(this.clickFields, fields);
        // We don't want to assign any of the fields to the page event, as this looks like an ad is being shown
        // on the page we're navigating to.
        Object.keys(this.pageFields).forEach((field) => delete this.pageFields[field]);
    }
    setSpecifiedAlignedRegionUsed(didUseSpecifiedCreative) {
        this.fastImpressionFields["iAdIsSpecifiedCreativeUsed"] = didUseSpecifiedCreative;
        this.clickFields["iAdIsSpecifiedCreativeUsed"] = didUseSpecifiedCreative;
    }
    /**
     * Set the template type for the page.
     */
    setTemplateType(templateType) {
        this.pageFields["iAdTemplateType"] = templateType;
        this.impressionsFields["iAdTemplateType"] = templateType;
        this.clickFields["iAdTemplateType"] = templateType;
    }
    setMissedOpportunity(objectGraph, reason, placementType) {
        this.missedOpportunityReason = reason;
        // Only set in page and impressions fields if reason is non-null.
        if (serverData.isDefinedNonNull(reason)) {
            this.clickFields["iAdMissedOpportunityReason"] = reason;
            // Chainlink placements don't expect "iAdMissedOpportunityReason" at the top level of impressions or page events.
            switch (this.placementType) {
                case "today":
                case "productPageYMAL":
                case "productPageYMALDuringDownload":
                    break;
                default:
                    this.pageFields["iAdMissedOpportunityReason"] = reason;
                    this.impressionsFields["iAdMissedOpportunityReason"] = reason;
                    break;
            }
        }
        else {
            delete this.clickFields["iAdMissedOpportunityReason"];
            // Generally, we just want to remove the missed opportunity reason if one isn't set.
            // The 'productPageYMALDuringDownload' placement is a special case where it's placed on the page
            // subsequent to a previous placement, and metrics is updated via the pageChange metrics.
            // This means that we need to send `null` as the value for this placement, in order to clear
            // any possible missed opportunity value from the placement it's replacing.
            switch (placementType) {
                case "productPageYMALDuringDownload":
                    this.pageFields["iAdMissedOpportunityReason"] = null;
                    this.impressionsFields["iAdMissedOpportunityReason"] = null;
                    break;
                default:
                    delete this.pageFields["iAdMissedOpportunityReason"];
                    delete this.impressionsFields["iAdMissedOpportunityReason"];
                    break;
            }
        }
        // Only set in page and impressions fields if reason is non-null.
        if (serverData.isDefinedNonNull(reason)) {
            this.pageFields["iAdMissedOpportunityReason"] = reason;
            this.impressionsFields["iAdMissedOpportunityReason"] = reason;
        }
        else {
            delete this.pageFields["iAdMissedOpportunityReason"];
            delete this.impressionsFields["iAdMissedOpportunityReason"];
        }
        // Update slotInfo with missed opportunity reason.
        this.updateSlotInfo();
        if (serverData.isDefinedNonNullNonEmpty(this.slotInfo)) {
            this.pageFields["iAdSlotInfo"] = this.slotInfo;
            this.clickFields["iAdSlotInfo"] = this.slotInfo;
            this.impressionsFields["iAdSlotInfo"] = this.slotInfo;
        }
    }
    placementIdFromType(type) {
        switch (type) {
            case "searchLanding":
                return "APPSTORE_SEARCH_LANDING_PAGE";
            case "searchResults":
                return "APPSTORE_SEARCH_RESULT_PAGE";
            case "today":
                return "APPSTORE_TODAY_TAB";
            case "productPageYMAL":
                return "APPSTORE_PRODUCT_PAGE";
            case "productPageYMALDuringDownload":
                return "APPSTORE_PRODUCT_PAGE_DOWNLOAD";
            default:
                throw new Error(`This method should never be called with value: ${type}`);
        }
    }
    static placementTypeFromPlacementId(objectGraph, id) {
        switch (id) {
            case "APPSTORE_SEARCH_LANDING_PAGE":
                return "searchLanding";
            case "APPSTORE_SEARCH_RESULT_PAGE":
                return "searchResults";
            case "APPSTORE_TODAY_TAB":
                return "today";
            case "APPSTORE_PRODUCT_PAGE":
                return "productPageYMAL";
            case "APPSTORE_PRODUCT_PAGE_DOWNLOAD":
                return "productPageYMALDuringDownload";
            default:
                objectGraph.console.log(`Failed to get placementType from placementId ${id}. Defaulting to searchResults`);
                // For legacy reasons we fall back to `seachResults` when nothing is provided.
                // Being the first ad slot, in the past this has been assumed as the "default".
                return "searchResults";
        }
    }
    /**
     * Get a containerId value for a given placement type to attach to the ad's metrics.
     * @param type The AdPlacementType for the current ad.
     * @returns A string value for containerId, if the placement has one. Otherwise null.
     */
    static containerIdFromType(type) {
        switch (type) {
            case "productPageYMAL":
                return "customers-also-bought-apps";
            case "productPageYMALDuringDownload":
                return "customers-also-bought-apps-download";
            case "today":
                return null; // Today page has variable containerIds that are updated programatically
            default:
                return null;
        }
    }
    /**
     * Return the fast impressions metrics fields for an item using the location tracker.
     *
     * `fastImpressionFields` are the items inside the impressions array of metrics events. Although some fields are stable,
     * these are unique to a given position, so we must use that position information to generate the right fields.
     *
     * @param locationTracker A LocationTracker used to identify the relevant information to attach to the metrics fields.
     * @param adSlotOverride The ad slot to use for the metrics fields. If not provided, the ad slot will be inferred from
     * the location tracker. This is needed for today where the ad can span different shelves so the position is going to be 0 for multiple ads
     * @returns Relevant metrics fields for the item in an impressions array.
     */
    fastImpressionsFieldsForCurrentItem(locationTracker, adSlotOverride) {
        switch (this.placementType) {
            case "productPageYMAL":
            case "productPageYMALDuringDownload":
            case "today":
                let position;
                if (isSome(adSlotOverride)) {
                    position = adSlotOverride;
                }
                else {
                    const location = metricsLocation.currentLocation(locationTracker);
                    // If the current location is within a todayCard, we want to extract the location of the card, not the location
                    // of this lockup within the card.
                    if (location !== null && location.locationType === "todayCard") {
                        position = metricsLocation.previousPosition(locationTracker);
                    }
                    else {
                        position = metricsLocation.currentPosition(locationTracker);
                    }
                }
                const sharedFields = shallowCopyOf(this.fastImpressionFields);
                sharedFields["iAdSlotId"] = `${this.containerIdForSlotIndex(position)}_${position}`;
                if (position !== this.adjustedSlotIndex) {
                    // If the current position is not where the ad is, we only want a subset of the tracked fields.
                    const allowedFields = ["iAdEligible", "iAdPlacementId", "iAdContainerId", "iAdSlotId"];
                    Object.keys(sharedFields).forEach((key) => {
                        if (!allowedFields.includes(key)) {
                            delete sharedFields[key];
                        }
                    });
                }
                return sharedFields;
            default:
                return this.fastImpressionFields;
        }
    }
    /**
     * Get the adjusted slot index of the ad we're tracking.
     * This is zero-based - Ad Platforms returns slot info to us one-based but we always adjust it before using it anywhere.
     */
    get adjustedSlotIndex() {
        var _a;
        const positionInfoSlotIndex = (_a = this.positionInfo) === null || _a === void 0 ? void 0 : _a.slot;
        // The slot as provided by ad platforms is one-based - adjust it so we're working with zero-based numbers.
        if (serverData.isDefinedNonNull(positionInfoSlotIndex)) {
            return positionInfoSlotIndex - 1;
        }
        return null;
    }
    /**
     * Update the existing slot information now that new data has been applied.
     */
    updateSlotInfo() {
        if (isNothing(this.slotInfo)) {
            return;
        }
        switch (this.placementType) {
            case "productPageYMAL":
            case "productPageYMALDuringDownload":
                for (const containerSlotInfo of this.slotInfo) {
                    for (const slot of containerSlotInfo.slots) {
                        slot.hasAdData = this.iAdIsPresent;
                        if (serverData.isDefinedNonNull(this.missedOpportunityReason)) {
                            slot.missedOpportunityReason = this.missedOpportunityReason;
                        }
                    }
                }
                break;
            case "today":
                const adjustedSlotIndex = this.adjustedSlotIndex;
                for (const containerSlotInfo of this.slotInfo) {
                    for (const slot of containerSlotInfo.slots) {
                        // Check whether the current index matches our slot information.
                        // If so, an ad is intended to be placed here.
                        const isAdInCurrentIndex = adjustedSlotIndex === slot.slotIndex;
                        // In order to understand whether a given slot has ad data, we ensure that a usable ad is present,
                        // and the current slot is the one an ad is placed in.
                        const hasAdData = this.iAdIsPresent && isAdInCurrentIndex;
                        // By default, use the missed opportunity reason, if any, stored here.
                        let missedOpportunityReason = this.missedOpportunityReason;
                        // If:
                        // - the ad is not in the current index we're building slot info for, and
                        // - there is a valid slot index
                        // we override the reason to 'NOAD', as it means another eligible slot was filled.
                        if (!isAdInCurrentIndex && serverData.isDefinedNonNull(adjustedSlotIndex)) {
                            missedOpportunityReason = "NOAD";
                        }
                        slot.hasAdData = hasAdData;
                        if (serverData.isDefinedNonNull(missedOpportunityReason)) {
                            slot.missedOpportunityReason = missedOpportunityReason;
                        }
                    }
                }
                break;
            default:
                break;
        }
    }
    /**
     * Modifies the provided MetricsFields for the ProductVariantData specific to fields required for iAds.
     * @param productVariantData The variant data of impressionable data to apply.
     * @param metricsFields An existing set of MetricsFields to modify.
     */
    updateIAdMetricsFieldsForProductVariantData(productVariantData, metricsFields) {
        if (isSome(productVariantData) && productVariantDataHasVariant(productVariantData, "customProductPage")) {
            metricsFields["iAdPageCustomId"] = productVariantData.productPageId;
        }
        else {
            // If a custom product page doesn't exist, we remove the fields.
            delete metricsFields["iAdPageCustomId"];
        }
    }
    /**
     * Modifies the provided MetricsFields to include the custom id for iAds.
     * @param selectedCustomCreativeId The id for the custom creative that we want to set as custom id.
     * @param metricsFields An existing set of MetricsFields to modify.
     */
    updateIAdMetricsFieldsForAlignedRegion(selectedCustomCreativeId, metricsFields) {
        if (preprocessor.CARRY_BUILD || preprocessor.DEBUG_BUILD) {
            if (isSome(selectedCustomCreativeId)) {
                metricsFields["iAdCustomId"] = selectedCustomCreativeId;
            }
            else {
                delete metricsFields["iAdCustomId"];
            }
        }
    }
    /**
     * Return the event version for fast impressions based on the placement of the ad.
     * Impressions v5 is being rolled out first for Chainlink. Once all impressions have adopted it, this can be removed.
     */
    get fastImpressionsEventVersion() {
        switch (this.placementType) {
            case "productPageYMAL":
            case "productPageYMALDuringDownload":
            case "today":
                return 5;
            default:
                return 4;
        }
    }
    /**
     * Return whether ad rotation fields should be included on metrics events, based on the current placement type.
     */
    get shouldIncludeAdRotationFields() {
        switch (this.placementType) {
            case "productPageYMAL":
            case "productPageYMALDuringDownload":
            case "today":
                return false;
            case "searchLanding":
            case "searchResults":
                return true;
            default:
                return true;
        }
    }
}
/**
 * Return the container id to use for a today feed item
 * @param slotIndex The slot index that we found a TodayItem for
 * @param isAdSlot Whether this slotIndex is the slot the ad is in
 * @param todayItem The today item that this card is contained in, this could be a single item or a story group item
 * @returns The configuration to use when parsing a today card
 */
export function iAdContainerIdForSlotInTodayItem(slotIndex, isAdSlot, todayItem) {
    if (isNothing(todayItem)) {
        return "0";
    }
    switch (todayItem.type) {
        case FlattenedTodayItemType.EditorialItemGroup:
            const storyGroupHasMultipleItems = todayItem.containedAdSlots.length > 1;
            const slotIndexIsFirstOrLastInStoryGroup = !storyGroupHasMultipleItems ||
                slotIndex === todayItem.containedAdSlots[0] ||
                slotIndex === todayItem.containedAdSlots[todayItem.containedAdSlots.length - 1];
            if (isAdSlot && slotIndexIsFirstOrLastInStoryGroup) {
                // If the story group has fewer than 2 items, that means theres not valid slot **in** the actual story group
                // to place the ad, so its going to be at the top level. Or if there are multiple items, but the slot is the
                // first or last item there is no reason to pull the ad into the story group, it should be top level.
                return "0";
            }
            else {
                // Using the `data` from the todayItem here and not the todayCardData, because
                // this will be the data for the containing story gorup
                return "0";
            }
        default:
            return "0";
    }
}
/**
 * Create the iAdInformation for a given page if necessary
 * @param objectGraph The dependency graph for the app store
 * @param pageId The id of the page this iAd info is being used for
 * @param response The MAPI response for this page
 * @param positionInfo The position information on where the ad will attempted to be inserted
 * @param flattenedTodayFeed The flattened to today feed if this is for a today page
 * @returns The iAdInformation to be used in a metrics page information object
 */
export function iAdInformationFromMediaApiResponse(objectGraph, pageId, response, positionInfo = null, flattenedTodayFeed = null) {
    var _a;
    /** <rdar://problem/33764430> Toro: iAd is missing AD_OPEN figaro event when tapping on ad and pressing OPEN through the product page */
    const iAdClickInfoString = serverData.asString(response, iAdURLParameterStringToken);
    if (isNothing(iAdClickInfoString)) {
        return null;
    }
    const iAdClickInfo = JSON.parse(iAdClickInfoString);
    // rdar://72607206 (Gibraltar: Tech Debt: Population of iAd fields in Product Page)
    const placementType = IAdSearchInformation.placementTypeFromPlacementId(objectGraph, serverData.asString(iAdClickInfo, "iAdPlacementId"));
    const iAdInfo = new IAdSearchInformation(objectGraph, placementType, IAdSearchInformation.createInitialSlotInfos(objectGraph, placementType, positionInfo, flattenedTodayFeed), (_a = serverData.asString(iAdClickInfo, "iAdId")) !== null && _a !== void 0 ? _a : undefined, undefined, undefined, positionInfo);
    iAdInfo.applyClickFieldsFromPageRequest(pageId !== null && pageId !== void 0 ? pageId : undefined, iAdClickInfo);
    return iAdInfo;
}
/** @public */
export class MetricsPageInformation {
    constructor(base = {}) {
        this.baseFields = base;
    }
}
//# sourceMappingURL=models.js.map