Skip to content

Commit 4bcaf7c

Browse files
committed
Use models in confluence-syncer
1 parent 5a9906d commit 4bcaf7c

4 files changed

Lines changed: 148 additions & 503 deletions

File tree

dist/index.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/confluence-syncer.js

Lines changed: 53 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
import path from 'node:path';
21
import context from './context.js';
32
import config from './config.js';
43
import logger from './logger.js';
54
import ConfluenceSDK from './confluence-sdk.js';
6-
import { Image, Graph, Meta } from '../lib/models/index.js';
5+
import { Meta, LocalPage } from '../lib/models/index.js';
76
import AssetRenderer from './renderers/asset-renderer.js';
87

98
const confluence = new ConfluenceSDK(config.confluence);
10-
const [majorVer, minorVer] = config.version.split('.');
119

10+
/**
11+
* Sync local markdown documentation with Confluence
12+
*/
1213
export async function sync() {
1314
try {
1415
const { siteName, repo, pages: localPages, readMe, pageRefs } = context.getContext();
@@ -44,53 +45,47 @@ function errorHandler(error) {
4445
/**
4546
* Create or update home page from README.md
4647
*
47-
* @typedef {import('../lib/models/local-page.js').default} LocalPage
4848
* @param {string} repo
4949
* @param {string} siteName
50-
* @param {LocalPage} readMe
50+
* @param {LocalPage} localPage
5151
* @param {AssetRenderer} renderer
52-
* @returns {number} Home page id
52+
* @returns {Promise<number>} Home page id
5353
*/
54-
async function syncHome(repo, siteName, readMe, renderer) {
55-
const parentPage = await findParentPage();
56-
const homeTitle = siteName;
57-
let homeHtml = `<h1>${siteName}</h1>`;
58-
let attachments = [];
59-
let homeMeta = new Meta(repo);
60-
if (readMe) {
61-
readMe.render(renderer);
62-
homeMeta = readMe.meta;
63-
homeHtml = readMe.html;
64-
attachments = readMe.attachments;
54+
async function syncHome(repo, siteName, localPage, renderer) {
55+
if (!localPage) {
56+
localPage = new LocalPage(siteName, new Meta(repo));
57+
localPage.html = `<h1>${siteName}</h1>`;
6558
}
66-
const existingPage = await confluence.findPage(homeTitle);
67-
if (existingPage) {
68-
// check if repo matches
69-
if (existingPage.meta.repo !== homeMeta.repo) {
70-
throw new Error(`Page "${homeTitle}" already exist for another repo "${existingPage.meta.repo}"`);
71-
}
72-
if (config.confluence.forceUpdate || versionChange(existingPage) || existingPage.meta.sha !== homeMeta.sha) {
73-
await confluence.updatePage(existingPage.id, existingPage.version + 1, homeTitle, homeHtml, parentPage, homeMeta);
74-
await createAttachments(existingPage.id, attachments, renderer);
59+
localPage.parentPageId = await findParentPage();
60+
let homePage = localPage;
61+
const remotePage = await confluence.findPage(siteName);
62+
if (remotePage) {
63+
homePage = remotePage;
64+
homePage.localPage = localPage;
65+
// check for potential repo conflict
66+
if (homePage.repoConflict()) {
67+
throw new Error(`Page "${siteName}" already exist for another repo "${homePage.meta.repo}"`);
7568
}
76-
return existingPage.id;
77-
} else {
78-
const pageId = await confluence.createPage(homeTitle, homeHtml, parentPage, homeMeta);
79-
await createAttachments(pageId, attachments, renderer);
80-
return pageId;
8169
}
70+
return homePage.sync(renderer, confluence).then(page => page.id);
8271
}
8372

73+
/**
74+
* Find the `id` of the Confluence page Configured to be the parent for our documents
75+
*
76+
* @returns {number} The `id` of the configured parent page
77+
* @throws `Error` if the configured parent page does not exist
78+
*/
8479
async function findParentPage() {
8580
const title = config.confluence.parentPage;
86-
let parentPage = null;
87-
if (title) {
88-
parentPage = await confluence.findPage(title);
89-
if (!parentPage) {
90-
throw new Error(`The page configured as parent (${title}) does not exist in confluence`);
91-
}
81+
if (!title) {
82+
return;
83+
}
84+
const parentPage = await confluence.findPage(title);
85+
if (!parentPage) {
86+
throw new Error(`The page configured as parent (${title}) does not exist in confluence`);
9287
}
93-
return parentPage?.id;
88+
return parentPage.id;
9489
}
9590

9691
/**
@@ -101,50 +96,10 @@ async function findParentPage() {
10196
* @param {AssetRenderer} renderer - `AssetRenderer` instance
10297
*/
10398
async function syncPages(home, localPages, renderer) {
104-
// get children of home
105-
const remotePages = await confluence.getChildPages(home);
106-
// compute diff between remote and local pages
107-
const differences = diff(localPages, remotePages);
108-
// delete removed pages
109-
await unpublish(differences.delete);
110-
// update changed pages
111-
await update(differences.update, home, renderer);
112-
// create added pages
113-
await create(differences.create, home, renderer);
114-
}
115-
116-
/**
117-
*
118-
* @param {number} home - The id of the home page
119-
* @param {Array<LocalPage>} localPages - Array of pages to be created
120-
* @param {AssetRenderer} renderer - `AssetRenderer` instance
121-
*/
122-
async function create(localPages, home, renderer) {
123-
for (let page of localPages) {
124-
page.render(renderer);
125-
const pageId = await confluence.createPage(page.title, page.html, home, page.meta);
126-
await createAttachments(pageId, page.attachments, renderer);
127-
logger.debug(`Created Page: [${pageId}] ${page.title}`);
128-
}
129-
}
130-
131-
/**
132-
*
133-
* @param {number} home - The id of the home page
134-
* @param {Array<LocalPage>} localPages - Array of pages to be updated
135-
* @param {AssetRenderer} renderer - `AssetRenderer` instance
136-
*/
137-
async function update(localPages, home, renderer) {
138-
for (let page of localPages) {
139-
page.render(renderer);
140-
await createAttachments(page.id, page.attachments, renderer);
141-
await confluence
142-
.updatePage(page.id, page.version, page.title, page.html, home, page.meta)
143-
.then(() => {
144-
logger.debug(
145-
`Updated Page: [${page.id}] ${page.title} v${page.version}`
146-
);
147-
});
99+
// compute the union of local/remote pages that need to be synced
100+
const pages = await union(home, localPages);
101+
for (let page of pages) {
102+
await page.sync(renderer, confluence);
148103
}
149104
}
150105

@@ -156,77 +111,34 @@ async function unpublish(remotePages) {
156111
}
157112
}
158113

159-
async function createAttachments(pageId, attachments, renderer) {
160-
for (const attachment of attachments) {
161-
if (attachment instanceof Image) {
162-
await createImage(pageId, attachment);
163-
} else if (attachment instanceof Graph) {
164-
await createGraph(pageId, attachment, renderer);
165-
}
166-
}
167-
}
168-
169-
async function createImage(pageId, image) {
170-
await confluence.createAttachment(pageId, path.resolve(image.path));
171-
logger.debug(`Attached image "${image.path}" to page #${pageId}`);
172-
}
173-
174114
/**
115+
* @typedef {import('../lib/models/remote-page.js').default} RemotePage
175116
*
176-
* @param {number} pageId - The id of the page to attach the graph
177-
* @param {Graph} graph - The graph to be created
178-
* @param {AssetRenderer} renderer
117+
* @param {number} parentPageId - The parent page to all pages
118+
* @param {Array<LocalPage>} localPages
119+
* @returns {Array<LocalPage|RemotePage>} An `array` of pages to be synced
179120
*/
180-
async function createGraph(pageId, graph, renderer) {
181-
const attachment = await graph.render(renderer);
182-
if (!attachment) {
183-
logger.warn(`Graph "${graph.path}" for page #${pageId} could not be processed`);
184-
return;
185-
}
186-
await confluence.createAttachment(pageId, path.resolve(attachment));
187-
logger.debug(`Attached ${graph.type} graph "${graph.path}" to page #${pageId}`);
188-
}
189-
190-
function diff(localPages, remotePages) {
191-
const results = {
192-
create: [],
193-
update: [],
194-
delete: []
195-
};
196-
121+
async function union(parentPageId, localPages) {
122+
const remotePages = await confluence.getChildPages(parentPageId);
123+
const union = [];
197124
for (let localPage of localPages) {
125+
localPage.parentPageId = parentPageId;
198126
const remotePage = remotePages.get(localPage.meta.path);
199127
if (!remotePage) {
200-
// not exist on remote -> create
201-
results.create.push(localPage);
202-
} else {
203-
remotePages.delete(localPage.meta.path);
204-
// if forceUpdate or exist with different sha, then update the page
205-
if (config.confluence.forceUpdate || versionChange(remotePage) || localPage.meta.sha !== remotePage.meta.sha) {
206-
localPage.id = remotePage.id;
207-
localPage.version = remotePage.version + 1;
208-
results.update.push(localPage);
209-
}
128+
union.push(localPage);
129+
continue;
210130
}
131+
remotePages.delete(localPage.meta.path);
132+
remotePage.localPage = localPage;
133+
union.push(remotePage);
211134
}
212-
213135
// Any remaining remote page not matching a local page should be deleted
214136
for (let remotePage of remotePages.values()) {
215-
results.delete.push(remotePage);
137+
union.push(remotePage);
216138
}
217-
218-
return results;
139+
return union;
219140
}
220141

221-
function versionChange(remotePage) {
222-
if (typeof remotePage.meta.publisher_version !== 'string') {
223-
return true;
224-
}
225-
const [pubMajor, pubMinor] = remotePage.meta.publisher_version.split('.');
226-
return pubMajor !== majorVer || pubMinor !== minorVer;
227-
}
228-
229-
230142
export async function cleanup() {
231143
const { siteName } = await context.getContext();
232144
try {
@@ -237,7 +149,7 @@ export async function cleanup() {
237149
}
238150
const remotePages = await confluence.getChildPages(home.id);
239151
// Delete all children
240-
await unpublish(remotePages);
152+
await unpublish(remotePages.values());
241153
// Delete home
242154
await unpublish([home]);
243155
cleanupSummary(siteName);

0 commit comments

Comments
 (0)