UNPKG

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

Version:

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

691 lines (686 loc) 18.3 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.createFormStorageOperations = void 0; var _error = _interopRequireDefault(require("@webiny/error")); var _query = require("@webiny/db-dynamodb/utils/query"); var _dbDynamodb = require("@webiny/db-dynamodb"); var _filter = require("@webiny/db-dynamodb/utils/filter"); var _sort = require("@webiny/db-dynamodb/utils/sort"); var _utils = require("@webiny/utils"); var _FormDynamoDbFieldPlugin = require("../../plugins/FormDynamoDbFieldPlugin"); var _cursor = require("@webiny/db-dynamodb/utils/cursor"); var _get = require("@webiny/db-dynamodb/utils/get"); const createFormStorageOperations = params => { const { entity, plugins } = params; const formDynamoDbFields = plugins.byType(_FormDynamoDbFieldPlugin.FormDynamoDbFieldPlugin.type); const createFormPartitionKey = params => { const { tenant, locale } = params; return `T#${tenant}#L#${locale}#FB#F`; }; const createFormLatestPartitionKey = params => { return `${createFormPartitionKey(params)}#L`; }; const createFormLatestPublishedPartitionKey = params => { return `${createFormPartitionKey(params)}#LP`; }; const createFormGSIPartitionKey = params => { const { tenant, locale, id: targetId } = params; const { id } = (0, _utils.parseIdentifier)(targetId); return `T#${tenant}#L#${locale}#FB#F#${id}`; }; const createRevisionSortKey = ({ id }) => { return `${id}`; }; const createFormLatestSortKey = ({ id, formId }) => { const value = (0, _utils.parseIdentifier)(id || formId); return value.id; }; const createLatestPublishedSortKey = ({ id, formId }) => { const value = (0, _utils.parseIdentifier)(id || formId); return value.id; }; const createGSISortKey = version => { return `${version}`; }; const createFormType = () => { return "fb.form"; }; const createFormLatestType = () => { return "fb.form.latest"; }; const createFormLatestPublishedType = () => { return "fb.form.latestPublished"; }; const createRevisionKeys = form => { return { PK: createFormPartitionKey(form), SK: createRevisionSortKey(form) }; }; const createLatestKeys = form => { return { PK: createFormLatestPartitionKey(form), SK: createFormLatestSortKey(form) }; }; const createLatestPublishedKeys = form => { return { PK: createFormLatestPublishedPartitionKey(form), SK: createLatestPublishedSortKey(form) }; }; const createGSIKeys = form => { return { GSI1_PK: createFormGSIPartitionKey(form), GSI1_SK: createGSISortKey(form.version) }; }; const createForm = async params => { const { form } = params; const revisionKeys = createRevisionKeys(form); const latestKeys = createLatestKeys(form); const gsiKeys = createGSIKeys(form); const entityBatch = (0, _dbDynamodb.createEntityWriteBatch)({ entity, put: [{ ...form, ...revisionKeys, ...gsiKeys, TYPE: createFormType() }, { ...form, ...latestKeys, TYPE: createFormLatestType() }] }); try { await entityBatch.execute(); } catch (ex) { throw new _error.default(ex.message || "Could not insert form data into table.", ex.code || "CREATE_FORM_ERROR", { revisionKeys, latestKeys, form }); } return form; }; const createFormFrom = async params => { const { form, original, latest } = params; const revisionKeys = createRevisionKeys(form); const latestKeys = createLatestKeys(form); const gsiKeys = createGSIKeys(form); const entityBatch = (0, _dbDynamodb.createEntityWriteBatch)({ entity, put: [{ ...form, ...revisionKeys, ...gsiKeys, 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 table, from existing form.", ex.code || "CREATE_FORM_FROM_ERROR", { revisionKeys, latestKeys, original, form, latest }); } return form; }; const updateForm = async params => { const { form, original } = params; const revisionKeys = createRevisionKeys(form); const latestKeys = createLatestKeys(form); const gsiKeys = createGSIKeys(form); 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, ...revisionKeys, ...gsiKeys, TYPE: createFormType() }] }); if (isLatestForm) { entityBatch.put({ ...form, ...latestKeys, TYPE: createFormLatestType() }); } try { await entityBatch.execute(); } catch (ex) { throw new _error.default(ex.message || "Could not update form data in the table.", ex.code || "UPDATE_FORM_ERROR", { revisionKeys, latestKeys, original, form, latestForm }); } return form; }; const getForm = async params => { const { where } = params; const { id, formId, latest, published, version } = where; if (latest && published) { throw new _error.default("Cannot have both latest and published params."); } let partitionKey; let sortKey; if (latest) { partitionKey = createFormLatestPartitionKey(where); sortKey = createFormLatestSortKey(where); } else if (published && !version) { /** * Because of the specifics how DynamoDB works, we must not load the published record if version is sent. */ partitionKey = createFormLatestPublishedPartitionKey(where); sortKey = createLatestPublishedSortKey(where); } else if (id || version) { partitionKey = createFormPartitionKey(where); sortKey = createRevisionSortKey({ id: id || (0, _utils.createIdentifier)({ id: formId, version: version }) }); } else { throw new _error.default("Missing parameter to create a sort key.", "MISSING_WHERE_PARAMETER", { where }); } const keys = { PK: partitionKey, SK: sortKey }; try { return await (0, _get.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: initialWhere, after } = params; const queryAllParams = { entity, partitionKey: createFormLatestPartitionKey(initialWhere), options: { gte: " " } }; let results; try { results = await (0, _query.queryAll)(queryAllParams); } catch (ex) { throw new _error.default(ex.message || "Could list forms.", ex.code || "LIST_FORMS_ERROR", { where: initialWhere, partitionKey: queryAllParams.partitionKey }); } const totalCount = results.length; const where = { ...initialWhere }; /** * We need to remove conditions so we do not filter by them again. */ delete where.tenant; delete where.locale; const filteredItems = (0, _filter.filterItems)({ plugins, items: results, where, fields: formDynamoDbFields }); const sortedItems = (0, _sort.sortItems)({ items: filteredItems, sort, fields: formDynamoDbFields }); const start = parseInt((0, _cursor.decodeCursor)(after) || "0") || 0; const hasMoreItems = totalCount > start + limit; const end = limit > totalCount + start + limit ? undefined : start + limit; const items = sortedItems.slice(start, end); /** * Although we do not need a cursor here, we will use it as such to keep it standardized. * Number is simply encoded. */ const cursor = items.length > 0 ? (0, _cursor.encodeCursor)(start + limit) : null; const meta = { hasMoreItems, totalCount, cursor }; return { items, meta }; }; const listFormRevisions = async params => { const { where: initialWhere, sort } = params; const { id, formId, tenant, locale } = initialWhere; const queryAllParams = { entity, partitionKey: createFormGSIPartitionKey({ tenant, locale, id: formId || id }), options: { index: "GSI1", gte: " " } }; 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 }; /** * We need to remove conditions so we do not filter by them again. */ delete where.id; delete where.formId; delete where.tenant; delete where.locale; const filteredItems = (0, _filter.filterItems)({ plugins, items, where, fields: formDynamoDbFields }); return (0, _sort.sortItems)({ items: filteredItems, sort, fields: formDynamoDbFields }); }; const deleteForm = async params => { const { form } = params; let items; /** * This will find all form records. */ const queryAllParams = { entity, partitionKey: createFormPartitionKey(form), options: { beginsWith: form.formId || undefined } }; 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 }); } let latestPublishedKeys; const entityBatch = (0, _dbDynamodb.createEntityWriteBatch)({ entity, delete: [createLatestKeys(form)] }); for (const item of items) { if (!latestPublishedKeys && item.published) { latestPublishedKeys = createLatestPublishedKeys(item); } entityBatch.delete({ PK: item.PK, SK: item.SK }); } if (latestPublishedKeys) { entityBatch.delete(latestPublishedKeys); } try { await entityBatch.execute(); } catch (ex) { throw new _error.default(ex.message || "Could not delete form and it's submissions.", ex.code || "DELETE_FORM_AND_SUBMISSIONS_ERROR"); } 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 = createRevisionKeys(form); const latestKeys = createLatestKeys(form); 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] }); 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, ...createLatestPublishedKeys(previouslyPublishedForm), GSI1_PK: null, GSI1_SK: null, TYPE: createFormLatestPublishedType() }); } else { entityBatch.delete(createLatestPublishedKeys(form)); } } /** * Sort out the latest record. */ if (isLatest) { entityBatch.put({ ...previous, ...latestKeys, GSI1_PK: null, GSI1_SK: null, TYPE: createFormLatestType() }); } } /** * Now save the batch data. */ try { await entityBatch.execute(); return form; } catch (ex) { throw new _error.default(ex.message || "Could not delete form revision from 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 = createRevisionKeys(form); const latestKeys = createLatestKeys(form); const latestPublishedKeys = createLatestPublishedKeys(form); const gsiKeys = { GSI1_PK: createFormGSIPartitionKey(form), GSI1_SK: createGSISortKey(form.version) }; 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, ...gsiKeys, 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 }); } return form; }; /** * 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 = createRevisionKeys(form); const latestKeys = createLatestKeys(form); const latestPublishedKeys = createLatestPublishedKeys(form); const gsiKeys = createGSIKeys(form); 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, ...gsiKeys, TYPE: createFormType() }] }); if (isLatest) { entityBatch.put({ ...form, ...latestKeys, TYPE: createFormLatestType() }); } /** * 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(); return form; } catch (ex) { throw new _error.default(ex.message || "Could not unpublish form.", 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