Skip to content
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 6419 import pages #79

Merged
merged 43 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
f7fd8df
Add export batch operation for pages
myovchev Aug 21, 2024
6b1aac7
when overriding page keep position info of the existing one
ValJed Aug 28, 2024
99f5247
add tests for import pages from a tree
haroun Aug 29, 2024
42bdef8
do not commit test public files
haroun Aug 29, 2024
4a2087c
cleanup before import
haroun Aug 29, 2024
722d5db
add missing dependency
haroun Aug 30, 2024
3692c95
add todos
haroun Aug 30, 2024
c626e94
polish assertions
haroun Aug 30, 2024
98ad75f
use import-page
haroun Aug 30, 2024
b7df1dd
remove form-data
haroun Aug 30, 2024
81e4e75
use apostrophe batch pages branch
haroun Aug 30, 2024
563e1ea
clean lint errors
haroun Aug 30, 2024
434c2fe
retrieve target
haroun Sep 2, 2024
dbfe6c9
test import twice and handling of parked pages
haroun Sep 2, 2024
c3c3216
Resolve conflicts
myovchev Sep 2, 2024
53bd65d
Add computed documents types
myovchev Sep 2, 2024
60c26e7
Ensure unique types
myovchev Sep 3, 2024
7586943
Add batch label for notificaitons
myovchev Sep 3, 2024
69798d4
Changelog
myovchev Sep 3, 2024
05d4afb
support parked pages
haroun Sep 3, 2024
a46bf26
Fix missing module action
myovchev Sep 3, 2024
3315330
Fix import labels for singleton pieces
myovchev Sep 3, 2024
173ffe1
override duplicates needs duplicated docs
haroun Sep 3, 2024
507bafd
Merge branch 'pro-6436-batch-operation-page' into pro-6419-import-pages
haroun Sep 3, 2024
bd044f2
fix lint issues
haroun Sep 3, 2024
d31e191
bring back doc ids
haroun Sep 3, 2024
998e3cf
keep slug from import file
haroun Sep 3, 2024
e40406d
fix rank values
haroun Sep 4, 2024
60969a0
remove branch
haroun Sep 4, 2024
7055681
Add replaces configuration for singleton import menu
myovchev Sep 4, 2024
a354598
fix typo with options, remove logs from tests
haroun Sep 4, 2024
5c7e539
do not throw error if we can not dismiss job notification
haroun Sep 4, 2024
09844b2
remove replaceDocIds
haroun Sep 4, 2024
274c153
run two separate suite for remove
haroun Sep 4, 2024
b2ee0bf
restore correct existing apos doc id
haroun Sep 4, 2024
b7a0239
revert mocks
haroun Sep 4, 2024
a4ff488
revert changes to test/index.js
haroun Sep 4, 2024
832e774
Merge branch 'pro-6436-batch-operation-page' into pro-6419-import-pages
haroun Sep 4, 2024
45dd288
do not try to replace existing attachments, just merge crops and use …
boutell Sep 4, 2024
1f5772b
keep attachment name
haroun Sep 5, 2024
828efa9
Merge branch 'main' into pro-6419-import-pages
haroun Sep 5, 2024
1ee1378
Merge branch 'pro-6337' into pro-6419-import-pages
haroun Sep 5, 2024
5b151a0
Merge branch 'main' into pro-6419-import-pages
haroun Sep 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,10 @@ node_modules

# vim swp files
.*.sw*

