import { isNothing, isSome } from "@jet/environment/types/optional"; import * as derivedData from "../../foundation/json-parsing/derived-data"; import * as serverData from "../../foundation/json-parsing/server-data"; import * as mediaAttributes from "../../foundation/media/attributes"; import * as mediaPlatformAttributes from "../../foundation/media/platform-attributes"; import { variantAttributeForKey } from "../product-page/product-page-variants"; import * as contentDeviceFamily from "./device-family"; /** * Retrieve the specified attribute from the data, coercing it to a JSONData dictionary * * @param data The data from which to retrieve the attribute. * @param attributePath The path of the attribute. * @param attributePlatform The specific platform attribute to get content for. Omit to infer from the data's structure. * @param defaultValue The object to return if the path search fails. * @returns The dictionary of data */ export function contentAttributeAsDictionary(objectGraph, data, attributePath, attributePlatform, defaultValue) { if (!attributePlatform) { attributePlatform = bestAttributePlatformFromData(objectGraph, data); } if (isNothing(attributePlatform)) { return null; } let value = mediaPlatformAttributes.platformAttributeAsDictionary(data, attributePlatform, attributePath); if (!value) { value = mediaAttributes.attributeAsDictionary(data, attributePath, defaultValue); } return value; } /** * Retrieve the specified attribute from the data as an array, coercing to an empty array if the object is not an array. * * @param data The data from which to retrieve the attribute. * @param attributePath The path of the attribute. * @param attributePlatformOverride An override platform, from which to fetch the attribute. * @returns {any[]} The attribute value as an array. */ export function contentAttributeAsArrayOrEmpty(objectGraph, data, attributePath, attributePlatformOverride = undefined) { const attributePlatform = attributePlatformOverride !== null && attributePlatformOverride !== void 0 ? attributePlatformOverride : bestAttributePlatformFromData(objectGraph, data); if (isNothing(attributePlatform)) { return []; } let value = mediaPlatformAttributes.platformAttributeAsArrayOrEmpty(data, attributePlatform, attributePath); if (serverData.isNullOrEmpty(value)) { value = mediaAttributes.attributeAsArrayOrEmpty(data, attributePath); } return value; } /** * Retrieve the specified attribute from the data as an array. * * @param data The data from which to retrieve the attribute. * @param attributePath The path of the attribute. * @param attributePlatformOverride An override platform, from which to fetch the attribute. * @returns {any[]} The attribute value as an array. */ export function contentAttributeAsArray(objectGraph, data, attributePath, attributePlatformOverride = undefined) { const attributePlatform = attributePlatformOverride !== null && attributePlatformOverride !== void 0 ? attributePlatformOverride : bestAttributePlatformFromData(objectGraph, data); if (isNothing(attributePlatform)) { return null; } let value = mediaPlatformAttributes.platformAttributeAsArray(data, attributePlatform, attributePath); if (isNothing(value)) { value = mediaAttributes.attributeAsArray(data, attributePath); } return value; } /** * Retrieve the specified attribute from the data as a string. * * @param data The data from which to retrieve the attribute. * @param attributePath The object path for the attribute. * @param attributePlatformOverride An override platform, from which to fetch the attribute. * @param policy The validation policy to use when resolving this value. * @returns {string} The attribute value as a string. */ export function contentAttributeAsString(objectGraph, data, attributePath, attributePlatformOverride = undefined, policy = "coercible") { let value; const attributePlatform = attributePlatformOverride !== null && attributePlatformOverride !== void 0 ? attributePlatformOverride : bestAttributePlatformFromData(objectGraph, data); if (isSome(attributePlatform)) { value = mediaPlatformAttributes.platformAttributeAsString(data, attributePlatform, attributePath, policy); } if (!value) { value = mediaAttributes.attributeAsString(data, attributePath, policy); } return value; } /** * Retrieve the specified attribute from the data as a boolean. * * @param data The data from which to retrieve the attribute. * @param attributePath The path of the attribute. * @param policy The validation policy to use when resolving this value. * @returns {boolean} The attribute value as a boolean. */ export function contentAttributeAsBoolean(objectGraph, data, attributePath, attributePlatform, policy = "coercible") { if (!attributePlatform) { attributePlatform = bestAttributePlatformFromData(objectGraph, data); } if (isNothing(attributePlatform)) { return null; } let value = mediaPlatformAttributes.platformAttributeAsBoolean(data, attributePlatform, attributePath, policy); if (serverData.isNull(value)) { value = mediaAttributes.attributeAsBoolean(data, attributePath, policy); } return value; } /** * Retrieve the specified attribute from the data as a boolean, which will be `false` if the attribute does not exist. * * @param data The data from which to retrieve the attribute. * @param attributePath The path of the attribute. * @param attributePlatform The specific platform attribute to get content for. Omit to infer from the data's structure. * @returns {boolean} The attribute value as a boolean, coercing to `false` if the value is not present.. */ export function contentAttributeAsBooleanOrFalse(objectGraph, data, attributePath, attributePlatform) { if (!attributePlatform) { attributePlatform = bestAttributePlatformFromData(objectGraph, data); } if (isNothing(attributePlatform)) { return false; } let value = mediaPlatformAttributes.platformAttributeAsBoolean(data, attributePlatform, attributePath); if (serverData.isNull(value)) { value = mediaAttributes.attributeAsBooleanOrFalse(data, attributePath); } return value; } /** * Retrieve the specified attribute from the data as a number. * * @param data The data from which to retrieve the attribute. * @param attributePath The path of the attribute. * @param policy The validation policy to use when resolving this value. * @returns {boolean} The attribute value as a number. */ export function contentAttributeAsNumber(objectGraph, data, attributePath, policy = "coercible") { const attributePlatform = bestAttributePlatformFromData(objectGraph, data); if (isNothing(attributePlatform)) { return null; } let value = mediaPlatformAttributes.platformAttributeAsNumber(data, attributePlatform, attributePath, policy); if (serverData.isNull(value)) { value = mediaAttributes.attributeAsNumber(data, attributePath); } return value; } /** * Computes the best attribute platform for a given piece of content * * @param {Data} data The media API data representing the content * @returns {AttributePlatform} */ export function bestAttributePlatformFromData(objectGraph, data, clientIdentifierOverride) { const baseCacheKey = "bestAttributePlatformFromData"; const cacheKey = isSome(clientIdentifierOverride) ? `${baseCacheKey}.${clientIdentifierOverride}` : baseCacheKey; return derivedData.value(data, cacheKey, () => { const isIOSOnly = contentDeviceFamily.dataOnlyHasDeviceFamilies(objectGraph, data, ["iphone", "ipad", "ipod"], true); const isTvOnly = contentDeviceFamily.dataOnlyHasDeviceFamily(objectGraph, data, "tvos"); const isMacOnly = contentDeviceFamily.dataOnlyHasDeviceFamily(objectGraph, data, "mac"); const isWatchOnly = contentDeviceFamily.dataOnlyHasDeviceFamily(objectGraph, data, "watch"); const isVisionOnly = contentDeviceFamily.dataOnlyHasDeviceFamily(objectGraph, data, "realityDevice"); // 1. The data is for a single platform only. let dedicatedPlatform = null; if (isTvOnly) { dedicatedPlatform = "appletvos"; } else if (isMacOnly) { dedicatedPlatform = "osx"; } else if (isIOSOnly) { dedicatedPlatform = "ios"; } else if (isWatchOnly) { dedicatedPlatform = "watch"; } else if (isVisionOnly) { dedicatedPlatform = "xros"; } if (!serverData.isNull(dedicatedPlatform)) { return dedicatedPlatform; } // 2. Loop through our preferred ordering of platforms and use the first one that has platformAttributes present. const alternatePlatforms = defaultAttributePlatformOrdering(objectGraph, clientIdentifierOverride); for (const candidatePlatform of alternatePlatforms) { if (mediaPlatformAttributes.hasPlatformAttribute(data, candidatePlatform)) { return candidatePlatform; } } // 3. Catch-All return defaultAttributePlatform(objectGraph); }); } /** * Computes the best attribute platform for a given Media API Marketplace * response. Since Marketplace responses don't contain a top-level * `deviceFamilies` property, this employs an alternative method from * `bestAttributePlatformFromData()` to get the attribute platform. * * @param objectGraph The App Store object graph * @param data The Media API Marketplace response to search for an attribute platform. * @returns The most appropriate attribute platform available for the current client. */ export function bestAttributePlatformFromMarketplaceData(objectGraph, data) { // 1. Iterate through the client's preferred platform ordering until we // find the first one present in the response. const preferredAttributePlatforms = defaultAttributePlatformOrdering(objectGraph); for (const attributePlatform of preferredAttributePlatforms) { const versionsAttributes = contentAttributeAsDictionary(objectGraph, data, "versionAttributes", attributePlatform); if (serverData.isDefinedNonNullNonEmpty(versionsAttributes)) { return attributePlatform; } } // 2. Catch-All return defaultAttributePlatform(objectGraph); } /** * The default attribute platform for the current client */ export function defaultAttributePlatform(objectGraph) { var _a; if ((_a = objectGraph.activeIntent) === null || _a === void 0 ? void 0 : _a.attributePlatform) { return objectGraph.activeIntent.attributePlatform; } switch (objectGraph.client.deviceType) { case "phone": case "pad": return "ios"; case "tv": return "appletvos"; case "mac": return "osx"; case "watch": return "watch"; case "vision": return "xros"; default: return null; } } /** * The preferred ordering to use given our default platform. */ function defaultAttributePlatformOrdering(objectGraph, clientIdentifierOverride) { const defaultPlatform = defaultAttributePlatform(objectGraph); if (defaultPlatform === null) { // If the `"web"` client is active and there is not an "active intent" to // inform a default platform, fall back to a hard-coded ordering if (objectGraph.client.isWeb) { return ["ios", "osx", "xros", "watch", "appletvos"]; } else { return []; } } switch (defaultPlatform) { case "ios": if (clientIdentifierOverride === "VisionAppStore" /* ClientIdentifier.VisionAppStore */ || clientIdentifierOverride === "com.apple.visionproapp" /* ClientIdentifier.VisionCompanion */) { return ["xros", "ios", "appletvos", "osx"]; } else { return ["ios", "appletvos", "osx", "xros"]; } case "osx": return ["osx", "ios", "appletvos", "xros"]; case "appletvos": return ["appletvos", "ios", "osx", "xros"]; case "watch": // Per Hiren Kotadia on 2019-2-26, watch platform attributes will always be under ios. // We're going to promote ios to the head of the search list to speed up Media API // response parsing. We'll keep watch as #2 in the list so that if this changes in // the future, it should mostly just work. -km return ["ios", "watch", "osx", "xros"]; case "xros": return ["xros", "ios", "appletvos", "osx"]; default: return [defaultPlatform]; } } // region Variant Attributes /** * Retrieve the attribute for a specific platform's variant attribute as a dictionary, from custom attributes or standard attributes. * @param data Data to get attribute for. * @param productVariantData Variant data to use when finding item. * @param attributeKey The key to fetch in platform attributes. May be converted to custom attribute key. * @param attributePlatform The platform to fetch attribute for. Defaults to current platform if unspecified. */ export function customAttributeAsDictionary(objectGraph, data, productVariantData, attributeKey, attributePlatform) { // Use `customAttributes.${customAttributeKey}` for platform if present const customAttributeKey = mediaAttributes.attributeKeyAsCustomAttributeKey(attributeKey); if (isNothing(customAttributeKey)) { return null; } const customAttributes = contentAttributeAsDictionary(objectGraph, data, "customAttributes", attributePlatform); const allowNondefaultTreatmentInNondefaultPage = mediaAttributes.attributeAllowsNonDefaultTreatmentInNonDefaultPage(customAttributeKey); const customAttribute = variantAttributeForKey(objectGraph, customAttributes, productVariantData, customAttributeKey, allowNondefaultTreatmentInNondefaultPage); if (serverData.isDefinedNonNullNonEmpty(customAttribute)) { return serverData.asDictionary(customAttribute); } // Otherwise, use `${attributeKey}` for platform. return contentAttributeAsDictionary(objectGraph, data, attributeKey, attributePlatform); } /** * Retrieve the attribute for a specific platform's variant attribute as a dictionary, from custom attributes or standard attributes. * @param data Data to get attribute for. * @param productVariantData Variant data to use when finding item. * @param attributeKey The key to fetch in platform attributes when custom attributes are not present. * @param attributePlatform The platform to fetch attribute for. Defaults to current platform if unspecified. */ export function customAttributeAsString(objectGraph, data, productVariantData, attributeKey, attributePlatform) { // Use `customAttributes.${customAttributeKey}` for platform if present const customAttributeKey = mediaAttributes.attributeKeyAsCustomAttributeKey(attributeKey); if (isNothing(customAttributeKey)) { return null; } const customAttributes = contentAttributeAsDictionary(objectGraph, data, "customAttributes", attributePlatform); const allowNondefaultTreatmentInNondefaultPage = mediaAttributes.attributeAllowsNonDefaultTreatmentInNonDefaultPage(customAttributeKey); const customAttribute = variantAttributeForKey(objectGraph, customAttributes, productVariantData, customAttributeKey, allowNondefaultTreatmentInNondefaultPage); if (serverData.isDefinedNonNullNonEmpty(customAttribute)) { return serverData.asString(customAttribute); } // Otherwise, use `${attributeKey}` for platform. return contentAttributeAsString(objectGraph, data, attributeKey); } // endregion //# sourceMappingURL=attributes.js.map