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