this repo has no description
1import { isNothing, isSome } from "@jet/environment/types/optional";
2import * as derivedData from "../../foundation/json-parsing/derived-data";
3import * as serverData from "../../foundation/json-parsing/server-data";
4import * as mediaAttributes from "../../foundation/media/attributes";
5import * as mediaPlatformAttributes from "../../foundation/media/platform-attributes";
6import { variantAttributeForKey } from "../product-page/product-page-variants";
7import * as contentDeviceFamily from "./device-family";
8/**
9 * Retrieve the specified attribute from the data, coercing it to a JSONData dictionary
10 *
11 * @param data The data from which to retrieve the attribute.
12 * @param attributePath The path of the attribute.
13 * @param attributePlatform The specific platform attribute to get content for. Omit to infer from the data's structure.
14 * @param defaultValue The object to return if the path search fails.
15 * @returns The dictionary of data
16 */
17export function contentAttributeAsDictionary(objectGraph, data, attributePath, attributePlatform, defaultValue) {
18 if (!attributePlatform) {
19 attributePlatform = bestAttributePlatformFromData(objectGraph, data);
20 }
21 if (isNothing(attributePlatform)) {
22 return null;
23 }
24 let value = mediaPlatformAttributes.platformAttributeAsDictionary(data, attributePlatform, attributePath);
25 if (!value) {
26 value = mediaAttributes.attributeAsDictionary(data, attributePath, defaultValue);
27 }
28 return value;
29}
30/**
31 * Retrieve the specified attribute from the data as an array, coercing to an empty array if the object is not an array.
32 *
33 * @param data The data from which to retrieve the attribute.
34 * @param attributePath The path of the attribute.
35 * @param attributePlatformOverride An override platform, from which to fetch the attribute.
36 * @returns {any[]} The attribute value as an array.
37 */
38export function contentAttributeAsArrayOrEmpty(objectGraph, data, attributePath, attributePlatformOverride = undefined) {
39 const attributePlatform = attributePlatformOverride !== null && attributePlatformOverride !== void 0 ? attributePlatformOverride : bestAttributePlatformFromData(objectGraph, data);
40 if (isNothing(attributePlatform)) {
41 return [];
42 }
43 let value = mediaPlatformAttributes.platformAttributeAsArrayOrEmpty(data, attributePlatform, attributePath);
44 if (serverData.isNullOrEmpty(value)) {
45 value = mediaAttributes.attributeAsArrayOrEmpty(data, attributePath);
46 }
47 return value;
48}
49/**
50 * Retrieve the specified attribute from the data as an array.
51 *
52 * @param data The data from which to retrieve the attribute.
53 * @param attributePath The path of the attribute.
54 * @param attributePlatformOverride An override platform, from which to fetch the attribute.
55 * @returns {any[]} The attribute value as an array.
56 */
57export function contentAttributeAsArray(objectGraph, data, attributePath, attributePlatformOverride = undefined) {
58 const attributePlatform = attributePlatformOverride !== null && attributePlatformOverride !== void 0 ? attributePlatformOverride : bestAttributePlatformFromData(objectGraph, data);
59 if (isNothing(attributePlatform)) {
60 return null;
61 }
62 let value = mediaPlatformAttributes.platformAttributeAsArray(data, attributePlatform, attributePath);
63 if (isNothing(value)) {
64 value = mediaAttributes.attributeAsArray(data, attributePath);
65 }
66 return value;
67}
68/**
69 * Retrieve the specified attribute from the data as a string.
70 *
71 * @param data The data from which to retrieve the attribute.
72 * @param attributePath The object path for the attribute.
73 * @param attributePlatformOverride An override platform, from which to fetch the attribute.
74 * @param policy The validation policy to use when resolving this value.
75 * @returns {string} The attribute value as a string.
76 */
77export function contentAttributeAsString(objectGraph, data, attributePath, attributePlatformOverride = undefined, policy = "coercible") {
78 let value;
79 const attributePlatform = attributePlatformOverride !== null && attributePlatformOverride !== void 0 ? attributePlatformOverride : bestAttributePlatformFromData(objectGraph, data);
80 if (isSome(attributePlatform)) {
81 value = mediaPlatformAttributes.platformAttributeAsString(data, attributePlatform, attributePath, policy);
82 }
83 if (!value) {
84 value = mediaAttributes.attributeAsString(data, attributePath, policy);
85 }
86 return value;
87}
88/**
89 * Retrieve the specified attribute from the data as a boolean.
90 *
91 * @param data The data from which to retrieve the attribute.
92 * @param attributePath The path of the attribute.
93 * @param policy The validation policy to use when resolving this value.
94 * @returns {boolean} The attribute value as a boolean.
95 */
96export function contentAttributeAsBoolean(objectGraph, data, attributePath, attributePlatform, policy = "coercible") {
97 if (!attributePlatform) {
98 attributePlatform = bestAttributePlatformFromData(objectGraph, data);
99 }
100 if (isNothing(attributePlatform)) {
101 return null;
102 }
103 let value = mediaPlatformAttributes.platformAttributeAsBoolean(data, attributePlatform, attributePath, policy);
104 if (serverData.isNull(value)) {
105 value = mediaAttributes.attributeAsBoolean(data, attributePath, policy);
106 }
107 return value;
108}
109/**
110 * Retrieve the specified attribute from the data as a boolean, which will be `false` if the attribute does not exist.
111 *
112 * @param data The data from which to retrieve the attribute.
113 * @param attributePath The path of the attribute.
114 * @param attributePlatform The specific platform attribute to get content for. Omit to infer from the data's structure.
115 * @returns {boolean} The attribute value as a boolean, coercing to `false` if the value is not present..
116 */
117export function contentAttributeAsBooleanOrFalse(objectGraph, data, attributePath, attributePlatform) {
118 if (!attributePlatform) {
119 attributePlatform = bestAttributePlatformFromData(objectGraph, data);
120 }
121 if (isNothing(attributePlatform)) {
122 return false;
123 }
124 let value = mediaPlatformAttributes.platformAttributeAsBoolean(data, attributePlatform, attributePath);
125 if (serverData.isNull(value)) {
126 value = mediaAttributes.attributeAsBooleanOrFalse(data, attributePath);
127 }
128 return value;
129}
130/**
131 * Retrieve the specified attribute from the data as a number.
132 *
133 * @param data The data from which to retrieve the attribute.
134 * @param attributePath The path of the attribute.
135 * @param policy The validation policy to use when resolving this value.
136 * @returns {boolean} The attribute value as a number.
137 */
138export function contentAttributeAsNumber(objectGraph, data, attributePath, policy = "coercible") {
139 const attributePlatform = bestAttributePlatformFromData(objectGraph, data);
140 if (isNothing(attributePlatform)) {
141 return null;
142 }
143 let value = mediaPlatformAttributes.platformAttributeAsNumber(data, attributePlatform, attributePath, policy);
144 if (serverData.isNull(value)) {
145 value = mediaAttributes.attributeAsNumber(data, attributePath);
146 }
147 return value;
148}
149/**
150 * Computes the best attribute platform for a given piece of content
151 *
152 * @param {Data} data The media API data representing the content
153 * @returns {AttributePlatform}
154 */
155export function bestAttributePlatformFromData(objectGraph, data, clientIdentifierOverride) {
156 const baseCacheKey = "bestAttributePlatformFromData";
157 const cacheKey = isSome(clientIdentifierOverride) ? `${baseCacheKey}.${clientIdentifierOverride}` : baseCacheKey;
158 return derivedData.value(data, cacheKey, () => {
159 const isIOSOnly = contentDeviceFamily.dataOnlyHasDeviceFamilies(objectGraph, data, ["iphone", "ipad", "ipod"], true);
160 const isTvOnly = contentDeviceFamily.dataOnlyHasDeviceFamily(objectGraph, data, "tvos");
161 const isMacOnly = contentDeviceFamily.dataOnlyHasDeviceFamily(objectGraph, data, "mac");
162 const isWatchOnly = contentDeviceFamily.dataOnlyHasDeviceFamily(objectGraph, data, "watch");
163 const isVisionOnly = contentDeviceFamily.dataOnlyHasDeviceFamily(objectGraph, data, "realityDevice");
164 // 1. The data is for a single platform only.
165 let dedicatedPlatform = null;
166 if (isTvOnly) {
167 dedicatedPlatform = "appletvos";
168 }
169 else if (isMacOnly) {
170 dedicatedPlatform = "osx";
171 }
172 else if (isIOSOnly) {
173 dedicatedPlatform = "ios";
174 }
175 else if (isWatchOnly) {
176 dedicatedPlatform = "watch";
177 }
178 else if (isVisionOnly) {
179 dedicatedPlatform = "xros";
180 }
181 if (!serverData.isNull(dedicatedPlatform)) {
182 return dedicatedPlatform;
183 }
184 // 2. Loop through our preferred ordering of platforms and use the first one that has platformAttributes present.
185 const alternatePlatforms = defaultAttributePlatformOrdering(objectGraph, clientIdentifierOverride);
186 for (const candidatePlatform of alternatePlatforms) {
187 if (mediaPlatformAttributes.hasPlatformAttribute(data, candidatePlatform)) {
188 return candidatePlatform;
189 }
190 }
191 // 3. Catch-All
192 return defaultAttributePlatform(objectGraph);
193 });
194}
195/**
196 * Computes the best attribute platform for a given Media API Marketplace
197 * response. Since Marketplace responses don't contain a top-level
198 * `deviceFamilies` property, this employs an alternative method from
199 * `bestAttributePlatformFromData()` to get the attribute platform.
200 *
201 * @param objectGraph The App Store object graph
202 * @param data The Media API Marketplace response to search for an attribute platform.
203 * @returns The most appropriate attribute platform available for the current client.
204 */
205export function bestAttributePlatformFromMarketplaceData(objectGraph, data) {
206 // 1. Iterate through the client's preferred platform ordering until we
207 // find the first one present in the response.
208 const preferredAttributePlatforms = defaultAttributePlatformOrdering(objectGraph);
209 for (const attributePlatform of preferredAttributePlatforms) {
210 const versionsAttributes = contentAttributeAsDictionary(objectGraph, data, "versionAttributes", attributePlatform);
211 if (serverData.isDefinedNonNullNonEmpty(versionsAttributes)) {
212 return attributePlatform;
213 }
214 }
215 // 2. Catch-All
216 return defaultAttributePlatform(objectGraph);
217}
218/**
219 * The default attribute platform for the current client
220 */
221export function defaultAttributePlatform(objectGraph) {
222 var _a;
223 if ((_a = objectGraph.activeIntent) === null || _a === void 0 ? void 0 : _a.attributePlatform) {
224 return objectGraph.activeIntent.attributePlatform;
225 }
226 switch (objectGraph.client.deviceType) {
227 case "phone":
228 case "pad":
229 return "ios";
230 case "tv":
231 return "appletvos";
232 case "mac":
233 return "osx";
234 case "watch":
235 return "watch";
236 case "vision":
237 return "xros";
238 default:
239 return null;
240 }
241}
242/**
243 * The preferred ordering to use given our default platform.
244 */
245function defaultAttributePlatformOrdering(objectGraph, clientIdentifierOverride) {
246 const defaultPlatform = defaultAttributePlatform(objectGraph);
247 if (defaultPlatform === null) {
248 // If the `"web"` client is active and there is not an "active intent" to
249 // inform a default platform, fall back to a hard-coded ordering
250 if (objectGraph.client.isWeb) {
251 return ["ios", "osx", "xros", "watch", "appletvos"];
252 }
253 else {
254 return [];
255 }
256 }
257 switch (defaultPlatform) {
258 case "ios":
259 if (clientIdentifierOverride === "VisionAppStore" /* ClientIdentifier.VisionAppStore */ ||
260 clientIdentifierOverride === "com.apple.visionproapp" /* ClientIdentifier.VisionCompanion */) {
261 return ["xros", "ios", "appletvos", "osx"];
262 }
263 else {
264 return ["ios", "appletvos", "osx", "xros"];
265 }
266 case "osx":
267 return ["osx", "ios", "appletvos", "xros"];
268 case "appletvos":
269 return ["appletvos", "ios", "osx", "xros"];
270 case "watch":
271 // Per Hiren Kotadia on 2019-2-26, watch platform attributes will always be under ios.
272 // We're going to promote ios to the head of the search list to speed up Media API
273 // response parsing. We'll keep watch as #2 in the list so that if this changes in
274 // the future, it should mostly just work. -km
275 return ["ios", "watch", "osx", "xros"];
276 case "xros":
277 return ["xros", "ios", "appletvos", "osx"];
278 default:
279 return [defaultPlatform];
280 }
281}
282// region Variant Attributes
283/**
284 * Retrieve the attribute for a specific platform's variant attribute as a dictionary, from custom attributes or standard attributes.
285 * @param data Data to get attribute for.
286 * @param productVariantData Variant data to use when finding item.
287 * @param attributeKey The key to fetch in platform attributes. May be converted to custom attribute key.
288 * @param attributePlatform The platform to fetch attribute for. Defaults to current platform if unspecified.
289 */
290export function customAttributeAsDictionary(objectGraph, data, productVariantData, attributeKey, attributePlatform) {
291 // Use `customAttributes.${customAttributeKey}` for platform if present
292 const customAttributeKey = mediaAttributes.attributeKeyAsCustomAttributeKey(attributeKey);
293 if (isNothing(customAttributeKey)) {
294 return null;
295 }
296 const customAttributes = contentAttributeAsDictionary(objectGraph, data, "customAttributes", attributePlatform);
297 const allowNondefaultTreatmentInNondefaultPage = mediaAttributes.attributeAllowsNonDefaultTreatmentInNonDefaultPage(customAttributeKey);
298 const customAttribute = variantAttributeForKey(objectGraph, customAttributes, productVariantData, customAttributeKey, allowNondefaultTreatmentInNondefaultPage);
299 if (serverData.isDefinedNonNullNonEmpty(customAttribute)) {
300 return serverData.asDictionary(customAttribute);
301 }
302 // Otherwise, use `${attributeKey}` for platform.
303 return contentAttributeAsDictionary(objectGraph, data, attributeKey, attributePlatform);
304}
305/**
306 * Retrieve the attribute for a specific platform's variant attribute as a dictionary, from custom attributes or standard attributes.
307 * @param data Data to get attribute for.
308 * @param productVariantData Variant data to use when finding item.
309 * @param attributeKey The key to fetch in platform attributes when custom attributes are not present.
310 * @param attributePlatform The platform to fetch attribute for. Defaults to current platform if unspecified.
311 */
312export function customAttributeAsString(objectGraph, data, productVariantData, attributeKey, attributePlatform) {
313 // Use `customAttributes.${customAttributeKey}` for platform if present
314 const customAttributeKey = mediaAttributes.attributeKeyAsCustomAttributeKey(attributeKey);
315 if (isNothing(customAttributeKey)) {
316 return null;
317 }
318 const customAttributes = contentAttributeAsDictionary(objectGraph, data, "customAttributes", attributePlatform);
319 const allowNondefaultTreatmentInNondefaultPage = mediaAttributes.attributeAllowsNonDefaultTreatmentInNonDefaultPage(customAttributeKey);
320 const customAttribute = variantAttributeForKey(objectGraph, customAttributes, productVariantData, customAttributeKey, allowNondefaultTreatmentInNondefaultPage);
321 if (serverData.isDefinedNonNullNonEmpty(customAttribute)) {
322 return serverData.asString(customAttribute);
323 }
324 // Otherwise, use `${attributeKey}` for platform.
325 return contentAttributeAsString(objectGraph, data, attributeKey);
326}
327// endregion
328//# sourceMappingURL=attributes.js.map