UNPKG

7.72 kBPlain TextView Raw
1// (C) 2007-2018 GoodData Corporation
2import get from "lodash/get";
3import find from "lodash/find";
4import omit from "lodash/omit";
5import cloneDeep from "lodash/cloneDeep";
6import { XhrModule } from "./xhr";
7import { ExecutionModule } from "./execution";
8import { IAdHocItemDescription, IStoredItemDescription, ItemDescription } from "./interfaces";
9
10const REQUEST_DEFAULTS = {
11 types: ["attribute", "metric", "fact"],
12 paging: {
13 offset: 0,
14 },
15};
16
17const LOAD_DATE_DATASET_DEFAULTS = {
18 includeUnavailableDateDataSetsCount: true,
19 includeAvailableDateAttributes: true,
20};
21
22/**
23 * Convert specific params in options to "requiredDataSets" structure. For more details look into
24 * res file https://github.com/gooddata/gdc-bear/blob/develop/resources/specification/internal/catalog.res
25 *
26 * @param options Supported keys in options are:
27 * <ul>
28 * <li>dataSetIdentifier - in value is string identifier of dataSet - this leads to CUSTOM type
29 * <li>returnAllDateDataSets - true value means to return ALL values without dataSet differentiation
30 * <li>returnAllRelatedDateDataSets - only related date dataSets are loaded across all dataSets
31 * <li>by default we get PRODUCTION dataSets
32 * </ul>
33 * @returns {Object} "requiredDataSets" object hash.
34 */
35const getRequiredDataSets = (options: any) => {
36 if (get(options, "returnAllRelatedDateDataSets")) {
37 return {};
38 }
39
40 if (get(options, "returnAllDateDataSets")) {
41 return { requiredDataSets: { type: "ALL" } };
42 }
43
44 if (get(options, "dataSetIdentifier")) {
45 return {
46 requiredDataSets: {
47 type: "CUSTOM",
48 customIdentifiers: [get(options, "dataSetIdentifier")],
49 },
50 };
51 }
52
53 return { requiredDataSets: { type: "PRODUCTION" } };
54};
55
56interface IColumnsAndDefinitions {
57 columns: string[];
58 definitions: any[];
59}
60
61const buildItemDescriptionObjects = ({ columns, definitions }: IColumnsAndDefinitions): ItemDescription[] => {
62 if (!columns) {
63 return [];
64 }
65
66 return columns.map((column: string) => {
67 const definition = find(
68 definitions,
69 ({ metricDefinition }) => metricDefinition.identifier === column,
70 );
71 const maql = get(definition, "metricDefinition.expression");
72 if (maql) {
73 return { expression: maql };
74 }
75 return { uri: column };
76 });
77};
78
79const isStoredItemDescription = (
80 itemDescription: ItemDescription,
81): itemDescription is IStoredItemDescription => {
82 return !!(itemDescription as IStoredItemDescription).uri;
83};
84
85const isAdHocItemDescription = (
86 itemDescription: ItemDescription,
87): itemDescription is IAdHocItemDescription => {
88 return !!(itemDescription as IAdHocItemDescription).expression;
89};
90
91const unwrapItemDescriptionObject = (itemDescription: ItemDescription): string => {
92 if (isStoredItemDescription(itemDescription)) {
93 return itemDescription.uri;
94 }
95 if (isAdHocItemDescription(itemDescription)) {
96 return itemDescription.expression;
97 }
98 throw new Error("Item description can only have expression or uri");
99};
100
101export class CatalogueModule {
102 constructor(private xhr: XhrModule, private execution: ExecutionModule) {}
103
104 public loadItems(projectId: string, options = {}) {
105 const request = omit(
106 {
107 ...REQUEST_DEFAULTS,
108 ...options,
109 ...getRequiredDataSets(options),
110 },
111 ["dataSetIdentifier", "returnAllDateDataSets", "attributesMap"],
112 );
113
114 const mdObj = get(cloneDeep(options), "bucketItems");
115 const attributesMap = get(options, "attributesMap");
116 const hasBuckets = get(mdObj, "buckets") !== undefined;
117 if (hasBuckets) {
118 return this.loadItemDescriptions(projectId, mdObj, attributesMap).then((bucketItems: any) =>
119 this.loadCatalog(projectId, { ...request, bucketItems }),
120 );
121 }
122
123 return this.loadCatalog(projectId, request);
124 }
125
126 public async loadDateDataSets(projectId: string, options: any) {
127 const mdObj = cloneDeep(options).bucketItems;
128 const bucketItems = mdObj
129 ? await this.loadItemDescriptions(projectId, mdObj, get(options, "attributesMap"), true)
130 : undefined;
131
132 const omittedOptions = [
133 "filter",
134 "types",
135 "paging",
136 "dataSetIdentifier",
137 "returnAllDateDataSets",
138 "returnAllRelatedDateDataSets",
139 "attributesMap",
140 ];
141 // includeObjectsWithTags has higher priority than excludeObjectsWithTags,
142 // so when present omit excludeObjectsWithTags
143 if (options.includeObjectsWithTags) {
144 omittedOptions.push("excludeObjectsWithTags");
145 }
146
147 const request = omit(
148 {
149 ...LOAD_DATE_DATASET_DEFAULTS,
150 ...REQUEST_DEFAULTS,
151 ...options,
152 ...getRequiredDataSets(options),
153 bucketItems,
154 },
155 omittedOptions,
156 );
157
158 return this.requestDateDataSets(projectId, request);
159 }
160
161 /**
162 * Loads item description objects and returns them
163 *
164 * @internal
165 * @private
166 *
167 * @param projectId {string}
168 * @param mdObj metadata object containing buckets, visualization class, properties etc.
169 * @param attributesMap contains map of attributes where the keys are the attributes display forms URIs
170 * @param removeDateItems {boolean} skip date items
171 * @return ItemDescription which is either `{ uri: string }` or `{ expression: string }`
172 */
173 public async loadItemDescriptionObjects(
174 projectId: string,
175 mdObj: any,
176 attributesMap: any,
177 removeDateItems = false,
178 ): Promise<ItemDescription[]> {
179 const definitionsAndColumns = await this.execution.mdToExecutionDefinitionsAndColumns(
180 projectId,
181 mdObj,
182 { attributesMap, removeDateItems },
183 );
184
185 return buildItemDescriptionObjects(definitionsAndColumns);
186 }
187
188 /**
189 * ItemDescription is either URI or MAQL expression
190 * https://github.com/gooddata/gdc-bear/blob/185.4/resources/specification/md/obj.res#L284
191 *
192 * @param projectId {string}
193 * @param mdObj metadata object containing buckets, visualization class, properties etc.
194 * @param attributesMap contains map of attributes where the keys are the attributes display forms URIs
195 * @param removeDateItems {boolean} skip date items
196 * @deprecated
197 */
198 public async loadItemDescriptions(
199 projectId: string,
200 mdObj: any,
201 attributesMap: any,
202 removeDateItems = false,
203 ): Promise<string[]> {
204 const itemDescriptions = await this.loadItemDescriptionObjects(
205 projectId,
206 mdObj,
207 attributesMap,
208 removeDateItems,
209 );
210
211 return itemDescriptions.map(unwrapItemDescriptionObject);
212 }
213
214 private requestDateDataSets(projectId: string, dateDataSetsRequest: any) {
215 const uri = `/gdc/internal/projects/${projectId}/loadDateDataSets`;
216
217 return this.xhr
218 .post(uri, { data: { dateDataSetsRequest } })
219 .then(r => r.getData())
220 .then(data => data.dateDataSetsResponse);
221 }
222
223 private loadCatalog(projectId: string, catalogRequest: any) {
224 const uri = `/gdc/internal/projects/${projectId}/loadCatalog`;
225
226 return this.xhr
227 .post(uri, { data: { catalogRequest } })
228 .then(r => r.getData())
229 .then(data => data.catalogResponse);
230 }
231}