@webiny/api-form-builder-so-ddb
Version:
[](https://www.npmjs.com/package/@webiny/api-form-builder-so-ddb) [](https://www.npmjs.com/package/@webiny/api-form
691 lines (686 loc) • 18.3 kB
JavaScript
"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