# Dont commit test generated css
test/public/css/*.css
test/public/css/master-*.less

# Dont commit test uploads
test/public/uploads
94 changes: 94 additions & 0 deletions lib/methods/import-page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
const getTargetId = async ({
manager,
doc,
req,
duplicatedDocs = []
}) => {
if (doc.archived) {
return '_archive';
}

const duplicatedDocsMapping = Object.fromEntries(
duplicatedDocs.map(duplicate => [ duplicate.aposDocId, duplicate.replaceId ])
);

const { path = '' } = doc;
const ancestorIds = path.split('/').reverse().slice(1);
for (const ancestorId of ancestorIds) {
try {
const { aposDocId } = await manager.getTarget(req, duplicatedDocsMapping[ancestorId] || ancestorId);

return aposDocId;
} catch (error) {
// continue search
}
}

return '_home';
};

const insert = async ({
manager,
doc,
req,
duplicatedDocs
}) => {
const targetId = await getTargetId({
manager,
doc,
req,
duplicatedDocs
});
const position = 'lastChild';

return manager.insert(
req,
targetId,
position,
doc,
{ setModified: false }
);
};

const update = async ({
manager,
doc,
req,
duplicatedDocs
}) => {
const {
_id,
aposDocId,
path,
rank,
level,
...patch
} = doc;

const move = doc.parkedId
? {}
: {
_targetId: await getTargetId({
manager,
doc,
req,
duplicatedDocs
}),
_position: 'lastChild'
};

return manager.patch(
req.clone({
body: {
...patch,
...move
}
}),
_id
);
};

module.exports = {
insert,
update
};
121 changes: 97 additions & 24 deletions lib/methods/import.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { cloneDeep } = require('lodash');
const importPage = require('./import-page.js');

module.exports = self => {
return {
Expand Down Expand Up @@ -127,6 +128,7 @@
docs,
reporting,
duplicatedIds,
duplicatedDocs,
failedIds
});

Expand Down Expand Up @@ -303,23 +305,33 @@
// If the parked/singleton doc is not found, it should be processed as a
// regular doc.
if (isSingleton || parkedId) {
// TODO: what about singleton with localized = false, we don't have aposMode
// TODO: do I need that for parked page?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is a problem. aposMode in my knowledge always exists (no matter localized or not). We check for draft here because we want to trigger this (expensive) code only once per unique page (aposDocId).

if (aposMode !== 'draft' || !aposDocId) {
continue;
}
const singletonAposDocId = await self.findExistingSingletonId({
}
const replaceId = isSingleton
? await self.findSingletonAposDocId({
type,
aposLocale
});
if (singletonAposDocId) {
replaceItems.push({
aposDocId,
title,
})
: parkedId
? await self.findParkedAposDocId({
parkedId,
type,
updatedAt,
replaceId: singletonAposDocId
});
continue;
}
aposLocale
})
: null;
if (replaceId) {
replaceItems.push({
aposDocId,
title,
type,
updatedAt,
replaceId
});
continue;
}
if (aposDocId && !docIds.includes(aposDocId)) {
docIds.push(aposDocId);
Expand Down Expand Up @@ -366,7 +378,7 @@
},

async insertDocs(req, {
docs, reporting, duplicatedIds, failedIds
docs, reporting, duplicatedIds, duplicatedDocs, failedIds
}) {
for (const doc of docs) {
if (duplicatedIds.has(doc.aposDocId) || failedIds.includes(doc.aposDocId)) {
Expand All @@ -382,7 +394,8 @@
await self.insertOrUpdateDocWithKey(req, {
doc,
updateKey,
updateField
updateField,
duplicatedDocs
});
reporting.success();
} catch (error) {
Expand All @@ -399,7 +412,8 @@
try {
const inserted = await self.insertOrUpdateDoc(req, {
doc,
failedIds
failedIds,
duplicatedDocs
});
if (inserted) {
reporting.success();
Expand All @@ -415,7 +429,8 @@
async insertOrUpdateDocWithKey(req, {
doc,
updateKey,
updateField
updateField,
duplicatedDocs = []
}) {
const manager = self.apos.doc.getManager(doc.type);
if (!self.canImport(req, doc.type)) {
Expand Down Expand Up @@ -470,11 +485,17 @@
});

if (self.isPage(manager)) {
// TODO: check if this is still true
// `convert` sets the type to `@apostrophecms/home-page`,
// let's set it back to the original type:
docToInsert.type = type;

return self.apos.page.insert(_req, '_home', 'lastChild', docToInsert, { setModified: false });
return importPage.insert({
manager: self.apos.page,
doc: docToInsert,
req: _req,
duplicatedDocs
});
}

return manager.insert(_req, docToInsert, { setModified: false });
Expand Down Expand Up @@ -506,7 +527,14 @@
const _req = req.clone({ mode: docToUpdate.aposMode });

await manager.convert(_req, doc, docToUpdate, { presentFieldsOnly: true });
await manager.update(_req, docToUpdate);
self.isPage(manager)
? await importPage.update({
manager: self.apos.page,
doc: docToUpdate,
req: _req,
duplicatedDocs
})
: await manager.update(_req, docToUpdate);
}

await self.setDocAsNotModified(matchingDraft);
Expand Down Expand Up @@ -561,7 +589,7 @@
},

async insertOrUpdateDoc(req, {
doc, method = 'insert', failedIds = [], existingAposDocId
doc, method = 'insert', failedIds = [], duplicatedDocs = [], existingAposDocId
}) {
const manager = self.apos.doc.getManager(doc.type);
if (existingAposDocId) {
Expand Down Expand Up @@ -609,11 +637,17 @@
});

if (self.isPage(manager)) {
// TODO: check if this is still true
// `convert` sets the type to `@apostrophecms/home-page`,
// let's set it back to the original type:
docToInsert.type = type;

return self.apos.page.insert(_req, '_home', 'lastChild', docToInsert, { setModified: false });
return importPage.insert({
manager: self.apos.page,
doc: docToInsert,
req: _req,
duplicatedDocs
});
}

return manager.insert(_req, docToInsert, { setModified: false });
Expand All @@ -628,10 +662,18 @@
});
if (existingDoc) {
doc.lastPublishedAt = existingDoc.lastPublishedAt;

}
}

await manager.update(_req, doc);
self.isPage(manager)
? await importPage.update({
manager: self.apos.page,
doc,
req: _req,
duplicatedDocs
})
: await manager.update(_req, doc);

if (doc.aposMode === 'draft') {
await self.setDocAsNotModified(doc);
Expand All @@ -646,7 +688,8 @@
// `self.apos.page.isPage` does not work here since
// the slug can be omitted in a CSV or Excel file.
isPage(manager) {
return manager.__meta.chain.some(module => module.name === '@apostrophecms/page-type');
return self.apos.instanceOf(manager, '@apostrophecms/page-type') ||
self.apos.instanceOf(manager, '@apostrophecms/any-page-type');
},

// Manually set `modified: false` because `setModified`
Expand Down Expand Up @@ -704,15 +747,23 @@
async overrideDuplicates(req) {
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 duplicatedDocs = req.body.duplicatedDocs?.map(duplicatedDoc => {
return {
aposDocId: self.apos.launder.string(duplicatedDoc.aposDocId),
title: self.apos.launder.string(duplicatedDoc.title),
type: self.apos.launder.string(duplicatedDoc.type),
updatedAt: self.apos.launder.date(duplicatedDoc.updatedAt)
};
}) ?? [];
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 docIds = duplicatedDocs.map(duplicatedDoc => duplicatedDoc.aposDocId);
const jobManager = self.apos.modules['@apostrophecms/job'];
const job = await jobManager.db.findOne({ _id: jobId });

Expand Down Expand Up @@ -773,7 +824,8 @@
doc,
method: 'update',
failedIds,
existingAposDocId: replaceAposDocId
existingAposDocId: replaceAposDocId,
duplicatedDocs
});

if (replaceAposDocId) {
Expand Down Expand Up @@ -849,7 +901,7 @@
// make sure the singleton exists.
// Singletons are parked pages (having parkedId property) and
// pieces having option `singleton: true`.
async findExistingSingletonId({ type, aposLocale }) {
async findSingletonAposDocId({ type, aposLocale }) {
const singleton = await self.apos.doc.db.findOne({
$and: [
{ type },
Expand All @@ -870,6 +922,27 @@

return null;
},
async findParkedAposDocId({ parkedId, type, aposLocale }) {

Check warning on line 925 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (20, 6.0)

Expected a line break after this opening brace

Check warning on line 925 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (20, 6.0)

Expected a line break before this closing brace

Check warning on line 925 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (20, 5.0)

Expected a line break after this opening brace

Check warning on line 925 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (20, 5.0)

Expected a line break before this closing brace

Check warning on line 925 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (20, 4.4)

Expected a line break after this opening brace

Check warning on line 925 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (20, 4.4)

Expected a line break before this closing brace

Check warning on line 925 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (18, 6.0)

Expected a line break after this opening brace

Check warning on line 925 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (18, 6.0)

Expected a line break before this closing brace

Check warning on line 925 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (18, 4.4)

Expected a line break after this opening brace

Check warning on line 925 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (18, 4.4)

Expected a line break before this closing brace
const parked = await self.apos.doc.db.findOne({
$and: [
{ parkedId, type },

Check warning on line 928 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (20, 6.0)

Expected a line break after this opening brace

Check warning on line 928 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (20, 6.0)

Object properties must go on a new line

Check warning on line 928 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (20, 6.0)

Expected a line break before this closing brace

Check warning on line 928 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (20, 5.0)

Expected a line break after this opening brace

Check warning on line 928 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (20, 5.0)

Object properties must go on a new line

Check warning on line 928 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (20, 5.0)

Expected a line break before this closing brace

Check warning on line 928 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (20, 4.4)

Expected a line break after this opening brace

Check warning on line 928 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (20, 4.4)

Object properties must go on a new line

Check warning on line 928 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (20, 4.4)

Expected a line break before this closing brace

Check warning on line 928 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (18, 6.0)

Expected a line break after this opening brace

Check warning on line 928 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (18, 6.0)

Object properties must go on a new line

Check warning on line 928 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (18, 6.0)

Expected a line break before this closing brace

Check warning on line 928 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (18, 4.4)

Expected a line break after this opening brace

Check warning on line 928 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (18, 4.4)

Object properties must go on a new line

Check warning on line 928 in lib/methods/import.js

View workflow job for this annotation

GitHub Actions / build (18, 4.4)

Expected a line break before this closing brace
{
$or: [
{ aposLocale: { $exists: false } },
{ aposLocale }
]
}
]
}, {
projection: { aposDocId: 1 }
});

if (parked && parked.aposDocId) {
return parked.aposDocId;
}

return null;
},

// Replace ID fields of imported document with a new ID,
// usually the ID of an existing document in the database.
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@
"author": "Apostrophe Technologies",
"license": "UNLICENSED",
"devDependencies": {
"apostrophe": "github:apostrophecms/apostrophe",
"apostrophe": "github:apostrophecms/apostrophe#pro-6425-batch-operation-page",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs to be changed

"eslint": "^8.44.0",
"eslint-config-apostrophe": "^4.2.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-vue": "^9.19.2",
"form-data": "^4.0.0",
"mocha": "^10.2.0",
"stylelint": "^16.0.0",
"stylelint-config-apostrophe": "^4.1.0",
Expand Down
Loading
Loading