UNPKG

@gooddata/api-client-bear

Version:
918 lines • 37.4 kB
// (C) 2007-2022 GoodData Corporation import isPlainObject from "lodash/isPlainObject.js"; import chunk from "lodash/chunk.js"; import flatten from "lodash/flatten.js"; import pick from "lodash/pick.js"; import pickBy from "lodash/pickBy.js"; import { isWrappedAttribute, } from "@gooddata/api-model-bear"; import { getQueryEntries, handlePolling, queryString } from "./util.js"; import { ApiResponseError } from "./xhr.js"; import { stringify } from "./utils/queryString.js"; function getResponseData(r) { if (!r.response.ok) { throw new ApiResponseError(r.response.statusText, r.response, r.getData()); } return r.getData(); } /** * Functions for working with metadata objects * */ export class MetadataModule { xhr; constructor(xhr) { this.xhr = xhr; } /** * Get default display form value of provided attribute element uri * @param attributeElementUri - string */ async getAttributeElementDefaultDisplayFormValue(attributeElementUri) { const uriChunks = attributeElementUri.match(/(.+)\/elements\?id=(.*)/); if (!uriChunks) { throw new Error("Provide valid attribute element uri"); } const attributeUri = uriChunks[1]; const elementId = uriChunks[2]; const defaultDisplayForm = await this.getAttributeDefaultDisplayForm(attributeUri); if (!defaultDisplayForm) { throw new Error("Attribute of the provided element has no default display form!"); } const defaultDisplayFormUri = defaultDisplayForm.meta.uri; const defaultDisplayFormElementValue = await this.xhr.getParsed(`${defaultDisplayFormUri}/elements?id=${elementId}`); return defaultDisplayFormElementValue.attributeElements.elements[0]; } /** * Get default display form of provided attribute uri * @param attributeUri - string */ async getAttributeDefaultDisplayForm(attributeUri) { const object = await this.xhr.getParsed(attributeUri); if (!isWrappedAttribute(object)) { throw new Error("Provided uri is not attribute uri!"); } return (object.attribute.content.displayForms.find((displayForm) => displayForm.content.default === 1) || object.attribute.content.displayForms[0]); } /** * Get metadata object by provided identifier * @param projectId - string * @param identifier - string */ async getObjectByIdentifier(projectId, identifier) { const uri = await this.getObjectUri(projectId, identifier); return this.xhr.getParsed(uri); } /** * Get metadata objects by provided identifiers * @param projectId - string * @param identifiers - string[] */ async getObjectsByIdentifiers(projectId, identifiers) { const uriIdentifierPairs = await this.getUrisFromIdentifiers(projectId, identifiers); const uris = uriIdentifierPairs.map((pair) => pair.uri); return this.getObjects(projectId, uris); } /** * Load all objects with given uris * (use bulk loading instead of getting objects one by one) * * @param projectId - id of the project * @param objectUris - array of uris for objects to be loaded * @returns array of loaded elements */ getObjects(projectId, objectUris) { const LIMIT = 50; const uri = `/gdc/md/${projectId}/objects/get`; const objectsUrisChunks = chunk(objectUris, LIMIT); const promises = objectsUrisChunks.map((objectUrisChunk) => { const body = { get: { items: objectUrisChunk, }, }; return this.xhr .post(uri, { body }) .then((r) => { if (!r.response.ok) { throw new ApiResponseError(r.response.statusText, r.response, r.responseBody); } return r.getData(); }) .then((result) => result.objects.items.map((item) => { if (item.visualizationObject) { return { visualizationObject: item.visualizationObject, }; } return item; })); }); return Promise.all(promises).then(flatten); } /** * Loads all objects by query (fetches all pages, one by one) * * @param projectId - id of the project * @param options - (see https://developer.gooddata.com/api endpoint: `/gdc/md/{project_id}/objects/query`) * - category - for example 'dataSets' or 'projectDashboard' * - mode - 'enriched' or 'raw' * - author - the URI of the author of the metadata objects * - limit - default is 50 (also maximum) * - deprecated - show also deprecated objects * @returns array of returned objects */ getObjectsByQuery(projectId, options) { const getOnePage = (uri, items = []) => { return this.xhr .get(uri) .then((r) => r.getData()) .then(({ objects }) => { items.push(...objects.items); const nextUri = objects.paging.next; return nextUri ? getOnePage(nextUri, items) : items; }); }; const deprecated = options.deprecated ? { deprecated: 1 } : {}; const uri = `/gdc/md/${projectId}/objects/query`; const query = pick({ limit: 50, ...options, ...deprecated }, [ "category", "mode", "author", "limit", "deprecated", ]); return getOnePage(uri + queryString(query)); } /** * Loads all objects by query with paging * * @param projectId - id of the project * @param options - (see https://developer.gooddata.com/api endpoint: `/gdc/md/{project_id}/objects/query`) * - category - for example 'dataSets' or 'projectDashboard' * - mode - 'enriched' or 'raw' * - author - the URI of the author of the metadata objects * - limit - default is 50 (also maximum) * - deprecated - show also deprecated objects * - orderBy - order the results by id, title or the last updated (newest first) * - getTotalCount - include total count of items in the paging object * @returns array of returned objects */ getObjectsByQueryWithPaging(projectId, options) { const getTotalCount = options?.getTotalCount ? 1 : 0; const uri = `/gdc/md/${projectId}/objects/query?${stringify({ ...options, getTotalCount })}`; return this.xhr .get(uri) .then((r) => r.getData()) .then(({ objects }) => objects); } /** * Get MD objects from using2 resource. Include only objects of given types * and take care about fetching only nearest objects if requested. * * @param projectId - id of the project * @param uri - uri of the object for which dependencies are to be found * @param options - objects with options: * - types - array of strings with object types to be included * - nearest - whether to include only nearest dependencies * @returns promise promise once resolved returns an array of * entries returned by using2 resource */ getObjectUsing(projectId, uri, options = {}) { const { types = [], nearest = false } = options; const resourceUri = `/gdc/md/${projectId}/using2`; const body = { inUse: { uri, types, nearest: nearest ? 1 : 0, }, }; return this.xhr .post(resourceUri, { body }) .then(getResponseData) .then((result) => result.entries); } /** * Get MD objects from using2 resource. Include only objects of given types * and take care about fetching only nearest objects if requested. * * @param projectId - id of the project * @param uris - uris of objects for which dependencies are to be found * @param options - objects with options: * - types - array of strings with object types to be included * - nearest - whether to include only nearest dependencies * @returns promise promise once resolved returns an array of * entries returned by using2 resource */ getObjectUsingMany(projectId, uris, options = {}) { const { types = [], nearest = false } = options; const resourceUri = `/gdc/md/${projectId}/using2`; const body = { inUseMany: { uris, types, nearest: nearest ? 1 : 0, }, }; return this.xhr .post(resourceUri, { body }) .then(getResponseData) .then((result) => result.useMany); } /** * Get MD objects from usedby2 resource. Include only objects of given types * and take care about fetching only nearest objects if requested. * * @param projectId - id of the project * @param uri - uri of the object for which dependencies are to be found * @param options - objects with options: * - types - array of strings with object types to be included * - nearest - whether to include only nearest dependencies (default is false) * @returns promise promise once resolved returns an array of * entries returned by usedby2 resource */ getObjectUsedBy(projectId, uri, options) { const { nearest = false, types = [] } = options; const body = { inUse: { nearest: nearest ? 1 : 0, uri, types, }, }; return this.xhr .postParsed(`/gdc/md/${projectId}/usedby2`, { body }) .then((result) => result.entries); } /** * Get MD objects from usedby2 resource. Include only objects of given types * and take care about fetching only nearest objects if requested. * * @param projectId - id of the project * @param uris - uris of objects for which dependencies are to be found * @param options - objects with options: * - types - array of strings with object types to be included * - nearest - whether to include only nearest dependencies (default is false) * @returns promise promise once resolved returns an array of * entries returned by usedby2 resource */ getObjectsUsedByMany(projectId, uris, options) { const uri = `/gdc/md/${projectId}/usedby2`; const { nearest = false, types = [] } = options; const body = { inUseMany: { nearest: nearest ? 1 : 0, uris, types, }, }; return this.xhr .postParsed(uri, { body }) .then((result) => result.useMany); } /** * Returns all visualizationObjects metadata in a project specified by projectId param * * @param projectId - Project identifier * @returns An array of visualization objects metadata */ getVisualizations(projectId) { return this.xhr .get(`/gdc/md/${projectId}/query/visualizationobjects`) .then((apiResponse) => apiResponse.response.ok ? apiResponse.getData() : apiResponse.response) .then(getQueryEntries); } /** * Returns all attributes in a project specified by projectId param * * @param projectId - Project identifier * @returns An array of attribute objects */ getAttributes(projectId) { return this.xhr .get(`/gdc/md/${projectId}/query/attributes`) .then((apiResponse) => apiResponse.response.ok ? apiResponse.getData() : apiResponse.response) .then(getQueryEntries); } /** * Returns all dimensions in a project specified by projectId param * * @param projectId - Project identifier * @returns An array of dimension objects * @see getFolders */ getDimensions(projectId) { return this.xhr .get(`/gdc/md/${projectId}/query/dimensions`) .then((apiResponse) => apiResponse.response.ok ? apiResponse.getData() : apiResponse.response) .then(getQueryEntries); } /** * Returns project folders. Folders can be of specific types and you can specify * the type you need by passing and optional `type` parameter * * @param projectId - Project identifier * @param type - Optional, possible values are `metric`, `fact`, `attribute` * @returns An array of dimension objects */ getFolders(projectId, type) { // TODO enum? const getFolderEntries = (pId, t) => { const typeURL = t ? `?type=${t}` : ""; return this.xhr .get(`/gdc/md/${pId}/query/folders${typeURL}`) .then((r) => r.getData()) .then(getQueryEntries); }; switch (type) { case "fact": case "metric": return getFolderEntries(projectId, type); case "attribute": return this.getDimensions(projectId); default: return Promise.all([ getFolderEntries(projectId, "fact"), getFolderEntries(projectId, "metric"), this.getDimensions(projectId), ]).then(([fact, metric, attribute]) => { return { fact, metric, attribute }; }); } } /** * Returns all facts in a project specified by the given projectId * * @param projectId - Project identifier * @returns An array of fact objects */ getFacts(projectId) { return this.xhr .get(`/gdc/md/${projectId}/query/facts`) .then((apiResponse) => apiResponse.response.ok ? apiResponse.getData() : apiResponse.response) .then(getQueryEntries); } /** * Returns all metrics in a project specified by the given projectId * * @param projectId - Project identifier * @returns An array of metric objects */ getMetrics(projectId) { return this.xhr .get(`/gdc/md/${projectId}/query/metrics`) .then((apiResponse) => apiResponse.response.ok ? apiResponse.getData() : apiResponse.response) .then(getQueryEntries); } /** * Returns all project dashboards (pixel perfect dashboards) in a project specified by the given projectId * * @param projectId - Project identifier * @returns An array of project dashboard objects */ getProjectDashboards(projectId) { return this.xhr .getParsed(`/gdc/md/${projectId}/query/projectdashboards`) .then((dashboardsQuery) => { const dashboardLinks = dashboardsQuery.query.entries.map((dashboard) => dashboard.link); return this.getObjects(projectId, dashboardLinks); }); } /** * Returns all analytical dashboards (kpi dashboards) in a project specified by the given projectId * * @param projectId - Project identifier * @param fetchAllListedDashboards - Specify if also all the listed * dashboards should be loaded. Note that these include not just shared dashboards and dashboards * that were not shared with the user but are accessible via link, but also dashboards that cannot * be accessed because there were not shared are under strict control access (only its listed record * is accessible, not the whole metadata object). * * @returns An array of links to analytical dashboard objects */ getAnalyticalDashboards(projectId, fetchAllListedDashboards) { return this.xhr .getParsed(`/gdc/md/${projectId}/query/analyticaldashboard?showAll=${fetchAllListedDashboards ? 1 : 0}`) .then((dashboardsQuery) => { return dashboardsQuery.query.entries; }); } /** * Returns all dashboard plugins in a project specified by the given projectId * * @param projectId - Project identifier * @returns An array of links to dashboard plugin objects */ getDashboardPlugins(projectId) { return this.xhr .getParsed(`/gdc/md/${projectId}/query/dashboardplugins`) .then((dashboardsQuery) => { return dashboardsQuery.query.entries; }); } /** * Returns all metrics that are reachable (with respect to ldm of the project * specified by the given projectId) for given attributes * * @param projectId - Project identifier * @param attrs - An array of attribute uris for which we want to get * available metrics * @returns An array of reachable metrics for the given attrs * @see getAvailableAttributes * @see getAvailableFacts */ getAvailableMetrics(projectId, attrs = []) { return this.xhr .post(`/gdc/md/${projectId}/availablemetrics`, { body: attrs }) .then((apiResponse) => apiResponse.response.ok ? apiResponse.getData() : apiResponse.response) .then((data) => data.entries); } /** * Returns all attributes that are reachable (with respect to ldm of the project * specified by the given projectId) for given metrics (also called as drillCrossPath) * * @param projectId - Project identifier * @param metrics - An array of metric uris for which we want to get * available attributes * @returns An array of reachable attributes for the given metrics * @see getAvailableMetrics * @see getAvailableFacts */ getAvailableAttributes(projectId, metrics = []) { return this.xhr .post(`/gdc/md/${projectId}/drillcrosspaths`, { body: metrics }) .then((apiResponse) => (apiResponse.response.ok ? apiResponse.getData() : apiResponse.response)) .then((r) => r.drillcrosspath.links); } /** * Returns all attributes that are reachable (with respect to ldm of the project * specified by the given projectId) for given metrics (also called as drillCrossPath) * * @param projectId - Project identifier * @param items - An array of metric or attribute uris for which we want to get * available facts * @returns An array of reachable facts for the given items * @see getAvailableAttributes * @see getAvailableMetrics */ getAvailableFacts(projectId, items = []) { return this.xhr .post(`/gdc/md/${projectId}/availablefacts`, { body: items }) .then((r) => (r.response.ok ? r.getData() : r.response)) .then((r) => r.entries); } /** * Get details of a metadata object specified by its uri * * @param uri - uri of the metadata object for which details are to be retrieved * @returns object details */ getObjectDetails(uri) { return this.xhr.get(uri).then((r) => r.getData()); } /** * Get folders with items. * Returns array of folders, each having a title and items property which is an array of * corresponding items. Each item is either a metric or attribute, keeping its original * verbose structure. * * @param type - type of folders to return * @returns Array of folder object, each containing title and * corresponding items. */ getFoldersWithItems(projectId, type) { // fetch all folders of given type and process them return this.getFolders(projectId, type).then((folders) => { // Helper public to get details for each metric in the given // array of links to the metadata objects representing the metrics. // @returns the array of promises const getMetricItemsDetails = (array) => { return Promise.all(array.map(this.getObjectDetails)).then((metricArgs) => { return metricArgs.map((item) => item.metric); }); }; // helper mapBy function function mapBy(array, key) { return array.map((item) => { return item[key]; }); } // helper for sorting folder tree structure // sadly @returns void (sorting == mutating array in js) const sortFolderTree = (structure) => { structure.forEach((folder) => { folder.items.sort((a, b) => { if (a.meta.title < b.meta.title) { return -1; } else if (a.meta.title > b.meta.title) { return 1; } return 0; }); }); structure.sort((a, b) => { if (a.title < b.title) { return -1; } else if (a.title > b.title) { return 1; } return 0; }); }; const foldersLinks = mapBy(folders, "link"); const foldersTitles = mapBy(folders, "title"); // fetch details for each folder return Promise.all(foldersLinks.map(this.getObjectDetails)).then((folderDetails) => { // if attribute, just parse everything from what we've received // and resolve. For metrics, lookup again each metric to get its // identifier. If passing unsupported type, reject immediately. if (type === "attribute") { // get all attributes, subtract what we have and add rest in unsorted folder return this.getAttributes(projectId).then((attributes) => { // get uris of attributes which are in some dimension folders const attributesInFolders = []; folderDetails.forEach((fd) => { fd.dimension.content.attributes.forEach((attr) => { attributesInFolders.push(attr.meta.uri); }); }); // unsortedUris now contains uris of all attributes which aren't in a folder const unsortedUris = attributes .filter((item) => attributesInFolders.indexOf(item.link) === -1) .map((item) => item.link); // now get details of attributes in no folders return Promise.all(unsortedUris.map(this.getObjectDetails)).then((unsortedAttributeArgs) => { // TODO add map to r.json // get unsorted attribute objects const unsortedAttributes = unsortedAttributeArgs.map((attr) => attr.attribute); // create structure of folders with attributes const structure = folderDetails.map((folderDetail) => { return { title: folderDetail.dimension.meta.title, items: folderDetail.dimension.content.attributes, }; }); // and append "Unsorted" folder with attributes to the structure structure.push({ title: "Unsorted", items: unsortedAttributes, }); sortFolderTree(structure); return structure; }); }); } else if (type === "metric") { const entriesLinks = folderDetails.map((entry) => mapBy(entry.folder.content.entries, "link")); // get all metrics, subtract what we have and add rest in unsorted folder return this.getMetrics(projectId).then((metrics) => { // get uris of metrics which are in some dimension folders const metricsInFolders = []; folderDetails.forEach((fd) => { fd.folder.content.entries.forEach((metric) => { metricsInFolders.push(metric.link); }); }); // unsortedUris now contains uris of all metrics which aren't in a folder const unsortedUris = metrics .filter((item) => metricsInFolders.indexOf(item.link) === -1) .map((item) => item.link); // sadly order of parameters of concat matters! (we want unsorted last) entriesLinks.push(unsortedUris); // now get details of all metrics return Promise.all(entriesLinks.map((linkArray) => getMetricItemsDetails(linkArray))).then((tree) => { // TODO add map to r.json // all promises resolved, i.e. details for each metric are available const structure = tree.map((treeItems, idx) => { // if idx is not in folders list than metric is in "Unsorted" folder return { title: foldersTitles[idx] || "Unsorted", items: treeItems, }; }); sortFolderTree(structure); return structure; }); }); } return Promise.reject(null); }); }); } /** * Get identifier of a metadata object identified by its uri * * @param uri - uri of the metadata object for which the identifier is to be retrieved * @returns object identifier */ getObjectIdentifier(uri) { function idFinder(obj) { // TODO if (obj.attribute) { return obj.attribute.content.displayForms[0].meta.identifier; } else if (obj.dimension) { return obj.dimension.content.attributes.content.displayForms[0].meta.identifier; } else if (obj.metric) { return obj.metric.meta.identifier; } else if (obj.dataSet) { return obj.dataSet.meta.identifier; } throw Error("Unknown object!"); } if (!isPlainObject(uri)) { return this.getObjectDetails(uri).then((data) => idFinder(data)); } return Promise.resolve(idFinder(uri)); } /** * Get uri of an metadata object, specified by its identifier and project id it belongs to * * @param projectId - id of the project * @param identifier - identifier of the metadata object * @returns uri of the metadata object */ getObjectUri(projectId, identifier) { return this.xhr .post(`/gdc/md/${projectId}/identifiers`, { body: { identifierToUri: [identifier], }, }) .then((r) => { const data = r.getData(); const found = data.identifiers.find((pair) => pair.identifier === identifier); if (found) { return found.uri; } // WARNING: code in sdk-backend-bear is searching for 'not found' within the message // in order to determine the NotFound status. Cannot use the response status because // the POST request returns 200 throw new ApiResponseError(`Object with identifier ${identifier} not found in project ${projectId}`, r.response, r.responseBody); }); } /** * Get uris specified by identifiers * * @param projectId - id of the project * @param identifiers - identifiers of the metadata objects * @returns array of identifier + uri pairs */ getUrisFromIdentifiers(projectId, identifiers) { if (!identifiers.length) { return Promise.resolve([]); } return this.xhr .post(`/gdc/md/${projectId}/identifiers`, { body: { identifierToUri: identifiers, }, }) .then((r) => r.getData()) .then((data) => { return data.identifiers; }); } /** * Get identifiers specified by uris * * @param projectId - id of the project * @param uris - of the metadata objects * @returns array of identifier + uri pairs */ getIdentifiersFromUris(projectId, uris) { return this.xhr .post(`/gdc/md/${projectId}/identifiers`, { body: { uriToIdentifier: uris, }, }) .then((r) => r.getData()) .then((data) => { return data.identifiers; }); } /** * Get attribute elements with their labels and uris. * * @param projectId - id of the project * @param labelUri - uri of the label (display form) * @param patterns - elements labels/titles (for EXACT mode), or patterns (for WILD mode) * @param mode - match mode, currently only EXACT supported * @returns array of elementLabelUri objects */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types translateElementLabelsToUris(projectId, labelUri, patterns, mode = "EXACT") { return this.xhr .post(`/gdc/md/${projectId}/labels`, { body: { elementLabelToUri: [ { labelUri, mode, patterns, }, ], }, }) .then((r) => (r.response.ok ? r.getData()?.elementLabelUri : r.response)); } /** * Get valid elements of an attribute, specified by its identifier and project id it belongs to * * @param projectId - id of the project * @param id - display form id of the metadata object * @param options - objects with options * @returns ValidElements response */ getValidElements(projectId, id, options = {}) { const query = pickBy(pick(options, ["limit", "offset", "order", "filter", "prompt"]), (val) => val !== undefined); const queryParams = queryString(query); const pickedOptions = pick(options, [ "uris", "complement", "includeTotalCountWithoutFilters", "restrictiveDefinition", ]); const { afm } = options; const getRequestBodyWithReportDefinition = () => this.xhr .post(`/gdc/app/projects/${projectId}/executeAfm/debug`, { body: { execution: { afm, }, }, }) .then((response) => response.getData()) .then((reportDefinitionResult) => ({ ...pickedOptions, restrictiveDefinitionContent: reportDefinitionResult.reportDefinitionWithInlinedMetrics.content, })); const getOptions = afm ? getRequestBodyWithReportDefinition : () => Promise.resolve(pickedOptions); return getOptions().then((requestBody) => this.xhr.postParsed(`/gdc/md/${projectId}/obj/${id}/validElements${queryParams}`.replace(/\?$/, ""), { body: { validElementsRequest: requestBody, }, })); } /** * Get visualization by Uri and process data * * @param uri - visualization URI */ getVisualization(uri) { return this.getObjectDetails(uri).then((visualizationObject) => { const mdObject = visualizationObject.visualizationObject; return { visualizationObject: mdObject, }; }); } /** * Save visualization * * @param projectId - id of the project to save the visualization to * @param visualization - the visualization to save */ saveVisualization(projectId, visualization) { return this.createObject(projectId, { visualizationObject: visualization.visualizationObject }); } /** * Update visualization * * @param projectId - id of the project to update the visualization in * @param visualization - the visualization to update */ updateVisualization(projectId, visualizationUri, visualization) { const objectId = visualizationUri.split("/").slice(-1)[0]; return this.updateObject(projectId, objectId, { visualizationObject: visualization.visualizationObject, }); } /** * Delete visualization * * @param visualizationUri - URI of the visualization to delete */ deleteVisualization(visualizationUri) { return this.deleteObject(visualizationUri); } /** * Delete object * * @experimental * @param uri - of the object to be deleted */ deleteObject(uri) { return this.xhr.del(uri); } /** * Bulk delete objects */ async bulkDeleteObjects(projectId, uris, mode = "cascade") { const uri = `/gdc/md/${projectId}/objects/delete`; const data = { delete: { items: uris, mode, }, }; await this.xhr.post(uri, { data }); } /** * Create object * * @experimental * @param projectId - id of the project to create the object in * @param obj - object definition */ createObject(projectId, obj) { return this.xhr.postParsed(`/gdc/md/${projectId}/obj?createAndGet=true`, { body: obj, }); } /** * Update object * * @experimental * @param projectId - id of the project to update the object in * @param objectId - objectId of the object to update * @param obj - object definition */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types updateObject(projectId, objectId, obj) { return this.xhr .put(`/gdc/md/${projectId}/obj/${objectId}`, { body: obj, }) .then((r) => r.getData()); } /** * Converts the visualization object to legacy report. * @param projectId - id of the project to perform the conversion in * @param mdObject - visualization object to convert * @returns uri to the converted report */ openVisualizationAsReport(projectId, mdObject) { return this.xhr .post(`/gdc/internal/projects/${projectId}/convertVisualizationObject`, { body: mdObject, }) .then((res) => res.getData()) .then(({ uri }) => uri); } /** * LDM manage * * @experimental * @param projectId - id of the project to use * @param maql - MAQL to manage * @param options - for polling (maxAttempts, pollStep) */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types ldmManage(projectId, maql, options = {}) { return this.xhr .post(`/gdc/md/${projectId}/ldm/manage2`, { body: { manage: { maql } } }) .then((r) => r.getData()) .then((response) => { const manageStatusUri = response.entries[0].link; return handlePolling(this.xhr.get.bind(this.xhr), manageStatusUri, this.isTaskFinished, options); }) .then(this.checkStatusForError); } /** * ETL pull * * @experimental * @param projectId - id of the project to use * @param uploadsDir - the directory to use * @param options - for polling (maxAttempts, pollStep) */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types etlPull(projectId, uploadsDir, options = {}) { return this.xhr .post(`/gdc/md/${projectId}/etl/pull2`, { body: { pullIntegration: uploadsDir } }) .then((r) => r.getData()) .then((response) => { const etlPullStatusUri = response.pull2Task.links.poll; return handlePolling(this.xhr.get.bind(this.xhr), etlPullStatusUri, this.isTaskFinished, options); }) .then(this.checkStatusForError); } isTaskFinished(task) { const taskState = task.wTaskStatus.status; return taskState === "OK" || taskState === "ERROR"; } checkStatusForError(response) { if (response.wTaskStatus.status === "ERROR") { return Promise.reject(response); } return response; } } //# sourceMappingURL=metadata.js.map