-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PRO-6418: parked pages & singletons #74
Changes from 15 commits
c3df677
f454400
5fe1865
2d923fa
8147167
a3795da
efef3d7
1154009
4695135
6ae3548
0f95acc
673242b
6e9b73a
85a5faf
52adc9c
3e8e26b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,13 @@ | ||
{ | ||
"extends": "stylelint-config-apostrophe" | ||
"extends": "stylelint-config-apostrophe", | ||
"overrides": [ | ||
{ | ||
"files": ["*.vue", "**/*.vue"], | ||
"customSyntax": "postcss-html" | ||
}, | ||
{ | ||
"files": ["*.s{c,a}ss", "**/*.s{c,a}ss"], | ||
"customSyntax": "postcss-scss" | ||
} | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -279,9 +279,13 @@ module.exports = self => { | |
reporting, docs, failedIds | ||
}) { | ||
const docIds = []; | ||
const replaceItems = []; | ||
const aposLocale = `${req.locale}:draft`; | ||
const alreadyFailed = [ ...failedIds ]; | ||
|
||
for (const { type, aposDocId } of docs) { | ||
for (const { | ||
type, aposDocId, parkedId, title, updatedAt, aposMode, aposLocale | ||
} of docs) { | ||
if (alreadyFailed.includes(aposDocId)) { | ||
continue; | ||
} | ||
|
@@ -290,12 +294,38 @@ module.exports = self => { | |
reporting.failure(); | ||
continue; | ||
} | ||
|
||
const isSingleton = self.apos.modules[type] && | ||
self.apos.modules[type].options.singleton === true; | ||
|
||
// Mark singleton and parked docs as "replace" items. | ||
// Be sure to always continue the loop to avoid duplicates. | ||
// If the parked/singleton doc is not found, it should be processed as a | ||
// regular doc. | ||
if (isSingleton || parkedId) { | ||
if (aposMode !== 'draft' || !aposDocId) { | ||
continue; | ||
} | ||
const singletonAposDocId = await self.findExistingSingletonId({ | ||
type, | ||
aposLocale | ||
}); | ||
if (singletonAposDocId) { | ||
replaceItems.push({ | ||
aposDocId, | ||
title, | ||
type, | ||
updatedAt, | ||
replaceId: singletonAposDocId | ||
}); | ||
continue; | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Optional]: Maybe we could have used a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've tried many variants, including trying to extract the whole logic in a function. The real problem is the complex A better design is to move the whole logic (for a single document) in a separate method. This would "relax" the code depth and open possibilities for improvements. I don't think we are still there/forced to do this effort yett. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes it's good for me. |
||
if (aposDocId) { | ||
docIds.push(aposDocId); | ||
} | ||
} | ||
|
||
const aposLocale = `${req.locale}:draft`; | ||
const criteria = { | ||
$and: [ | ||
{ aposDocId: { $in: docIds } }, | ||
|
@@ -322,6 +352,13 @@ module.exports = self => { | |
updatedAt | ||
})); | ||
|
||
// Append the replace items to the duplicate lists, so that we can | ||
// get confirmation from the user to replace them | ||
for (const item of replaceItems) { | ||
duplicatedIds.add(item.aposDocId); | ||
duplicatedDocs.push(item); | ||
} | ||
|
||
return { | ||
duplicatedIds, | ||
duplicatedDocs | ||
|
@@ -368,31 +405,9 @@ module.exports = self => { | |
reporting.success(); | ||
} | ||
} catch (error) { | ||
if (!self.apos.doc.isUniqueError(error)) { | ||
reporting.failure(); | ||
failedIds.push(cloned.aposDocId); | ||
self.apos.util.error(error); | ||
continue; | ||
} | ||
|
||
// TODO: We should ask user too if he want to override singleton docs | ||
// This logic might be useless since we pre check duplicates | ||
const isSingleton = self.apos.modules[cloned.type] && | ||
self.apos.modules[cloned.type].options.singleton === true; | ||
|
||
if (isSingleton) { | ||
try { | ||
await self.insertOrUpdateDoc(req, { | ||
doc: cloned, | ||
method: 'update', | ||
failedIds | ||
}); | ||
reporting.success(); | ||
} catch (err) { | ||
reporting.failure(); | ||
failedIds.push(cloned.aposDocId); | ||
} | ||
} | ||
reporting.failure(); | ||
failedIds.push(cloned.aposDocId); | ||
self.apos.util.error(error); | ||
} | ||
} | ||
}, | ||
|
@@ -546,9 +561,12 @@ module.exports = self => { | |
}, | ||
|
||
async insertOrUpdateDoc(req, { | ||
doc, method = 'insert', failedIds = [] | ||
doc, method = 'insert', failedIds = [], existingAposDocId | ||
}) { | ||
const manager = self.apos.doc.getManager(doc.type); | ||
if (existingAposDocId) { | ||
method = 'update'; | ||
} | ||
|
||
// Import can be disable at the page-type level | ||
if (!self.canImport(req, doc.type)) { | ||
|
@@ -571,6 +589,7 @@ module.exports = self => { | |
} | ||
} | ||
|
||
await self.replaceDocId(doc, existingAposDocId); | ||
self.handleRichTextFields(manager, doc); | ||
|
||
if (method === 'insert') { | ||
|
@@ -686,10 +705,13 @@ module.exports = self => { | |
const overrideLocale = self.apos.launder.boolean(req.body.overrideLocale); | ||
const exportPath = await self.getExportPathById(self.apos.launder.string(req.body.exportPathId)); | ||
const docIds = self.apos.launder.strings(req.body.docIds); | ||
// Import aposDocId/Existing aposDocId pairs | ||
const replaceAposDocIds = req.body.replaceDocIds?.map(arr => self.apos.launder.strings(arr)) ?? []; | ||
const jobId = self.apos.launder.string(req.body.jobId); | ||
const importedAttachments = self.apos.launder.strings(req.body.importedAttachments); | ||
const formatLabel = self.apos.launder.string(req.body.formatLabel); | ||
const failedIds = []; | ||
const replaceDocIdPairs = []; | ||
|
||
const jobManager = self.apos.modules['@apostrophecms/job']; | ||
const job = await jobManager.db.findOne({ _id: jobId }); | ||
|
@@ -743,12 +765,22 @@ module.exports = self => { | |
} | ||
} | ||
|
||
const [ , replaceAposDocId ] = replaceAposDocIds | ||
.find(([ importId ]) => importId === doc.aposDocId) ?? []; | ||
await self.insertOrUpdateDoc(req, { | ||
doc, | ||
method: 'update', | ||
failedIds | ||
failedIds, | ||
existingAposDocId: replaceAposDocId | ||
}); | ||
|
||
if (replaceAposDocId) { | ||
replaceDocIdPairs.push([ | ||
doc._id, | ||
`${replaceAposDocId}:${doc.aposLocale}` | ||
]); | ||
} | ||
|
||
jobManager.success(job); | ||
} catch (err) { | ||
jobManager.failure(job); | ||
|
@@ -758,6 +790,10 @@ module.exports = self => { | |
} | ||
} | ||
|
||
if (replaceDocIdPairs.length) { | ||
await self.apos.doc.changeDocIds(replaceDocIdPairs, { skipReplace: true }); | ||
} | ||
|
||
if (failedIds.length) { | ||
await self.apos.notify(req, 'aposImportExport:importFailedForSome', { | ||
interpolate: { | ||
|
@@ -804,6 +840,68 @@ module.exports = self => { | |
} | ||
|
||
await self.remove(exportPath); | ||
}, | ||
|
||
// This methods expects a singleton type and it doesn't | ||
// perform any related checks. It's up to the caller to | ||
// make sure the singleton exists. | ||
// Singletons are parked pages (having parkedId property) and | ||
// pieces having option `singleton: true`. | ||
async findExistingSingletonId({ type, aposLocale }) { | ||
const singleton = await self.apos.doc.db.findOne({ | ||
$and: [ | ||
{ type }, | ||
{ | ||
$or: [ | ||
{ aposLocale: { $exists: false } }, | ||
{ aposLocale } | ||
] | ||
} | ||
] | ||
}, { | ||
projection: { aposDocId: 1 } | ||
}); | ||
|
||
if (singleton && singleton.aposDocId) { | ||
return singleton.aposDocId; | ||
} | ||
|
||
return null; | ||
}, | ||
|
||
// Replace ID fields of imported document with a new ID, | ||
// usually the ID of an existing document in the database. | ||
// This methods shouldn't be "perfect" as `apos.doc.changeDocIds` | ||
// will be called at the end of the import process to replace | ||
// all the IDs and update relationships. | ||
async replaceDocId(doc, replaceId) { | ||
if (!replaceId) { | ||
return doc; | ||
} | ||
doc._id = `${replaceId}:${doc.aposLocale}`; | ||
doc.aposDocId = replaceId; | ||
|
||
const manager = self.apos.doc.getManager(doc.type); | ||
if (!manager || !self.isPage(manager)) { | ||
return doc; | ||
} | ||
|
||
const existing = await self.apos.doc.db.findOne({ | ||
_id: doc._id | ||
}, { | ||
projection: { | ||
path: 1, | ||
rank: 1, | ||
level: 1 | ||
} | ||
}); | ||
if (existing) { | ||
doc.path = existing.path; | ||
doc.rank = existing.rank; | ||
doc.level = existing.level; | ||
} | ||
|
||
return doc; | ||
} | ||
}; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not be needed, it already exists in
stylelint-config-apostrophe
. No extra config should be needed to use the same config in each project/There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Example error this fix:
Same when removing it:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok this is because you don't use the latest version of our stylelint config.
Please upgrade to
4.1
and remove the added config.