@contentstack/datasync-content-store-filesystem
Version:
Datasync content store library - saves data in filesystem
632 lines (631 loc) • 29.3 kB
JavaScript
"use strict";
/*!
* DataSync Content Store Filesystem
* Copyright (c) Contentstack LLC
* MIT Licensed
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FilesystemStore = void 0;
const fs_1 = require("fs");
const lodash_1 = require("lodash");
const path_1 = require("path");
const fs_2 = require("./util/fs");
const get_file_fields_1 = require("./util/get-file-fields");
const index_1 = require("./util/index");
const messages_1 = require("./util/messages");
const validations_1 = require("./util/validations");
const debug = require('debug')('core-fs');
debug.log = console.log.bind(console);
if (process.env.DEBUG === "*" ||
(process.env.DEBUG || "").includes("core-fs")) {
debug.enabled = true;
}
class FilesystemStore {
constructor(assetStore, config) {
this.assetStore = assetStore;
this._config = config;
this.config = config.contentStore;
const baseDirKeys = this.config.baseDir.split(path_1.sep);
this.pattern = {};
this.unwanted = this.config.unwanted;
if (config.contentstack.branch) {
this.pattern.contentTypeKeys = baseDirKeys.concat((0, lodash_1.compact)(this.config.patternsWithBranch.contentType.split('/')));
this.pattern.entryKeys = baseDirKeys.concat((0, lodash_1.compact)(this.config.patternsWithBranch.entry.split('/')));
this.pattern.assetKeys = baseDirKeys.concat((0, lodash_1.compact)(this.config.patternsWithBranch.asset.split('/')));
}
else {
this.pattern.contentTypeKeys = baseDirKeys.concat((0, lodash_1.compact)(this.config.patterns.contentType.split('/')));
this.pattern.entryKeys = baseDirKeys.concat((0, lodash_1.compact)(this.config.patterns.entry.split('/')));
this.pattern.assetKeys = baseDirKeys.concat((0, lodash_1.compact)(this.config.patterns.asset.split('/')));
}
this.localePath = (0, index_1.buildLocalePath)(this.config);
}
publish(input) {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
try {
debug(messages_1.LOG_MESSAGES.PUBLISHING_ENTRY(input));
(0, validations_1.validatePublishedObject)(input);
if ((0, fs_1.existsSync)(this.localePath)) {
const data = yield (0, fs_2.readFile)(this.localePath, 'utf-8');
const locales = JSON.parse(data);
const idx = locales.indexOf(input.locale);
if (idx === -1) {
locales.unshift(input.locale);
yield (0, fs_2.writeFile)(this.localePath, JSON.stringify(locales));
}
}
else {
yield (0, fs_2.writeFile)(this.localePath, JSON.stringify([input.locale]));
}
if (input._content_type_uid === '_assets') {
return this.publishAsset(input)
.then(resolve)
.catch(reject);
}
const [publishEntryResult] = yield Promise.all([this.publishEntry(input), this.updateAssetReferences(input, input._content_type)]);
return resolve(publishEntryResult);
}
catch (error) {
return reject(error);
}
}));
}
unpublish(input) {
return new Promise((resolve, reject) => {
try {
(0, validations_1.validateUnpublishedObject)(input);
if (input._content_type_uid === '_assets') {
return this.unpublishAsset(input)
.then(resolve)
.catch(reject);
}
return this.unpublishEntry(input)
.then(resolve)
.catch(reject);
}
catch (error) {
return reject(error);
}
});
}
delete(input) {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
try {
let output;
if (input._content_type_uid === '_assets') {
output = yield this.deleteAsset(input);
}
else if (input._content_type_uid === '_content_types') {
output = yield this.deleteContentType(input);
}
else {
output = yield this.deleteEntry(input);
}
return resolve(output);
}
catch (error) {
return reject(error);
}
}));
}
updateContentType(data) {
return __awaiter(this, void 0, void 0, function* () {
let schema = (0, lodash_1.cloneDeep)(data);
(0, validations_1.validateContentTypeDeletedObject)(schema);
schema = (0, index_1.removeUnwantedKeys)(this.unwanted.contentType, data);
const contentTypePathKeys = (0, index_1.getPathKeys)(this.pattern.contentTypeKeys, data);
const contentTypePath = path_1.join.apply(this, contentTypePathKeys) + '.json';
if ((0, fs_1.existsSync)(contentTypePath)) {
const content = yield (0, fs_2.readFile)(contentTypePath, 'utf-8');
const jsonData = JSON.parse(content);
let contentTypeUpdated = false;
for (let i = 0, j = jsonData.length; i < j; i++) {
if (jsonData[i].uid === data.uid) {
jsonData[i] = data;
contentTypeUpdated = true;
break;
}
}
if (!contentTypeUpdated) {
jsonData.unshift(data);
}
yield (0, fs_2.writeFile)(contentTypePath, JSON.stringify(jsonData));
}
else {
contentTypePathKeys.splice(contentTypePathKeys.length - 1);
const contentTypeFolderPath = path_1.join.apply(contentTypePathKeys);
(0, fs_1.mkdirSync)(contentTypeFolderPath, { recursive: true });
yield (0, fs_2.writeFile)(contentTypePath, JSON.stringify([schema]));
}
return schema;
});
}
publishAsset(data) {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
try {
let asset = (0, lodash_1.cloneDeep)(data);
const assetPathKeys = (0, index_1.getPathKeys)(this.pattern.assetKeys, asset);
const assetPath = path_1.join.apply(this, assetPathKeys) + '.json';
assetPathKeys.splice(assetPathKeys.length - 1);
const assetFolderPath = path_1.join.apply(this, assetPathKeys);
asset = (0, index_1.removeUnwantedKeys)(this.unwanted.asset, asset);
if (asset.hasOwnProperty('_version')) {
yield this.unpublishAsset(asset);
}
asset = yield this.assetStore.download(asset);
if ((0, fs_1.existsSync)(assetFolderPath)) {
if ((0, fs_1.existsSync)(assetPath)) {
const contents = yield (0, fs_2.readFile)(assetPath, 'utf-8');
const assets = JSON.parse(contents);
if (asset.hasOwnProperty('_version')) {
assets.unshift(asset);
yield (0, fs_2.writeFile)(assetPath, JSON.stringify(assets));
}
else {
let rteMarkdownExists = false;
for (let i = 0, j = assets.length; i < j; i++) {
if (assets[i].download_id === asset.download_id) {
rteMarkdownExists = true;
break;
}
}
if (!rteMarkdownExists) {
assets.unshift(asset);
yield (0, fs_2.writeFile)(assetPath, JSON.stringify(assets));
}
}
}
else {
yield (0, fs_2.writeFile)(assetPath, JSON.stringify([asset]));
}
}
else {
(0, fs_1.mkdirSync)(assetFolderPath, { recursive: true });
yield (0, fs_2.writeFile)(assetPath, JSON.stringify([data]));
}
return resolve(asset);
}
catch (error) {
return reject(error);
}
}));
}
unpublishAsset(asset) {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
try {
const assetPathKeys = (0, index_1.getPathKeys)(this.pattern.assetKeys, asset);
const assetPath = path_1.join.apply(this, assetPathKeys) + '.json';
if ((0, fs_1.existsSync)(assetPath)) {
const data = yield (0, fs_2.readFile)(assetPath);
const assets = JSON.parse(data);
let unpublishedAsset = false;
let rteAsset = false;
let removedAsset;
for (let i = 0, j = assets.length; i < j; i++) {
if (assets[i].uid === asset.uid) {
if (assets[i].hasOwnProperty('_version')) {
removedAsset = assets.splice(i, 1)[0];
unpublishedAsset = true;
i--;
j--;
}
else if (assets[i].hasOwnProperty('download_id')) {
rteAsset = true;
}
}
}
if (unpublishedAsset && !(rteAsset)) {
yield this.assetStore.unpublish(removedAsset);
}
if (unpublishedAsset) {
yield Promise.all([(0, fs_2.writeFile)(assetPath, JSON.stringify(assets)), this.updateDeletedAssetReferences(asset)]);
}
}
return resolve(asset);
}
catch (error) {
return reject(error);
}
}));
}
unpublishEntry(entry) {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
try {
const entryPathKeys = (0, index_1.getPathKeys)(this.pattern.entryKeys, entry);
const entryPath = path_1.join.apply(this, entryPathKeys) + '.json';
entryPathKeys.splice(entryPathKeys.length - 1);
const entryFolderPath = path_1.join.apply(this, entryPathKeys);
if ((0, fs_1.existsSync)(entryFolderPath)) {
if ((0, fs_1.existsSync)(entryPath)) {
const data = yield (0, fs_2.readFile)(entryPath, 'utf-8');
const entries = JSON.parse(data);
let entryUnpublished = false;
for (let i = 0, j = entries.length; i < j; i++) {
if (entries[i].uid === entry.uid) {
entries.splice(i, 1);
entryUnpublished = true;
break;
}
}
if (entryUnpublished) {
yield (0, fs_2.writeFile)(entryPath, JSON.stringify(entries));
}
}
}
return resolve(entry);
}
catch (error) {
return reject(error);
}
}));
}
deleteAsset(asset) {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
try {
(0, validations_1.validateEntryAssetDeletedObject)(asset);
const assetPathKeys = (0, index_1.getPathKeys)(this.pattern.assetKeys, asset);
const assetPath = path_1.join.apply(this, assetPathKeys) + '.json';
if ((0, fs_1.existsSync)(assetPath)) {
const data = yield (0, fs_2.readFile)(assetPath, 'utf-8');
const assets = JSON.parse(data);
let assetsRemoved = false;
const bucket = [];
for (let i = 0, j = assets.length; i < j; i++) {
if (assets[i].uid === asset.uid) {
assetsRemoved = true;
bucket.unshift(assets.splice(i, 1)[0]);
i--;
j--;
}
}
if (!assetsRemoved) {
return resolve(asset);
}
this.updateDeletedAssetReferences(asset).catch(reject);
return this.assetStore.delete(bucket)
.then(() => (0, fs_2.writeFile)(assetPath, JSON.stringify(assets)))
.then(() => resolve(asset))
.catch(reject);
}
return resolve(asset);
}
catch (error) {
return reject(error);
}
}));
}
deleteContentType(data) {
return __awaiter(this, void 0, void 0, function* () {
(0, validations_1.validateContentTypeDeletedObject)(data);
const content = yield (0, fs_2.readFile)(this.localePath, 'utf-8');
const locales = JSON.parse(content);
return Promise
.all([this.deleteSchema(data, locales), this.deleteAllEntries(data, locales)])
.then(() => {
return data;
});
});
}
deleteAllEntries(data, locales) {
return __awaiter(this, void 0, void 0, function* () {
const uid = data.uid;
for (let i = 0, j = locales.length; i < j; i++) {
const locale = locales[i];
const deleteContentTypeObject = {
_content_type_uid: uid,
locale,
branch: data.branch
};
const entryPathKeys = (0, index_1.getPathKeys)(this.pattern.entryKeys, deleteContentTypeObject);
const entryPath = path_1.join.apply(this, entryPathKeys) + '.json';
if ((0, fs_1.existsSync)(entryPath)) {
const contents = yield (0, fs_2.readFile)(entryPath, 'utf-8');
const entries = JSON.parse(contents);
for (let k = 0, l = entries.length; k < l; k++) {
if (entries[k]._content_type_uid === uid) {
entries.splice(k, 1);
k--;
l--;
}
}
yield (0, fs_2.writeFile)(entryPath, JSON.stringify(entries));
}
}
return;
});
}
deleteSchema(data, locales) {
return __awaiter(this, void 0, void 0, function* () {
for (let i = 0, j = locales.length; i < j; i++) {
const locale = locales[i];
const deleteContentTypeObject = {
_content_type_uid: '_content_types',
locale,
uid: data.uid,
branch: data.branch
};
const contentTypePathKeys = (0, index_1.getPathKeys)(this.pattern.contentTypeKeys, deleteContentTypeObject);
const contentTypePath = path_1.join.apply(this, contentTypePathKeys) + '.json';
if ((0, fs_1.existsSync)(contentTypePath)) {
const content = yield (0, fs_2.readFile)(contentTypePath, 'utf-8');
const jsonData = JSON.parse(content);
if (jsonData instanceof Array) {
for (let k = 0, m = jsonData.length; k < m; k++) {
if (jsonData[k].uid === data.uid) {
jsonData.splice(k, 1);
break;
}
}
yield (0, fs_2.writeFile)(contentTypePath, JSON.stringify(jsonData));
}
else {
(0, fs_1.unlinkSync)(contentTypePath);
}
}
}
return;
});
}
deleteEntry(entry) {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
try {
(0, validations_1.validateEntryAssetDeletedObject)(entry);
const entryPathKeys = (0, index_1.getPathKeys)(this.pattern.entryKeys, entry);
const entryPath = path_1.join.apply(this, entryPathKeys) + '.json';
if ((0, fs_1.existsSync)(entryPath)) {
const data = yield (0, fs_2.readFile)(entryPath, 'utf-8');
const entries = JSON.parse(data);
let entryDeleted = false;
for (let i = 0, j = entries.length; i < j; i++) {
if (entries[i].uid === entry.uid) {
entries.splice(i, 1);
entryDeleted = true;
break;
}
}
if (entryDeleted) {
yield (0, fs_2.writeFile)(entryPath, JSON.stringify(entries));
}
}
return resolve(entry);
}
catch (error) {
return reject(error);
}
}));
}
publishEntry(data) {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _e;
try {
if ((_a = this._config) === null || _a === void 0 ? void 0 : _a.enableContentReferences) {
this._updateReferenceFields(data, (_b = data._content_type) === null || _b === void 0 ? void 0 : _b.schema, 'reference');
}
if ((_c = this._config) === null || _c === void 0 ? void 0 : _c.enableAssetReferences) {
this._updateAssetFields(data, (_e = data._content_type) === null || _e === void 0 ? void 0 : _e.schema, 'file');
}
let entry = (0, lodash_1.cloneDeep)(data);
entry = (0, index_1.filter)(entry);
const entryPathKeys = (0, index_1.getPathKeys)(this.pattern.entryKeys, entry);
const entryPath = path_1.join.apply(this, entryPathKeys) + '.json';
entryPathKeys.splice(entryPathKeys.length - 1);
const entryFolderPath = path_1.join.apply(this, entryPathKeys);
entry = (0, index_1.removeUnwantedKeys)(this.unwanted.entry, entry);
if ((0, fs_1.existsSync)(entryFolderPath)) {
if ((0, fs_1.existsSync)(entryPath)) {
const contents = yield (0, fs_2.readFile)(entryPath, 'utf-8');
const entries = JSON.parse(contents);
let entryUpdated = false;
for (let i = 0, j = entries.length; i < j; i++) {
if (entries[i].uid === entry.uid && entries[i].locale === entry.locale) {
entries[i] = entry;
entryUpdated = true;
break;
}
}
if (!entryUpdated) {
entries.unshift(entry);
}
yield (0, fs_2.writeFile)(entryPath, JSON.stringify(entries));
}
else {
yield (0, fs_2.writeFile)(entryPath, JSON.stringify([entry]));
}
}
else {
(0, fs_1.mkdirSync)(entryFolderPath, { recursive: true });
yield (0, fs_2.writeFile)(entryPath, JSON.stringify([entry]));
}
return resolve(entry);
}
catch (error) {
return reject(error);
}
}));
}
updateAssetReferences(data, schema) {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
try {
const fileFieldPaths = (0, get_file_fields_1.getFileFieldPaths)(schema);
const assetPathKeys = (0, index_1.getPathKeys)(this.pattern.assetKeys, { locale: data.publish_details ? data.publish_details.locale : data.locale, branch: data.branch });
assetPathKeys.splice(assetPathKeys.length - 1);
const assetFolderPath = path_1.join.apply(this, assetPathKeys);
let assetMap = yield (0, fs_2.readFile)(assetFolderPath + '/asset_map.json', 'utf-8');
try {
assetMap = JSON.parse(assetMap);
if (Array.isArray(assetMap)) {
assetMap = {};
}
}
catch (error) {
assetMap = {};
}
const entryData = {
uid: data.uid,
contentTypeUid: data._content_type_uid,
locale: data.publish_details ? data.publish_details.locale : data.locale,
branch: data.branch,
};
for (const fileFieldPath of fileFieldPaths) {
this._getAssetFieldsHelper(data, fileFieldPath.split('.'), 0, assetMap, entryData);
}
yield (0, fs_2.writeFile)(assetFolderPath + '/asset_map.json', JSON.stringify(assetMap));
return resolve(assetFolderPath);
}
catch (error) {
return reject(error);
}
}));
}
updateDeletedAssetReferences(asset) {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
try {
const assetPathKeys = (0, index_1.getPathKeys)(this.pattern.assetKeys, { locale: asset.publish_details ? asset.publish_details.locale : asset.locale, branch: asset.branch });
assetPathKeys.splice(assetPathKeys.length - 1);
const assetFolderPath = path_1.join.apply(this, assetPathKeys);
let assetMap = yield (0, fs_2.readFile)(assetFolderPath + '/asset_map.json', 'utf-8');
try {
assetMap = JSON.parse(assetMap);
if (Array.isArray(assetMap)) {
assetMap = {};
}
}
catch (error) {
assetMap = {};
}
if (!assetMap[asset.uid]) {
return resolve({});
}
if (!this.config.preserveAssetInReferencedEntries) {
const entries = assetMap[asset.uid];
for (const entry of entries) {
yield this._updateEntryAssetReference(entry, asset.uid);
}
}
resolve({});
}
catch (error) {
return reject(error);
}
}));
}
_getAssetFieldsHelper(data, fileFieldPathArray, index, assetFieldMap, entryData) {
if (fileFieldPathArray.length <= index) {
if (Array.isArray(data)) {
for (const d of data) {
if (!assetFieldMap[d]) {
assetFieldMap[d] = [];
}
assetFieldMap[d].push(Object.assign({ path: fileFieldPathArray }, entryData));
}
}
else {
if (!assetFieldMap[data]) {
assetFieldMap[data] = [];
}
assetFieldMap[data].push(Object.assign({ path: fileFieldPathArray }, entryData));
}
return;
}
if (Array.isArray(data)) {
for (const d of data) {
this._getAssetFieldsHelper(d, fileFieldPathArray, index + 1, assetFieldMap, entryData);
}
}
else {
if (data[fileFieldPathArray[index]]) {
this._getAssetFieldsHelper(data[fileFieldPathArray[index]], fileFieldPathArray, index + 1, assetFieldMap, entryData);
}
}
}
_updateEntryAssetReference(data, assetId) {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
try {
const entryPathKeys = (0, index_1.getPathKeys)(this.pattern.entryKeys, Object.assign(Object.assign({}, data), { _content_type_uid: data.contentTypeUid }));
const entryFolderPath = path_1.join.apply(this, entryPathKeys);
let entries = yield (0, fs_2.readFile)(entryFolderPath + '.json', 'utf-8');
entries = JSON.parse(entries);
let _entry;
for (const entry of entries) {
if (entry.uid === data.uid) {
_entry = entry;
break;
}
}
if (_entry) {
this._nullifyDeletedAssetField(_entry, data.path, 0, assetId);
yield (0, fs_2.writeFile)(entryFolderPath + '.json', JSON.stringify(entries));
}
return resolve(entries);
}
catch (error) {
return reject(error);
}
}));
}
_nullifyDeletedAssetField(data, fileFieldPathArray, index, assetId) {
if (fileFieldPathArray.length - 1 <= index) {
if (Array.isArray(data[fileFieldPathArray[index]])) {
const _d = data[fileFieldPathArray[index]];
for (let i = 0; i < _d.length; i++) {
if (_d[i] === assetId) {
_d[i] = null;
}
}
}
else {
if (data[fileFieldPathArray[index]] === assetId) {
data[fileFieldPathArray[index]] = null;
}
}
return;
}
if (Array.isArray(data)) {
for (const d of data) {
this._nullifyDeletedAssetField(d, fileFieldPathArray, index + 1, assetId);
}
}
else {
if (data[fileFieldPathArray[index]]) {
this._nullifyDeletedAssetField(data[fileFieldPathArray[index]], fileFieldPathArray, index + 1, assetId);
}
}
}
_updateReferenceFields(data, ctSchema, fieldType) {
var _a, _b, _c;
for (const field of ctSchema) {
if (field.data_type === fieldType) {
data[field.uid] = {
reference_to: (_a = field.reference_to) === null || _a === void 0 ? void 0 : _a[0],
value: (_c = (_b = data[field.uid]) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.uid,
};
}
if (field.schema) {
this._updateReferenceFields(data[field.uid], field.schema, fieldType);
}
}
}
_updateAssetFields(data, ctSchema, fieldType) {
for (const field of ctSchema) {
if (field.data_type === fieldType) {
data[field.uid] = {
reference_to: "_assets",
value: data[field.uid],
};
}
if (field.schema) {
this._updateAssetFields(data[field.uid], field.schema, fieldType);
}
}
}
}
exports.FilesystemStore = FilesystemStore;