1- import path from 'node:path' ;
21import context from './context.js' ;
32import config from './config.js' ;
43import logger from './logger.js' ;
54import ConfluenceSDK from './confluence-sdk.js' ;
6- import { Image , Graph , Meta } from '../lib/models/index.js' ;
5+ import { Meta , LocalPage } from '../lib/models/index.js' ;
76import AssetRenderer from './renderers/asset-renderer.js' ;
87
98const confluence = new ConfluenceSDK ( config . confluence ) ;
10- const [ majorVer , minorVer ] = config . version . split ( '.' ) ;
119
10+ /**
11+ * Sync local markdown documentation with Confluence
12+ */
1213export 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+ */
8479async 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 */
10398async 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-
230142export 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