UNPKG

@gooddata/api-client-bear

Version:
504 lines • 17.8 kB
// (C) 2007-2022 GoodData Corporation import { invariant } from "ts-invariant"; import { getAllPagesByOffsetLimit, getQueryEntries, handlePolling, parseSettingItemValue } from "./util.js"; import { ApiError } from "./xhr.js"; import { stringify } from "./utils/queryString.js"; export const DEFAULT_PALETTE = [ { r: 0x2b, g: 0x6b, b: 0xae }, { r: 0x69, g: 0xaa, b: 0x51 }, { r: 0xee, g: 0xb1, b: 0x4c }, { r: 0xd5, g: 0x3c, b: 0x38 }, { r: 0x89, g: 0x4d, b: 0x94 }, { r: 0x73, g: 0x73, b: 0x73 }, { r: 0x44, g: 0xa9, b: 0xbe }, { r: 0x96, g: 0xbd, b: 0x5f }, { r: 0xfd, g: 0x93, b: 0x69 }, { r: 0xe1, g: 0x5d, b: 0x86 }, { r: 0x7c, g: 0x6f, b: 0xad }, { r: 0xa5, g: 0xa5, b: 0xa5 }, { r: 0x7a, g: 0xa6, b: 0xd5 }, { r: 0x82, g: 0xd0, b: 0x8d }, { r: 0xff, g: 0xd2, b: 0x89 }, { r: 0xf1, g: 0x84, b: 0x80 }, { r: 0xbf, g: 0x90, b: 0xc6 }, { r: 0xbf, g: 0xbf, b: 0xbf }, ]; const isProjectCreated = (project) => { // TODO const projectState = project.content.state; return projectState === "ENABLED" || projectState === "DELETED"; }; /** * Functions for working with projects * */ export class ProjectModule { xhr; constructor(xhr) { this.xhr = xhr; } /** * Get current project id * * @returns current project identifier */ getCurrentProjectId() { return this.xhr .getParsed("/gdc/app/account/bootstrap/projectId") .then((response) => response.projectId); } /** * Fetches project by its identifier. * * @param projectId - Project identifier * @returns Project */ getProject(projectId) { return this.xhr.getParsed(`/gdc/projects/${projectId}`); } /** * Fetches projects available for the user represented by the given profileId * * @param profileId - User profile identifier * @returns An Array of projects */ getProjects(profileId) { return getAllPagesByOffsetLimit(this.xhr, `/gdc/account/profile/${profileId}/projects`, "projects").then((result) => result.map((p) => p.project)); } /** * Fetches projects available for the user represented by the given profileId page by page. * @param userId - id of the user to get the projects for * @param offset - number of items to skip * @param limit - maximum items on page * @param search - search string that is matched to project title as a substring */ getProjectsWithPaging(userId, offset, limit, search) { // inspired by ProjectDataSource in goodstrap. Maybe the /gdc/account/profile/${profileId}/projects would be suitable as well. const mergedOptions = { limit, offset, userId, projectStates: "ENABLED", userState: "ENABLED", }; if (search) { mergedOptions.titleSubstring = search; } const uri = `/gdc/internal/projects/?${stringify(mergedOptions)}`; return this.xhr.get(uri).then((res) => res.getData()); } /** * Fetches all datasets for the given project * * @param projectId - GD project identifier * @returns An array of objects containing datasets metadata */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types getDatasets(projectId) { return this.xhr .get(`/gdc/md/${projectId}/query/datasets`) .then((r) => r.getData()) .then(getQueryEntries); } /** * Fetches a chart color palette for a project represented by the given * projectId parameter. * * @param projectId - A project identifier * @returns An array of objects with r, g, b fields representing a project's * color palette */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types getColorPalette(projectId) { return this.xhr .get(`/gdc/projects/${projectId}/styleSettings`) .then((apiResponse) => { return apiResponse.getData(); }) .then((result) => { if (!result) { return DEFAULT_PALETTE; } return result.styleSettings.chartPalette.map((c) => { return { r: c.fill.r, g: c.fill.g, b: c.fill.b, }; }); }) .catch((e) => { if (!(e instanceof ApiError)) { return DEFAULT_PALETTE; } throw e; }); } /** * Fetches a chart color palette for a project represented by the given * projectId parameter. * * @param projectId - A project identifier * @returns An array of objects representing a project's * color palette with color guid or undefined */ getColorPaletteWithGuids(projectId) { return this.xhr .get(`/gdc/projects/${projectId}/styleSettings`) .then((apiResponse) => { return apiResponse.getData(); }) .then((result) => { if (result?.styleSettings) { return result.styleSettings.chartPalette; } else { return undefined; } }) .catch((e) => { if (!(e instanceof ApiError)) { return undefined; } throw e; }); } /** * Sets given colors as a color palette for a given project. * * @param projectId - GD project identifier * @param colors - An array of colors that we want to use within the project. * Each color should be an object with r, g, b fields. // TODO really object? */ setColorPalette(projectId, colors) { return this.xhr.put(`/gdc/projects/${projectId}/styleSettings`, { body: { styleSettings: { chartPalette: colors.map((fill, idx) => { return { fill, guid: `guid${idx}` }; }), }, }, }); } /** * Gets current timezone and its offset. * @example * * Example output: * * ``` * { * id: 'Europe/Prague', * displayName: 'Central European Time', * currentOffsetMs: 3600000 * } * ``` * * @param projectId - GD project identifier */ getTimezone(projectId) { const uri = `/gdc/app/projects/${projectId}/timezone`; return this.xhr.getParsed(uri).then((result) => result.timezone); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types setTimezone(projectId, timezone) { const timezoneServiceUrl = `/gdc/md/${projectId}/service/timezone`; const data = { service: { timezone }, }; return this.xhr .post(timezoneServiceUrl, { body: data, }) .then((r) => r.getData()); } /** * Create project * Note: returns a promise which is resolved when the project creation is finished * * @experimental * @param title - title of the new project * @param authorizationToken - authorization token to use * @param options - for project creation (summary, projectTemplate, ...) * @returns created project object */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types createProject(title, authorizationToken, options = {}) { const { summary, projectTemplate, driver = "Pg", environment = "TESTING", guidedNavigation = 1, } = options; return this.xhr .post("/gdc/projects", { body: JSON.stringify({ project: { content: { guidedNavigation, driver, authorizationToken, environment, }, meta: { title, summary, projectTemplate, }, }, }), }) .then((r) => r.getData()) .then((project) => handlePolling(this.xhr.get.bind(this.xhr), project.uri, (response) => { // TODO project response return isProjectCreated(response.project); }, options)); } /** * Delete project * * @param projectId - projectId to delete */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types deleteProject(projectId) { return this.xhr.del(`/gdc/projects/${projectId}`); } /** * Gets aggregated feature flags for given project and current user * * @param projectId - A project identifier * @returns Hash table of feature flags and theirs values where feature flag is as key */ getFeatureFlags(projectId) { return this.xhr .get(`/gdc/app/projects/${projectId}/featureFlags`) .then((apiResponse) => { return apiResponse.getData(); }) .then((result) => { if (result?.featureFlags) { return result.featureFlags; } return {}; }); } /** * Gets project config including project specific feature flags * @deprecated Use getProjectFeatureFlags instead * * @param projectId - A project identifier * @returns An array of project config setting items */ getConfig(projectId) { return this.xhr .get(`/gdc/app/projects/${projectId}/config`) .then((apiResponse) => { return apiResponse.getData(); }) .then((result) => { if (result?.settings?.items) { return result.settings.items; } return []; }); } /** * Gets project config including project specific feature flags * * @param projectId - A project identifier * @param key - config item key * @returns single setting item or undefined if item with such key does not exist */ getConfigItem(projectId, key) { return this.xhr .get(`/gdc/app/projects/${projectId}/config/${key}`) .then((apiResponse) => { return apiResponse.getData(); }) .catch((error) => { if (error?.response?.status === 404) { return undefined; } throw error; }); } /** * Gets project specific feature flags * * @param projectId - A project identifier * @param source - optional filter settingItems with specific source * @returns Hash table of feature flags and theirs values where feature flag is as key */ getProjectFeatureFlags(projectId, source) { return this.xhr .get(`/gdc/app/projects/${projectId}/config`) .then((apiResponse) => { return apiResponse.getData(); }) .then((result) => { if (result?.settings?.items) { return result.settings.items; } return []; }) .then((settingItems) => { return this.parseProjectFeatureFlags(settingItems, source); }); } parseProjectFeatureFlags(settingItems, source) { const filteredSettingItems = source ? settingItems.filter((settingItem) => settingItem.settingItem.source === source) : settingItems; const featureFlags = {}; filteredSettingItems.forEach((settingItem) => { featureFlags[settingItem.settingItem.key] = parseSettingItemValue(settingItem.settingItem.value); }); return featureFlags; } /** * Get paged user list * * @param projectId - project identifier * @param options - filtering options for the user list * @returns List of users with paging */ getUserListWithPaging(projectId, options) { return this.xhr.getParsed(`/gdc/projects/${projectId}/userlist`, { data: options, }); } /** * Get full user list * * @param projectId - project identifier * @param options - filtering options for the user list * @returns List of users */ getUserList(projectId, options) { const loadPage = async (offset = 0, limit = 1000, items = []) => { return this.getUserListWithPaging(projectId, { ...options, limit, offset }).then(({ userList: { items: userItems, paging: { count }, }, }) => { const updatedItems = [...items, ...userItems]; return count < limit ? updatedItems : loadPage(offset + limit, limit, updatedItems); }); }; return loadPage(); } /** * Get paged user groups * * @param projectId - project identifier * @param options - paging params * @returns List of user groups with paging */ getUserGroups(projectId, options) { const { offset = "0", limit = 100 } = options; return this.xhr.getParsed(`/gdc/userGroups?project=${projectId}&offset=${offset}&limit=${limit}`); } /** * Get info about all grantees able to access given object * * @param objectUri - object's uri * @param options - grantee limitations params * @returns List of all grantees */ getGranteesInfo(objectUri, options) { const { permission = "read" } = options; const apiUri = objectUri.replace("/md/", "/projects/"); return this.xhr.getParsed(`${apiUri}/grantees?permission=${permission}`); } convertGrantees(granteeUris = []) { return granteeUris.map((granteeUri) => ({ aclEntryURI: { permission: "read", grantee: granteeUri, }, })); } handleGranteesChangeError(error) { if (error?.response?.status !== 200) { throw error; } } /** * Add grantees to access given object * @param objectUri - object's uri * @param granteeUris - grantees uri array */ addGrantees(objectUri, granteeUris) { const addGranteesRequest = { granteeURIs: { items: this.convertGrantees(granteeUris), }, }; return this.xhr .post(`${objectUri}/grantees/add`, { body: { ...addGranteesRequest } }) .catch(this.handleGranteesChangeError); } /** * Remove grantees access given object * @param objectUri - object's uri * @param granteeUris - grantees uri array */ removeGrantees(objectUri, granteeUris = []) { const removeGranteesRequest = { granteeURIs: { items: this.convertGrantees(granteeUris), }, }; return this.xhr .post(`${objectUri}/grantees/remove`, { body: { ...removeGranteesRequest } }) .catch(this.handleGranteesChangeError); } /** * Get permissions for the workspace and user * @param workspaceId - ID of the workspace * @param userId - ID of the user */ getPermissions(workspaceId, userId) { return this.xhr.getParsed(`/gdc/projects/${workspaceId}/users/${userId}/permissions`); } /** * Resolves LCM workspace identifiers. This function will use the data product and client information * and consult the backend in order to obtain identifier of workspace contains analytics for that * data product & client combination. * * Domain parameter is required. Then either project ID or product ID and client ID pair must be provided. * * @param domainId - ID of the domain, must be provided * @param projectId - ID of the project. LCM identifiers will be fetched via project ID if the * ID is provided. * @param productId - ID of the product. LCM identifiers will be provided by product ID and client ID * pair, if project ID is not provided. * @param clientId - ID of the client. LCM identifiers will be provided by product ID and client ID pair, * if project ID is not provided. * * @returns Resolves with project LCM identifiers. */ getProjectLcmIdentifiers(domainId, projectId, productId, clientId) { invariant(domainId, "domain ID must be specified"); if (projectId) { return this.xhr .getParsed(`/gdc/projects/${projectId}/lcmEntity`) .then(({ projectUri, clientId, dataProductId, segmentId }) => ({ projectLcm: { projectId: this.extractIdFromUri(projectUri), clientId, dataProductId, segmentId, }, })); } invariant(productId, "product ID must be specified when project ID is not provided"); invariant(clientId, "client ID must be specified when project ID is not provided"); return this.xhr .getParsed(`/gdc/domains/${domainId}/dataproducts/${productId}/clients/${clientId}`) .then(({ client: { id, project, segment, links } }) => ({ projectLcm: { projectId: this.extractIdFromUri(project), clientId: id, dataProductId: this.extractIdFromUri(links?.dataProduct), segmentId: this.extractIdFromUri(segment), }, })); } extractIdFromUri(uri) { return uri?.split("/").pop(); } } //# sourceMappingURL=project.js.map