gooddata-js v13.5.0

File: src/DataLayer/converters/toAfmResultSpec.ts

                        // (C) 2007-2020 GoodData Corporation
                        import compact from "lodash/compact";
                        import flatMap from "lodash/flatMap";
                        import get from "lodash/get";
                        import { AFM, VisualizationObject } from "@gooddata/typings";
                        import { convertVisualizationObjectExtendedFilter } from "./FilterConverter";
                        import MeasureConverter from "./MeasureConverter";
                        import { LOCATION, TOOLTIP_TEXT } from "../../DataLayer/constants/buckets";
                        import { IVisualizationProperties } from "./model/VisualizationObject";
                        
                        function convertAttribute(
                            attribute: VisualizationObject.IVisualizationAttribute,
                            idx: number,
                        ): AFM.IAttribute {
                            const alias = attribute.visualizationAttribute.alias;
                            const aliasProp = alias ? { alias } : {};
                            return {
                                displayForm: attribute.visualizationAttribute.displayForm,
                                localIdentifier: attribute.visualizationAttribute.localIdentifier || `a${idx + 1}`,
                                ...aliasProp,
                            };
                        }
                        
                        function convertAFM(visualizationObject: VisualizationObject.IVisualizationObjectContent): AFM.IAfm {
                            const textualAttributes: AFM.IAttribute[] = getAttributes(visualizationObject.buckets).map(
                                convertAttribute,
                            );
                            const geoAttribute = getGeoAttributeForTooltip(visualizationObject);
                            const attributes = geoAttribute ? [...textualAttributes, geoAttribute] : textualAttributes;
                        
                            const attrProp = attributes.length ? { attributes } : {};
                        
                            const measures: AFM.IMeasure[] = getMeasures(visualizationObject.buckets).map(
                                MeasureConverter.convertMeasure,
                            );
                            const measuresProp = measures.length ? { measures } : {};
                        
                            const filters: AFM.CompatibilityFilter[] = visualizationObject.filters
                                ? compact(visualizationObject.filters.map(convertVisualizationObjectExtendedFilter))
                                : [];
                            const filtersProp = filters.length ? { filters } : {};
                        
                            const nativeTotals = convertNativeTotals(visualizationObject);
                            const nativeTotalsProp = nativeTotals.length ? { nativeTotals } : {};
                        
                            return {
                                ...measuresProp,
                                ...attrProp,
                                ...filtersProp,
                                ...nativeTotalsProp,
                            };
                        }
                        
                        function getMeasures(buckets: VisualizationObject.IBucket[]): VisualizationObject.IMeasure[] {
                            return buckets.reduce((result: VisualizationObject.IMeasure[], bucket: VisualizationObject.IBucket) => {
                                const measureItems: VisualizationObject.IMeasure[] = bucket.items.filter(
                                    VisualizationObject.isMeasure,
                                );
                        
                                return result.concat(measureItems);
                            }, []);
                        }
                        
                        function getNativeTotalAttributeIdentifiers(
                            bucket: VisualizationObject.IBucket,
                            total: VisualizationObject.IVisualizationTotal,
                        ): string[] {
                            const attributes = bucket.items.filter(VisualizationObject.isAttribute);
                        
                            const totalAttributeIndex = attributes.findIndex(
                                attribute => attribute.visualizationAttribute.localIdentifier === total.attributeIdentifier,
                            );
                        
                            return attributes
                                .slice(0, totalAttributeIndex)
                                .map(attribute => attribute.visualizationAttribute.localIdentifier);
                        }
                        
                        function convertNativeTotals(
                            visObj: VisualizationObject.IVisualizationObjectContent,
                        ): AFM.INativeTotalItem[] {
                            const nativeTotalsPerBucket = visObj.buckets.map(bucket => {
                                const totals = bucket.totals || [];
                                const nativeTotals = totals.filter(total => total.type === "nat");
                        
                                return nativeTotals.map(total => ({
                                    measureIdentifier: total.measureIdentifier,
                                    attributeIdentifiers: getNativeTotalAttributeIdentifiers(bucket, total),
                                }));
                            });
                        
                            return flatMap(nativeTotalsPerBucket);
                        }
                        
                        function getAttributes(
                            buckets: VisualizationObject.IBucket[],
                        ): VisualizationObject.IVisualizationAttribute[] {
                            return buckets.reduce(
                                (result: VisualizationObject.IVisualizationAttribute[], bucket: VisualizationObject.IBucket) => {
                                    const items: VisualizationObject.IVisualizationAttribute[] = bucket.items.filter(
                                        VisualizationObject.isAttribute,
                                    );
                        
                                    return result.concat(items);
                                },
                                [],
                            );
                        }
                        
                        function parsePropertyItem(item: string): IVisualizationProperties {
                            try {
                                return JSON.parse(item);
                            } catch (e) {
                                return {};
                            }
                        }
                        
                        function buildTooltipBucketItem(tooltipText: string, alias: string): AFM.IAttribute {
                            const tooltipAlias = alias ? { alias } : {};
                            return {
                                localIdentifier: TOOLTIP_TEXT,
                                displayForm: {
                                    uri: tooltipText,
                                },
                                ...tooltipAlias,
                            };
                        }
                        
                        function getGeoAttributeForTooltip(
                            visualizationObject: VisualizationObject.IVisualizationObjectContent,
                        ): AFM.IAttribute | null {
                            const properties: IVisualizationProperties = parsePropertyItem(visualizationObject.properties || "");
                            const tooltipText: string = get(properties, "controls.tooltipText", "");
                            if (tooltipText) {
                                // copy alias of Geo attribute to alias of tooltipText attribute
                                const locationAlias: string = visualizationObject.buckets.reduce(
                                    (locationAlias: string, bucket: VisualizationObject.IBucket): string => {
                                        if (!locationAlias && bucket.localIdentifier === LOCATION) {
                                            return get(bucket, "items[0].visualizationAttribute.alias");
                                        }
                                        return locationAlias;
                                    },
                                    "",
                                );
                                return buildTooltipBucketItem(tooltipText, locationAlias);
                            }
                        
                            return null;
                        }
                        
                        function convertSorting(visObj: VisualizationObject.IVisualizationObjectContent): AFM.SortItem[] {
                            if (visObj.properties) {
                                let properties = {};
                                try {
                                    properties = JSON.parse(visObj.properties);
                                } catch {
                                    // tslint:disable-next-line:no-console
                                    console.error("Properties contains invalid JSON string.");
                                }
                        
                                const sorts: AFM.SortItem[] = get(properties, "sortItems", []);
                                return sorts ? sorts : [];
                            }
                        
                            return [];
                        }
                        
                        function convertResultSpec(visObj: VisualizationObject.IVisualizationObjectContent): AFM.IResultSpec {
                            const sorts = convertSorting(visObj);
                            // Workaround because we can handle only 1 sort item for now
                            const sortsProp = sorts.length ? { sorts: sorts.slice(0, 1) } : {};
                        
                            return {
                                ...sortsProp,
                            };
                        }
                        
                        export interface IConvertedAFM {
                            afm: AFM.IAfm;
                            resultSpec: AFM.IResultSpec;
                        }
                        
                        /**
                         * Converts visualizationObject to afm and resultSpec
                         *
                         * @method toAfmResultSpec
                         * @param {VisualizationObject.IVisualizationObjectContent} visObj
                         * @returns {IConvertedAFM}
                         */
                        export function toAfmResultSpec(visObj: VisualizationObject.IVisualizationObjectContent): IConvertedAFM {
                            const afm = convertAFM(visObj);
                            return {
                                afm,
                                resultSpec: convertResultSpec(visObj),
                            };
                        }