UNPKG

@webiny/api-form-builder-so-ddb-es

Version:

[![](https://img.shields.io/npm/dw/@webiny/api-form-builder-so-ddb-es.svg)](https://www.npmjs.com/package/@webiny/api-form-builder-so-ddb-es) [![](https://img.shields.io/npm/v/@webiny/api-form-builder-so-ddb-es.svg)](https://www.npmjs.com/package/@webiny

856 lines (850 loc) 22.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.createFormStorageOperations = void 0; var _query = require("@webiny/db-dynamodb/utils/query"); var _error = _interopRequireDefault(require("@webiny/error")); var _dbDynamodb = require("@webiny/db-dynamodb"); var _configurations = require("../../configurations"); var _filter = require("@webiny/db-dynamodb/utils/filter"); var _fields = _interopRequireDefault(require("./fields")); var _sort = require("@webiny/db-dynamodb/utils/sort"); var _utils = require("@webiny/utils"); var _elasticsearchBody = require("./elasticsearchBody"); var _apiElasticsearch = require("@webiny/api-elasticsearch"); const getESDataForLatestRevision = form => ({ __type: (0, _elasticsearchBody.createFormElasticType)(), id: form.id, createdOn: form.createdOn, savedOn: form.savedOn, name: form.name, slug: form.slug, published: form.published, publishedOn: form.publishedOn, version: form.version, locked: form.locked, status: form.status, createdBy: form.createdBy, ownedBy: form.ownedBy, tenant: form.tenant, locale: form.locale, webinyVersion: form.webinyVersion, formId: form.formId }); const createFormStorageOperations = params => { const { entity, esEntity, plugins, elasticsearch } = params; const formDynamoDbFields = (0, _fields.default)(); const createFormPartitionKey = params => { const { tenant, locale, id: targetId } = params; const { id } = (0, _utils.parseIdentifier)(targetId); return `T#${tenant}#L#${locale}#FB#F#${id}`; }; const createRevisionSortKey = value => { const version = typeof value === "number" ? Number(value) : (0, _utils.parseIdentifier)(value).version; return `REV#${(0, _utils.zeroPad)(version)}`; }; const createLatestSortKey = () => { return "L"; }; const createLatestPublishedSortKey = () => { return "LP"; }; const createFormType = () => { return "fb.form"; }; const createFormLatestType = () => { return "fb.form.latest"; }; const createFormLatestPublishedType = () => { return "fb.form.latestPublished"; }; const createForm = async params => { const { form } = params; const revisionKeys = { PK: createFormPartitionKey(form), SK: createRevisionSortKey(form.id) }; const latestKeys = { PK: createFormPartitionKey(form), SK: createLatestSortKey() }; const itemsBatch = (0, _dbDynamodb.createEntityWriteBatch)({ entity, put: [{ ...form, TYPE: createFormType(), ...revisionKeys }, { ...form, TYPE: createFormLatestType(), ...latestKeys }] }); try { await itemsBatch.execute(); } catch (ex) { throw new _error.default(ex.message || "Could not insert form data into regular table.", ex.code || "CREATE_FORM_ERROR", { revisionKeys, latestKeys, form }); } try { const { index } = _configurations.configurations.es({ tenant: form.tenant, locale: form.locale }); await (0, _dbDynamodb.put)({ entity: esEntity, item: { index, data: getESDataForLatestRevision(form), TYPE: createFormType(), ...latestKeys } }); } catch (ex) { throw new _error.default(ex.message || "Could not insert form data into Elasticsearch table.", ex.code || "CREATE_FORM_ERROR", { latestKeys, form }); } return form; }; const createFormFrom = async params => { const { form, original, latest } = params; const revisionKeys = { PK: createFormPartitionKey(form), SK: createRevisionSortKey(form.version) }; const latestKeys = { PK: createFormPartitionKey(form), SK: createLatestSortKey() }; const entityBatch = (0, _dbDynamodb.createEntityWriteBatch)({ entity, put: [{ ...form, ...revisionKeys, TYPE: createFormType() }, { ...form, ...latestKeys, TYPE: createFormLatestType() }] }); try { await entityBatch.execute(); } catch (ex) { throw new _error.default(ex.message || "Could not create form data in the regular table, from existing form.", ex.code || "CREATE_FORM_FROM_ERROR", { revisionKeys, latestKeys, original, form, latest }); } try { const { index } = _configurations.configurations.es({ tenant: form.tenant, locale: form.locale }); await (0, _dbDynamodb.put)({ entity: esEntity, item: { index, data: getESDataForLatestRevision(form), TYPE: createFormLatestType(), ...latestKeys } }); } catch (ex) { throw new _error.default(ex.message || "Could not create form in the Elasticsearch table, from existing form.", ex.code || "CREATE_FORM_FROM_ERROR", { latestKeys, form, latest, original }); } return form; }; const updateForm = async params => { const { form, original } = params; const revisionKeys = { PK: createFormPartitionKey(form), SK: createRevisionSortKey(form.id) }; const latestKeys = { PK: createFormPartitionKey(form), SK: createLatestSortKey() }; const { formId, tenant, locale } = form; const latestForm = await getForm({ where: { formId, tenant, locale, latest: true } }); const isLatestForm = latestForm ? latestForm.id === form.id : false; const entityBatch = (0, _dbDynamodb.createEntityWriteBatch)({ entity, put: [{ ...form, TYPE: createFormType(), ...revisionKeys }] }); if (isLatestForm) { entityBatch.put({ ...form, TYPE: createFormLatestType(), ...latestKeys }); } try { await entityBatch.execute(); } catch (ex) { throw new _error.default(ex.message || "Could not update form data in the regular table.", ex.code || "UPDATE_FORM_ERROR", { revisionKeys, latestKeys, original, form, latestForm }); } /** * No need to go further if its not latest form. */ if (!isLatestForm) { return form; } try { const { index } = _configurations.configurations.es({ tenant: form.tenant, locale: form.locale }); await (0, _dbDynamodb.put)({ entity: esEntity, item: { index, data: getESDataForLatestRevision(form), TYPE: createFormLatestType(), ...latestKeys } }); } catch (ex) { throw new _error.default(ex.message || "Could not update form data in the Elasticsearch table.", ex.code || "UPDATE_FORM_ERROR", { latestKeys, form, latestForm, original }); } return form; }; const getForm = async params => { const { where } = params; const { id, formId, latest, published, version, tenant, locale } = where; if (latest && published) { throw new _error.default("Cannot have both latest and published params."); } let sortKey; if (latest) { sortKey = createLatestSortKey(); } else if (published && !version) { /** * Because of the specifics how DynamoDB works, we must not load the published record if version is sent. */ sortKey = createLatestPublishedSortKey(); } else if (id || version) { sortKey = createRevisionSortKey(version || id); } else { throw new _error.default("Missing parameter to create a sort key.", "MISSING_WHERE_PARAMETER", { where }); } const keys = { PK: createFormPartitionKey({ tenant, locale, id: formId || id }), SK: sortKey }; try { return await (0, _dbDynamodb.getClean)({ entity, keys }); } catch (ex) { throw new _error.default(ex.message || "Could not get form by keys.", ex.code || "GET_FORM_ERROR", { keys }); } }; const listForms = async params => { const { sort, limit, where, after } = params; const body = (0, _elasticsearchBody.createElasticsearchBody)({ plugins, sort, limit: limit + 1, where, after: (0, _apiElasticsearch.decodeCursor)(after) }); const esConfig = _configurations.configurations.es({ tenant: where.tenant, locale: where.locale }); const query = { ...esConfig, body }; let response; try { response = await elasticsearch.search(query); } catch (ex) { throw new _error.default(ex.message || "Could list forms.", ex.code || "LIST_FORMS_ERROR", { where, query }); } const { hits, total } = response.body.hits; const items = hits.map(item => item._source); const hasMoreItems = items.length > limit; if (hasMoreItems) { /** * Remove the last item from results, we don't want to include it. */ items.pop(); } /** * Cursor is the `sort` value of the last item in the array. * https://www.elastic.co/guide/en/elasticsearch/reference/current/paginate-search-results.html#search-after */ const meta = { hasMoreItems, totalCount: total.value, cursor: items.length > 0 ? (0, _apiElasticsearch.encodeCursor)(hits[items.length - 1].sort) || null : null }; return { items, meta }; }; const listFormRevisions = async params => { const { where: initialWhere, sort } = params; const { id, formId, tenant, locale } = initialWhere; const queryAllParams = { entity, partitionKey: createFormPartitionKey({ tenant, locale, id: id || formId }), options: { beginsWith: "REV#" } }; let items = []; try { items = await (0, _query.queryAll)(queryAllParams); } catch (ex) { throw new _error.default(ex.message || "Could not query forms by given params.", ex.code || "QUERY_FORMS_ERROR", { partitionKey: queryAllParams.partitionKey, options: queryAllParams.options }); } const where = { ...initialWhere, id: undefined, formId: undefined }; const filteredItems = (0, _filter.filterItems)({ plugins, items, where, fields: formDynamoDbFields }); if (!sort || sort.length === 0) { return filteredItems; } return (0, _sort.sortItems)({ items: filteredItems, sort, fields: formDynamoDbFields }); }; const deleteForm = async params => { const { form } = params; let items; /** * This will find all form and submission records. */ const queryAllParams = { entity, partitionKey: createFormPartitionKey(form), options: { gte: " " } }; try { items = await (0, _query.queryAll)(queryAllParams); } catch (ex) { throw new _error.default(ex.message || "Could not query forms and submissions by given params.", ex.code || "QUERY_FORM_AND_SUBMISSIONS_ERROR", { partitionKey: queryAllParams.partitionKey, options: queryAllParams.options }); } const deleteBatch = (0, _dbDynamodb.createEntityWriteBatch)({ entity, delete: items.map(item => { return { PK: item.PK, SK: item.SK }; }) }); try { await deleteBatch.execute(); } catch (ex) { throw new _error.default(ex.message || "Could not delete form and it's submissions.", ex.code || "DELETE_FORM_AND_SUBMISSIONS_ERROR"); } const latestKeys = { PK: createFormPartitionKey(form), SK: createLatestSortKey() }; const deleteEsBatch = (0, _dbDynamodb.createEntityWriteBatch)({ entity: esEntity, delete: [latestKeys] }); try { await deleteEsBatch.execute(); } catch (ex) { throw new _error.default(ex.message || "Could not delete latest form record from Elasticsearch.", ex.code || "DELETE_FORM_ERROR", { latestKeys }); } return form; }; /** * We need to: * - delete current revision * - get previously published revision and update the record if it exists or delete if it does not * - update latest record if current one is the latest */ const deleteFormRevision = async params => { const { form, revisions, previous } = params; const revisionKeys = { PK: createFormPartitionKey(form), SK: createRevisionSortKey(form.id) }; const latestKeys = { PK: createFormPartitionKey(form), SK: createLatestSortKey() }; const latestForm = revisions[0]; const latestPublishedForm = revisions.find(rev => rev.published === true); const isLatest = latestForm ? latestForm.id === form.id : false; const isLatestPublished = latestPublishedForm ? latestPublishedForm.id === form.id : false; const entityBatch = (0, _dbDynamodb.createEntityWriteBatch)({ entity, delete: [revisionKeys] }); let esDataItem = undefined; if (isLatest || isLatestPublished) { /** * Sort out the latest published record. */ if (isLatestPublished) { const previouslyPublishedForm = revisions.filter(f => !!f.publishedOn && f.version !== form.version).sort((a, b) => { return new Date(b.publishedOn).getTime() - new Date(a.publishedOn).getTime(); }).shift(); if (previouslyPublishedForm) { entityBatch.put({ ...previouslyPublishedForm, PK: createFormPartitionKey(previouslyPublishedForm), SK: createLatestPublishedSortKey(), TYPE: createFormLatestPublishedType() }); } else { entityBatch.delete({ PK: createFormPartitionKey(form), SK: createLatestPublishedSortKey() }); } } /** * Sort out the latest record. */ if (isLatest && previous) { entityBatch.put({ ...previous, ...latestKeys, TYPE: createFormLatestType() }); const { index } = _configurations.configurations.es({ tenant: previous.tenant, locale: previous.locale }); esDataItem = { index, ...latestKeys, data: getESDataForLatestRevision(previous) }; } } /** * Now save the batch data. */ try { await entityBatch.execute(); } catch (ex) { throw new _error.default(ex.message || "Could not delete form revision from regular table.", ex.code || "DELETE_FORM_REVISION_ERROR", { form, latestForm, revisionKeys, latestKeys }); } /** * And then the Elasticsearch data, if any. */ if (!esDataItem) { return form; } try { await (0, _dbDynamodb.put)({ entity: esEntity, item: esDataItem }); return form; } catch (ex) { throw new _error.default(ex.message || "Could not delete form from to the Elasticsearch table.", ex.code || "DELETE_FORM_REVISION_ERROR", { form, latestForm, revisionKeys, latestKeys }); } }; /** * We need to save form in: * - regular form record * - latest published form record * - latest form record - if form is latest one * - elasticsearch latest form record */ const publishForm = async params => { const { form, original } = params; const revisionKeys = { PK: createFormPartitionKey(form), SK: createRevisionSortKey(form.version) }; const latestKeys = { PK: createFormPartitionKey(form), SK: createLatestSortKey() }; const latestPublishedKeys = { PK: createFormPartitionKey(form), SK: createLatestPublishedSortKey() }; const { locale, tenant, formId } = form; const latestForm = await getForm({ where: { formId, tenant, locale, latest: true } }); const isLatestForm = latestForm ? latestForm.id === form.id : false; /** * Update revision and latest published records */ const entityBatch = (0, _dbDynamodb.createEntityWriteBatch)({ entity, put: [{ ...form, ...revisionKeys, TYPE: createFormType() }, { ...form, ...latestPublishedKeys, TYPE: createFormLatestPublishedType() }] }); /** * Update the latest form as well */ if (isLatestForm) { entityBatch.put({ ...form, ...latestKeys, TYPE: createFormLatestType() }); } try { await entityBatch.execute(); } catch (ex) { throw new _error.default(ex.message || "Could not publish form.", ex.code || "PUBLISH_FORM_ERROR", { form, original, latestForm, revisionKeys, latestKeys, latestPublishedKeys }); } if (!isLatestForm) { return form; } const { index } = _configurations.configurations.es({ tenant: form.tenant, locale: form.locale }); const esData = getESDataForLatestRevision(form); try { await (0, _dbDynamodb.put)({ entity: esEntity, item: { ...latestKeys, index, TYPE: createFormLatestType(), data: esData } }); return form; } catch (ex) { throw new _error.default(ex.message || "Could not publish form to the Elasticsearch.", ex.code || "PUBLISH_FORM_ERROR", { form, original, latestForm, revisionKeys, latestKeys, latestPublishedKeys }); } }; /** * We need to: * - update form revision record * - if latest published (LP) is current form, find the previously published record and update LP if there is some previously published, delete otherwise * - if is latest update the Elasticsearch record */ const unpublishForm = async params => { const { form, original } = params; const revisionKeys = { PK: createFormPartitionKey(form), SK: createRevisionSortKey(form.version) }; const latestKeys = { PK: createFormPartitionKey(form), SK: createLatestSortKey() }; const latestPublishedKeys = { PK: createFormPartitionKey(form), SK: createLatestPublishedSortKey() }; const { formId, tenant, locale } = form; const latestForm = await getForm({ where: { formId, tenant, locale, latest: true } }); const latestPublishedForm = await getForm({ where: { formId, tenant, locale, published: true } }); const isLatest = latestForm ? latestForm.id === form.id : false; const isLatestPublished = latestPublishedForm ? latestPublishedForm.id === form.id : false; const entityBatch = (0, _dbDynamodb.createEntityWriteBatch)({ entity, put: [{ ...form, ...revisionKeys, TYPE: createFormType() }] }); let esData = undefined; if (isLatest) { esData = getESDataForLatestRevision(form); } /** * In case previously published revision exists, replace current one with that one. * And if it does not, delete the record. */ if (isLatestPublished) { const revisions = await listFormRevisions({ where: { formId, tenant, locale, version_not: form.version, publishedOn_not: null }, sort: ["savedOn_DESC"] }); const previouslyPublishedRevision = revisions.shift(); if (previouslyPublishedRevision) { entityBatch.put({ ...previouslyPublishedRevision, ...latestPublishedKeys, TYPE: createFormLatestPublishedType() }); } else { entityBatch.delete(latestPublishedKeys); } } try { await entityBatch.execute(); } catch (ex) { throw new _error.default(ex.message || "Could not unpublish form.", ex.code || "UNPUBLISH_FORM_ERROR", { form, original, latestForm, revisionKeys, latestKeys, latestPublishedKeys }); } /** * No need to go further in case of non-existing Elasticsearch data. */ if (!esData) { return form; } const { index } = _configurations.configurations.es({ tenant: form.tenant, locale: form.locale }); try { await (0, _dbDynamodb.put)({ entity: esEntity, item: { ...latestKeys, index, TYPE: createFormLatestType(), data: esData } }); return form; } catch (ex) { throw new _error.default(ex.message || "Could not unpublish form from the Elasticsearch.", ex.code || "UNPUBLISH_FORM_ERROR", { form, original, latestForm, revisionKeys, latestKeys, latestPublishedKeys }); } }; return { createForm, createFormFrom, updateForm, listForms, listFormRevisions, getForm, deleteForm, deleteFormRevision, publishForm, unpublishForm, createFormPartitionKey }; }; exports.createFormStorageOperations = createFormStorageOperations; //# sourceMappingURL=index.js.map