UNPKG

cozy-dataproxy-lib

Version:

Library meant to be by Cozy Cloud's DataProxy apps for data manipulation

359 lines (276 loc) 13.2 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.normalizeSearchResult = exports.getCleanedFilePath = exports.enrichResultsWithDocs = void 0; var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _cozyClient = require("cozy-client"); var _cozyMinilog = _interopRequireDefault(require("cozy-minilog")); var _filePaths = require("./filePaths"); var _consts = require("../consts"); var _queries = require("../queries"); var _types = require("../types"); var _utils = require("./utils"); function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } var log = (0, _cozyMinilog.default)('🗂️ [Indexing]'); var normalizeSearchResult = function normalizeSearchResult(client, searchResults, query) { var doc = getCleanedFilePath(searchResults.doc); var slug = getSearchResultSlug(client, doc); var url = buildOpenURL(client, doc, slug); var secondaryUrl = buildSecondaryURL(client, doc, url); var title = getSearchResultTitle(doc); var subTitle = getSearchResultSubTitle(client, { fields: searchResults.fields, doc: doc, query: query }); var normalizedRes = { doc: doc, slug: slug, title: title, subTitle: subTitle, url: url, secondaryUrl: secondaryUrl }; return normalizedRes; }; exports.normalizeSearchResult = normalizeSearchResult; var getCleanedFilePath = function getCleanedFilePath(doc) { if (!(0, _types.isIOCozyFile)(doc)) { return doc; } var path = doc.path ? doc.path : (0, _filePaths.getFilePath)(doc._id); if (!path) { // Paths should be completed for both files and directories, at indexing time log.warn("No path found for ".concat(doc._id, "}")); return doc; } var newPath = path; if (path.endsWith("/".concat(doc.name))) { // Remove the name from the path, which is added at indexing time to search on it newPath = path.slice(0, -doc.name.length - 1); } return _objectSpread(_objectSpread({}, doc), {}, { path: newPath }); }; exports.getCleanedFilePath = getCleanedFilePath; var getSearchResultTitle = function getSearchResultTitle(doc) { if ((0, _types.isIOCozyFile)(doc)) { return doc.name; } if ((0, _types.isIOCozyContact)(doc)) { return doc.displayName || doc.fullname || null; } if ((0, _types.isIOCozyApp)(doc)) { return doc.name; } return null; }; var getSearchResultSubTitle = function getSearchResultSubTitle(client, params) { var fields = params.fields, doc = params.doc, query = params.query; if ((0, _types.isIOCozyFile)(doc)) { var _doc$path; return (_doc$path = doc.path) !== null && _doc$path !== void 0 ? _doc$path : null; } if ((0, _types.isIOCozyContact)(doc)) { var _matchingValue$toStri, _matchingValue; var matchingValue; // Several document fields might match a search query. Let's take the first one different from name, assuming a relevance order var matchingField = fields.find(function (field) { return field !== 'displayName' && field !== 'fullname'; }); if (!matchingField) { return null; } if (matchingField.includes('[]:')) { var tokens = matchingField.split('[]:'); if (tokens.length !== 2) { return null; } var arrayAttributeName = tokens[0]; var valueAttribute = tokens[1]; var array = doc[arrayAttributeName]; var matchingArrayItem = Array.isArray(array) && array.find(function (item) { var value = typeof item === 'object' && item !== null && valueAttribute in item && item[valueAttribute]; return typeof value === 'string' && value.includes(query); }); if (!matchingArrayItem) { return null; } matchingValue = matchingArrayItem[valueAttribute]; } else { matchingValue = doc[matchingField]; } return (_matchingValue$toStri = (_matchingValue = matchingValue) === null || _matchingValue === void 0 ? void 0 : _matchingValue.toString()) !== null && _matchingValue$toStri !== void 0 ? _matchingValue$toStri : null; } if (doc._type === _consts.APPS_DOCTYPE) { try { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment var locale = client.getInstanceOptions().locale || 'en'; if (doc.locales[locale]) { return doc.locales[locale].short_description; } } catch (_unused) { return doc.name; } } return null; }; var getSearchResultSlug = function getSearchResultSlug(client, doc) { if ((0, _types.isIOCozyFile)(doc)) { if (_cozyClient.models.file.isNote(doc)) { var _doc$cozyMetadata; var cozyUrl = client.getStackClient().uri; var createdOn = (_doc$cozyMetadata = doc.cozyMetadata) === null || _doc$cozyMetadata === void 0 ? void 0 : _doc$cozyMetadata.createdOn; var isSharedNote = createdOn && createdOn !== "".concat(cozyUrl, "/"); // In case of a shared note, the cozyURL must be the one from the instance who the created it, // and should include the docID coming from this instance. // As we do not have this info, we need to first open the note on Drive, which will handle it // and make the correct redirection. return isSharedNote ? 'drive' : 'notes'; } return 'drive'; } if ((0, _types.isIOCozyContact)(doc)) { return 'contacts'; } if ((0, _types.isIOCozyApp)(doc)) { return doc.slug; } return null; }; var buildOpenURL = function buildOpenURL(client, doc, slug) { // TODO: extract some of this common logic with Drive in cozy-client var urlHash = ''; if ((0, _types.isIOCozyFile)(doc)) { var isDir = doc.type === _consts.TYPE_DIRECTORY; var dirId = isDir ? doc._id : doc.dir_id; var folderURLHash = "/folder/".concat(dirId); if (_cozyClient.models.file.isNote(doc)) { // A note might be opened by Drive if it is shared urlHash = slug === 'notes' ? "/n/".concat(doc._id) : "/note/".concat(doc._id); } else if (_cozyClient.models.file.shouldBeOpenedByOnlyOffice(doc)) { urlHash = "/onlyoffice/".concat(doc._id, "?redirectLink=drive").concat(folderURLHash); } else if (isDir) { urlHash = folderURLHash; } else { urlHash = "".concat(folderURLHash, "/file/").concat(doc._id); } } if ((0, _types.isIOCozyContact)(doc)) { urlHash = "/".concat(doc._id); } if (!slug) { return null; } return (0, _cozyClient.generateWebLink)({ cozyUrl: client.getStackClient().uri, slug: slug, subDomainType: client.getInstanceOptions().subdomain, hash: urlHash, pathname: '', searchParams: [] }); }; var buildSecondaryURL = function buildSecondaryURL(client, doc, url) { if (!(0, _types.isIOCozyFile)(doc) || !url) { return null; } var folderURLHash = "/folder/".concat(doc.dir_id); return (0, _cozyClient.generateWebLink)({ cozyUrl: client.getStackClient().uri, slug: 'drive', subDomainType: client.getInstanceOptions().subdomain, hash: folderURLHash, pathname: '', searchParams: [] }); }; var enrichResultsWithDocs = /*#__PURE__*/function () { var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(client, results) { var _docs; var enrichedResults, resultsByDoctype, docs, _i, _Object$keys, doctype, ids, startQuery, fromStore, queryDocs, endQuery, docsMap, _iterator, _step, _res$id, res, id, doc; return _regenerator.default.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: enrichedResults = (0, _toConsumableArray2.default)(results); // Group by doctype resultsByDoctype = results.reduce(function (acc, _ref2) { var id = _ref2.id, doctype = _ref2.doctype; if (!acc[doctype]) { acc[doctype] = []; } acc[doctype].push(id); return acc; }, {}); docs = []; _i = 0, _Object$keys = Object.keys(resultsByDoctype); case 4: if (!(_i < _Object$keys.length)) { _context.next = 18; break; } doctype = _Object$keys[_i]; ids = resultsByDoctype[doctype]; startQuery = performance.now(); fromStore = false; // We used to query from store as it was much more efficient, but now we query directly from PouchDB // which should be fast enough after performances improvements in cozy-pouch-link _context.next = 11; return (0, _queries.queryDocsByIds)(client, doctype, ids, { fromStore: false }); case 11: queryDocs = _context.sent; endQuery = performance.now(); docs = docs.concat(queryDocs); if ((0, _utils.isDebug)()) { log.debug("Query took ".concat((endQuery - startQuery).toFixed(2), " ms to retrieve ").concat(ids.length, " ").concat(doctype, " from store: ").concat(fromStore)); } case 15: _i++; _context.next = 4; break; case 18: docsMap = new Map((_docs = docs) === null || _docs === void 0 ? void 0 : _docs.map(function (doc) { return [doc._id, doc]; })); _iterator = _createForOfIteratorHelper(enrichedResults); try { for (_iterator.s(); !(_step = _iterator.n()).done;) { res = _step.value; id = (_res$id = res.id) === null || _res$id === void 0 ? void 0 : _res$id.toString(); // Because of flexsearch Id typing doc = docsMap.get(id); if (!doc) { log.error("".concat(id, " is found in search but not in local data")); } else { res.doc = doc; } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return _context.abrupt("return", enrichedResults); case 22: case "end": return _context.stop(); } } }, _callee); })); return function enrichResultsWithDocs(_x, _x2) { return _ref.apply(this, arguments); }; }(); exports.enrichResultsWithDocs = enrichResultsWithDocs;