From 0f6910e5a70662e09a78c87f1833f3e38299de74 Mon Sep 17 00:00:00 2001 From: timdeluxe <5765175+timdeluxe@users.noreply.github.com> Date: Fri, 5 Jul 2024 12:58:44 +0200 Subject: [PATCH 1/7] feat: Introducing option to authenticate with (personal access) token, instead of username/password --- lib/DefaultOptions.ts | 16 ++- lib/api/Configuration.ts | 27 ++++- lib/api/Confluence.ts | 115 ++++++++++++++------ lib/commands/Check.ts | 14 ++- lib/commands/CreateConfigurationDocument.ts | 13 ++- 5 files changed, 140 insertions(+), 45 deletions(-) diff --git a/lib/DefaultOptions.ts b/lib/DefaultOptions.ts index 596db49..5c7039e 100644 --- a/lib/DefaultOptions.ts +++ b/lib/DefaultOptions.ts @@ -15,17 +15,25 @@ export class DefaultOptions extends Options { name: 'user', flag: 'u', description: 'Username for checking all confluence documents', - required: true, + required: false, }) - confluenceUser: string + confluenceUser: string = '' @option({ name: 'password', flag: 'p', description: 'Password for the user', - required: true, + required: false, + }) + confluencePassword: string = '' + + @option({ + name: 'token', + flag: 't', + description: 'Personal Access Token for the user. If set user and password will be ignored', + required: false, }) - confluencePassword: string + confluencePersonalAccessToken: string = '' @option({ description: 'Log-Level to use (trace, debug, verbose, info, warn, error)', diff --git a/lib/api/Configuration.ts b/lib/api/Configuration.ts index 8eb4665..99242b2 100644 --- a/lib/api/Configuration.ts +++ b/lib/api/Configuration.ts @@ -34,6 +34,11 @@ export class Configuration { */ public confluencePassword: string + /** + * The personal access token of the Confluence user + */ + public confluencePersonalAccessToken: string + /** * The document id of the configuration document */ @@ -94,10 +99,11 @@ export class Configuration { */ private _log: Logger - constructor(confluenceUrl: string, confluenceUser: string, confluencePassword: string, configurationDocumentId: string) { + constructor(confluenceUrl: string, confluenceUser: string, confluencePassword: string, confluencePersonalAccessToken: string, configurationDocumentId: string) { this.confluenceUrl = confluenceUrl this.confluenceUser = confluenceUser this.confluencePassword = confluencePassword + this.confluencePersonalAccessToken = confluencePersonalAccessToken this.configurationDocumentId = configurationDocumentId this._loaded = false @@ -134,11 +140,20 @@ export class Configuration { // eslint-disable-next-line @typescript-eslint/no-explicit-any let configurationDocument: any try { - configurationDocument = await got(configurationUrl, { - username: this.confluenceUser, - password: this.confluencePassword, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - }).json() + if (this.confluencePersonalAccessToken !== '') { + configurationDocument = await got(configurationUrl, { + headers: { + 'Authorization': 'Bearer ' + this.confluencePersonalAccessToken + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + }).json() + } else { + configurationDocument = await got(configurationUrl, { + username: this.confluenceUser, + password: this.confluencePassword, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + }).json() + } } catch (e) { this._log.error(`Can't fetch configuration document: (${e.name}) ${e.message}`) throw e diff --git a/lib/api/Confluence.ts b/lib/api/Confluence.ts index 4670db8..fe30917 100644 --- a/lib/api/Confluence.ts +++ b/lib/api/Confluence.ts @@ -17,12 +17,14 @@ export class Confluence { public confluenceUrl: string public confluenceUser: string public confluencePassword: string + public confluencePersonalAccessToken: string private _log: Logger - constructor(confluenceUrl: string, confluenceUser: string, confluencePassword: string) { + constructor(confluenceUrl: string, confluenceUser: string, confluencePassword: string, confluencePersonalAccessToken: string,) { this.confluenceUrl = confluenceUrl this.confluenceUser = confluenceUser this.confluencePassword = confluencePassword + this.confluencePersonalAccessToken = confluencePersonalAccessToken this._log = log.getLogger('Confluence') } @@ -47,11 +49,20 @@ export class Confluence { this._log.debug(`Searching for documents with ${cql}`) do { const configurationUrl = `${this.confluenceUrl}/rest/api/content/search?cql=${cql}&start=${start}&limit=${limit}` - results = await got(configurationUrl, { - username: this.confluenceUser, - password: this.confluencePassword, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - }).json() + if (this.confluencePersonalAccessToken !== '') { + results = await got(configurationUrl, { + headers: { + 'Authorization': 'Bearer ' + this.confluencePersonalAccessToken + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + }).json() + } else { + results = await got(configurationUrl, { + username: this.confluenceUser, + password: this.confluencePassword, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + }).json() + } for (const result of results.results) { documentInfos.push(await this.getDocumentInfo(result.id)) } @@ -69,12 +80,20 @@ export class Confluence { public async getDocumentInfo(documentId: number): Promise { this._log.debug(`Getting document information of document ${documentId}`) const documentUrl = `${this.confluenceUrl}/rest/api/content/${documentId}?expand=ancestors,version,metadata.labels,history` - const document = await got(documentUrl, { - username: this.confluenceUser, - password: this.confluencePassword, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - }).json() - + if (this.confluencePersonalAccessToken !== '') { + const document = await got(documentUrl, { + headers: { + 'Authorization': 'Bearer ' + this.confluencePersonalAccessToken + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + }).json() + } else { + const document = await got(documentUrl, { + username: this.confluenceUser, + password: this.confluencePassword, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + }).json() + } const author = document.version.by.username ?? null const creator = document.history['createdBy'].username ?? null @@ -142,31 +161,61 @@ export class Confluence { public async createConfigurationDocument(space: string, title: string, parentId: string): Promise { const template = await fs.promises.readFile(path.join(__dirname, '..', '..', 'resources', 'configurationDocument.html'), 'utf-8') - const response = await got - .post(`${this.confluenceUrl}/rest/api/content`, { - json: { - type: 'page', - title: title, - space: { - key: space, + let response: any + if (this.confluencePersonalAccessToken !== '') { + response = await got + .post(`${this.confluenceUrl}/rest/api/content`, { + json: { + type: 'page', + title: title, + space: { + key: space, + }, + ancestors: [ + { + id: parentId, + }, + ], + body: { + storage: { + value: template, + representation: 'storage', + }, + }, }, - ancestors: [ - { - id: parentId, + headers: { + 'Authorization': 'Bearer ' + this.confluencePersonalAccessToken + } + }) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .json() + } else { + response = await got + .post(`${this.confluenceUrl}/rest/api/content`, { + json: { + type: 'page', + title: title, + space: { + key: space, }, - ], - body: { - storage: { - value: template, - representation: 'storage', + ancestors: [ + { + id: parentId, + }, + ], + body: { + storage: { + value: template, + representation: 'storage', + }, }, }, - }, - username: this.confluenceUser, - password: this.confluencePassword, - }) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .json() + username: this.confluenceUser, + password: this.confluencePassword, + }) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .json() + } return response.id } } diff --git a/lib/commands/Check.ts b/lib/commands/Check.ts index 23b4c46..9f94991 100644 --- a/lib/commands/Check.ts +++ b/lib/commands/Check.ts @@ -38,17 +38,29 @@ export default class extends Command { public async execute(options: CheckOptions): Promise { const log = options.getLogger() + if( + options.confluencePersonalAccessToken === '' + && ( + options.confluenceUser === '' + || options.confluencePassword === '' + ) + ) { + log.error('user and/or password parameter not set or empty! When not using the token parameter both of these have to be set!') + return + } + log.info('Checking for outdated documents') const configuration = new Configuration( options.confluenceUrl, options.confluenceUser, options.confluencePassword, + options.confluencePersonalAccessToken, options.configurationDocumentId ) await configuration.load() - const confluence = new Confluence(options.confluenceUrl, options.confluenceUser, options.confluencePassword) + const confluence = new Confluence(options.confluenceUrl, options.confluenceUser, options.confluencePassword, options.confluencePersonalAccessToken) const notification = new Notification(configuration, options.smtpTransportUrl, confluence, null, options.dryRun) diff --git a/lib/commands/CreateConfigurationDocument.ts b/lib/commands/CreateConfigurationDocument.ts index 44b6e3a..f16464c 100644 --- a/lib/commands/CreateConfigurationDocument.ts +++ b/lib/commands/CreateConfigurationDocument.ts @@ -36,9 +36,20 @@ export default class extends Command { public async execute(options: CheckOptions): Promise { const log = options.getLogger() + if( + options.confluencePersonalAccessToken === '' + && ( + options.confluenceUser === '' + || options.confluencePassword === '' + ) + ) { + log.error('user and/or password parameter not set or empty! When not using the token parameter both of these have to be set!') + return + } + log.info('Checking for outdated documents') - const confluence = new Confluence(options.confluenceUrl, options.confluenceUser, options.confluencePassword) + const confluence = new Confluence(options.confluenceUrl, options.confluenceUser, options.confluencePassword, options.confluencePersonalAccessToken) const pageId = await confluence.createConfigurationDocument(options.space, options.title, options.parentId) From 1043820f3b0383b63198aa1e56d61e43ab0cc927 Mon Sep 17 00:00:00 2001 From: timdeluxe <5765175+timdeluxe@users.noreply.github.com> Date: Fri, 5 Jul 2024 13:10:59 +0200 Subject: [PATCH 2/7] feat: Introducing option to authenticate with (personal access) token, instead of username/password --- lib/DefaultOptions.ts | 6 +++--- lib/api/Configuration.ts | 2 +- lib/api/Confluence.ts | 8 ++++---- lib/commands/Check.ts | 8 +------- lib/commands/CreateConfigurationDocument.ts | 8 +------- 5 files changed, 10 insertions(+), 22 deletions(-) diff --git a/lib/DefaultOptions.ts b/lib/DefaultOptions.ts index 5c7039e..7b5609a 100644 --- a/lib/DefaultOptions.ts +++ b/lib/DefaultOptions.ts @@ -17,7 +17,7 @@ export class DefaultOptions extends Options { description: 'Username for checking all confluence documents', required: false, }) - confluenceUser: string = '' + confluenceUser: String = '' @option({ name: 'password', @@ -25,7 +25,7 @@ export class DefaultOptions extends Options { description: 'Password for the user', required: false, }) - confluencePassword: string = '' + confluencePassword: String = '' @option({ name: 'token', @@ -33,7 +33,7 @@ export class DefaultOptions extends Options { description: 'Personal Access Token for the user. If set user and password will be ignored', required: false, }) - confluencePersonalAccessToken: string = '' + confluencePersonalAccessToken: String = '' @option({ description: 'Log-Level to use (trace, debug, verbose, info, warn, error)', diff --git a/lib/api/Configuration.ts b/lib/api/Configuration.ts index 99242b2..b784b18 100644 --- a/lib/api/Configuration.ts +++ b/lib/api/Configuration.ts @@ -143,7 +143,7 @@ export class Configuration { if (this.confluencePersonalAccessToken !== '') { configurationDocument = await got(configurationUrl, { headers: { - 'Authorization': 'Bearer ' + this.confluencePersonalAccessToken + Authorization: 'Bearer ' + this.confluencePersonalAccessToken, } // eslint-disable-next-line @typescript-eslint/no-explicit-any }).json() diff --git a/lib/api/Confluence.ts b/lib/api/Confluence.ts index fe30917..6f3dd6a 100644 --- a/lib/api/Confluence.ts +++ b/lib/api/Confluence.ts @@ -20,7 +20,7 @@ export class Confluence { public confluencePersonalAccessToken: string private _log: Logger - constructor(confluenceUrl: string, confluenceUser: string, confluencePassword: string, confluencePersonalAccessToken: string,) { + constructor(confluenceUrl: string, confluenceUser: string, confluencePassword: string, confluencePersonalAccessToken: string) { this.confluenceUrl = confluenceUrl this.confluenceUser = confluenceUser this.confluencePassword = confluencePassword @@ -52,7 +52,7 @@ export class Confluence { if (this.confluencePersonalAccessToken !== '') { results = await got(configurationUrl, { headers: { - 'Authorization': 'Bearer ' + this.confluencePersonalAccessToken + Authorization: 'Bearer ' + this.confluencePersonalAccessToken, } // eslint-disable-next-line @typescript-eslint/no-explicit-any }).json() @@ -83,7 +83,7 @@ export class Confluence { if (this.confluencePersonalAccessToken !== '') { const document = await got(documentUrl, { headers: { - 'Authorization': 'Bearer ' + this.confluencePersonalAccessToken + Authorization: 'Bearer ' + this.confluencePersonalAccessToken, } // eslint-disable-next-line @typescript-eslint/no-explicit-any }).json() @@ -184,7 +184,7 @@ export class Confluence { }, }, headers: { - 'Authorization': 'Bearer ' + this.confluencePersonalAccessToken + Authorization: 'Bearer ' + this.confluencePersonalAccessToken, } }) // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/lib/commands/Check.ts b/lib/commands/Check.ts index 9f94991..df3232e 100644 --- a/lib/commands/Check.ts +++ b/lib/commands/Check.ts @@ -38,13 +38,7 @@ export default class extends Command { public async execute(options: CheckOptions): Promise { const log = options.getLogger() - if( - options.confluencePersonalAccessToken === '' - && ( - options.confluenceUser === '' - || options.confluencePassword === '' - ) - ) { + if( options.confluencePersonalAccessToken === '' && ( options.confluenceUser === '' || options.confluencePassword === '')) { log.error('user and/or password parameter not set or empty! When not using the token parameter both of these have to be set!') return } diff --git a/lib/commands/CreateConfigurationDocument.ts b/lib/commands/CreateConfigurationDocument.ts index f16464c..6ef62c7 100644 --- a/lib/commands/CreateConfigurationDocument.ts +++ b/lib/commands/CreateConfigurationDocument.ts @@ -36,13 +36,7 @@ export default class extends Command { public async execute(options: CheckOptions): Promise { const log = options.getLogger() - if( - options.confluencePersonalAccessToken === '' - && ( - options.confluenceUser === '' - || options.confluencePassword === '' - ) - ) { + if( options.confluencePersonalAccessToken === '' && ( options.confluenceUser === '' || options.confluencePassword === '')) { log.error('user and/or password parameter not set or empty! When not using the token parameter both of these have to be set!') return } From ba2c08f51a9f2897bc8de68a6af88058fe30f92b Mon Sep 17 00:00:00 2001 From: timdeluxe <5765175+timdeluxe@users.noreply.github.com> Date: Fri, 5 Jul 2024 13:17:46 +0200 Subject: [PATCH 3/7] feat: Introducing option to authenticate with (personal access) token, instead of username/password --- lib/DefaultOptions.ts | 6 +++--- lib/api/Confluence.ts | 6 +++--- lib/commands/Check.ts | 9 +++++++-- lib/commands/CreateConfigurationDocument.ts | 9 +++++++-- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/DefaultOptions.ts b/lib/DefaultOptions.ts index 7b5609a..6918a6f 100644 --- a/lib/DefaultOptions.ts +++ b/lib/DefaultOptions.ts @@ -17,7 +17,7 @@ export class DefaultOptions extends Options { description: 'Username for checking all confluence documents', required: false, }) - confluenceUser: String = '' + confluenceUser = '' @option({ name: 'password', @@ -25,7 +25,7 @@ export class DefaultOptions extends Options { description: 'Password for the user', required: false, }) - confluencePassword: String = '' + confluencePassword = '' @option({ name: 'token', @@ -33,7 +33,7 @@ export class DefaultOptions extends Options { description: 'Personal Access Token for the user. If set user and password will be ignored', required: false, }) - confluencePersonalAccessToken: String = '' + confluencePersonalAccessToken = '' @option({ description: 'Log-Level to use (trace, debug, verbose, info, warn, error)', diff --git a/lib/api/Confluence.ts b/lib/api/Confluence.ts index 6f3dd6a..c740560 100644 --- a/lib/api/Confluence.ts +++ b/lib/api/Confluence.ts @@ -53,7 +53,7 @@ export class Confluence { results = await got(configurationUrl, { headers: { Authorization: 'Bearer ' + this.confluencePersonalAccessToken, - } + }, // eslint-disable-next-line @typescript-eslint/no-explicit-any }).json() } else { @@ -84,7 +84,7 @@ export class Confluence { const document = await got(documentUrl, { headers: { Authorization: 'Bearer ' + this.confluencePersonalAccessToken, - } + }, // eslint-disable-next-line @typescript-eslint/no-explicit-any }).json() } else { @@ -185,7 +185,7 @@ export class Confluence { }, headers: { Authorization: 'Bearer ' + this.confluencePersonalAccessToken, - } + }, }) // eslint-disable-next-line @typescript-eslint/no-explicit-any .json() diff --git a/lib/commands/Check.ts b/lib/commands/Check.ts index df3232e..682643d 100644 --- a/lib/commands/Check.ts +++ b/lib/commands/Check.ts @@ -38,7 +38,7 @@ export default class extends Command { public async execute(options: CheckOptions): Promise { const log = options.getLogger() - if( options.confluencePersonalAccessToken === '' && ( options.confluenceUser === '' || options.confluencePassword === '')) { + if(options.confluencePersonalAccessToken === '' && (options.confluenceUser === '' || options.confluencePassword === '')) { log.error('user and/or password parameter not set or empty! When not using the token parameter both of these have to be set!') return } @@ -54,7 +54,12 @@ export default class extends Command { ) await configuration.load() - const confluence = new Confluence(options.confluenceUrl, options.confluenceUser, options.confluencePassword, options.confluencePersonalAccessToken) + const confluence = new Confluence( + options.confluenceUrl, + options.confluenceUser, + options.confluencePassword, + options.confluencePersonalAccessToken + ) const notification = new Notification(configuration, options.smtpTransportUrl, confluence, null, options.dryRun) diff --git a/lib/commands/CreateConfigurationDocument.ts b/lib/commands/CreateConfigurationDocument.ts index 6ef62c7..af1177c 100644 --- a/lib/commands/CreateConfigurationDocument.ts +++ b/lib/commands/CreateConfigurationDocument.ts @@ -36,14 +36,19 @@ export default class extends Command { public async execute(options: CheckOptions): Promise { const log = options.getLogger() - if( options.confluencePersonalAccessToken === '' && ( options.confluenceUser === '' || options.confluencePassword === '')) { + if(options.confluencePersonalAccessToken === '' && (options.confluenceUser === '' || options.confluencePassword === '')) { log.error('user and/or password parameter not set or empty! When not using the token parameter both of these have to be set!') return } log.info('Checking for outdated documents') - const confluence = new Confluence(options.confluenceUrl, options.confluenceUser, options.confluencePassword, options.confluencePersonalAccessToken) + const confluence = new Confluence( + options.confluenceUrl, + options.confluenceUser, + options.confluencePassword, + options.confluencePersonalAccessToken + ) const pageId = await confluence.createConfigurationDocument(options.space, options.title, options.parentId) From 3d7f95f5110d95751d32e7bf1575a63f93adea19 Mon Sep 17 00:00:00 2001 From: timdeluxe <5765175+timdeluxe@users.noreply.github.com> Date: Fri, 5 Jul 2024 13:21:28 +0200 Subject: [PATCH 4/7] feat: Introducing option to authenticate with (personal access) token, instead of username/password --- lib/api/Configuration.ts | 10 ++++++++-- lib/commands/Check.ts | 2 +- lib/commands/CreateConfigurationDocument.ts | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/api/Configuration.ts b/lib/api/Configuration.ts index b784b18..5ca4756 100644 --- a/lib/api/Configuration.ts +++ b/lib/api/Configuration.ts @@ -99,7 +99,13 @@ export class Configuration { */ private _log: Logger - constructor(confluenceUrl: string, confluenceUser: string, confluencePassword: string, confluencePersonalAccessToken: string, configurationDocumentId: string) { + constructor( + confluenceUrl: string, + confluenceUser: string, + confluencePassword: string, + confluencePersonalAccessToken: string, + configurationDocumentId: string + ) { this.confluenceUrl = confluenceUrl this.confluenceUser = confluenceUser this.confluencePassword = confluencePassword @@ -144,7 +150,7 @@ export class Configuration { configurationDocument = await got(configurationUrl, { headers: { Authorization: 'Bearer ' + this.confluencePersonalAccessToken, - } + }, // eslint-disable-next-line @typescript-eslint/no-explicit-any }).json() } else { diff --git a/lib/commands/Check.ts b/lib/commands/Check.ts index 682643d..fb51136 100644 --- a/lib/commands/Check.ts +++ b/lib/commands/Check.ts @@ -38,7 +38,7 @@ export default class extends Command { public async execute(options: CheckOptions): Promise { const log = options.getLogger() - if(options.confluencePersonalAccessToken === '' && (options.confluenceUser === '' || options.confluencePassword === '')) { + if (options.confluencePersonalAccessToken === '' && (options.confluenceUser === '' || options.confluencePassword === '')) { log.error('user and/or password parameter not set or empty! When not using the token parameter both of these have to be set!') return } diff --git a/lib/commands/CreateConfigurationDocument.ts b/lib/commands/CreateConfigurationDocument.ts index af1177c..e2616ea 100644 --- a/lib/commands/CreateConfigurationDocument.ts +++ b/lib/commands/CreateConfigurationDocument.ts @@ -36,7 +36,7 @@ export default class extends Command { public async execute(options: CheckOptions): Promise { const log = options.getLogger() - if(options.confluencePersonalAccessToken === '' && (options.confluenceUser === '' || options.confluencePassword === '')) { + if (options.confluencePersonalAccessToken === '' && (options.confluenceUser === '' || options.confluencePassword === '')) { log.error('user and/or password parameter not set or empty! When not using the token parameter both of these have to be set!') return } From 4d11d1b912725c034cd95de5b746a057211bff3e Mon Sep 17 00:00:00 2001 From: timdeluxe <5765175+timdeluxe@users.noreply.github.com> Date: Fri, 5 Jul 2024 13:28:00 +0200 Subject: [PATCH 5/7] feat: Introducing option to authenticate with (personal access) token, instead of username/password --- test/ConfigurationTest.ts | 6 +++--- test/ConfluenceTest.ts | 4 ++-- test/NotificationTest.ts | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/ConfigurationTest.ts b/test/ConfigurationTest.ts index 4fef776..6efaff5 100644 --- a/test/ConfigurationTest.ts +++ b/test/ConfigurationTest.ts @@ -11,7 +11,7 @@ describe('The Configuration API', (): void => { const mockServer = new MockServer('https://example.com') mockServer.addConfigurationDocumentEndpoint() - const configuration = new Configuration('https://example.com', 'nobody', 'nothing', '12345') + const configuration = new Configuration('https://example.com', 'nobody', 'nothing', '', '12345') await configuration.load() chai.expect(configuration.checks).to.have.lengthOf(2) chai.expect(configuration.checks[0].labels).to.contain('test1') @@ -31,7 +31,7 @@ describe('The Configuration API', (): void => { const mockServer = new MockServer('https://example.com') mockServer.addConfigurationDocumentEndpoint() - const configuration = new Configuration('https://example.com', 'nobody', 'nothing', '12346') + const configuration = new Configuration('https://example.com', 'nobody', 'nothing', '', '12346') await configuration.load() chai.expect(configuration.exceptions).to.have.lengthOf(0) @@ -40,7 +40,7 @@ describe('The Configuration API', (): void => { const mockServer = new MockServer('https://example.com') mockServer.addConfigurationDocumentEndpoint() - const configuration = new Configuration('https://example.com', 'nobody', 'nothing', '12347') + const configuration = new Configuration('https://example.com', 'nobody', 'nothing', '', '12347') await configuration.load() chai.expect(configuration.maintainer).to.have.lengthOf(1) }) diff --git a/test/ConfluenceTest.ts b/test/ConfluenceTest.ts index ee9d810..a4688e0 100644 --- a/test/ConfluenceTest.ts +++ b/test/ConfluenceTest.ts @@ -11,7 +11,7 @@ describe('The Confluence API', (): void => { const mockServer = new MockServer('https://example.com') mockServer.addSearchEndpoint() mockServer.addDocumentEndpoint() - const confluence = new Confluence('https://example.com', 'nobody', 'nothing') + const confluence = new Confluence('https://example.com', 'nobody', '', 'nothing') const results = await confluence.findDocumentsOlderThan('', 1, 1) chai.expect(results).to.have.lengthOf(2) chai.expect(results[0].url).to.eq('https://example.com/display/SAMPLE/Test') @@ -35,7 +35,7 @@ describe('The Confluence API', (): void => { it('should add a configuration document', async (): Promise => { const mockServer = new MockServer('https://example.com') mockServer.addCreateEndpoint() - const confluence = new Confluence('https://example.com', 'nobody', 'nothing') + const confluence = new Confluence('https://example.com', 'nobody', '', 'nothing') const result = await confluence.createConfigurationDocument('example', 'test', '0123') chai.expect(result).to.eq('12345') }) diff --git a/test/NotificationTest.ts b/test/NotificationTest.ts index 12a987c..20f5e52 100644 --- a/test/NotificationTest.ts +++ b/test/NotificationTest.ts @@ -22,9 +22,9 @@ describe('The Notification API', (): void => { mockServer.addConfigurationDocumentEndpoint() mockServer.addSearchEndpoint() mockServer.addDocumentEndpoint() - configuration = new Configuration('https://example.com', 'nobody', 'nothing', '12345') + configuration = new Configuration('https://example.com', 'nobody', 'nothing', '', '12345') await configuration.load() - confluence = new Confluence('https://example.com', 'nobody', 'nothing') + confluence = new Confluence('https://example.com', 'nobody', 'nothing', '') transportStub = sinon.createStubInstance(Mail.prototype.constructor, { sendMail: sinon.stub().resolves(), }) as Mail @@ -68,7 +68,7 @@ describe('The Notification API', (): void => { ).to.be.true }) it('should use a maintainer when configured', async (): Promise => { - configuration = new Configuration('https://example.com', 'nobody', 'nothing', '12347') + configuration = new Configuration('https://example.com', 'nobody', 'nothing', '', '12347') await configuration.load() const notification = new Notification(configuration, '', confluence, transportStub) const documentInfo = new DocumentInfo( @@ -291,7 +291,7 @@ describe('The Notification API', (): void => { }) it('should use creator author if specified', async (): Promise => { - configuration = new Configuration('https://example.com', 'nobody', 'nothing', '12348') + configuration = new Configuration('https://example.com', 'nobody', 'nothing', '', '12348') await configuration.load() const notification = new Notification(configuration, '', confluence, transportStub, false) const documentInfo = new DocumentInfo( From dca8f3fc3dc4c31734a402c4ab9607096617c3c4 Mon Sep 17 00:00:00 2001 From: timdeluxe <5765175+timdeluxe@users.noreply.github.com> Date: Fri, 5 Jul 2024 13:32:40 +0200 Subject: [PATCH 6/7] feat: Introducing option to authenticate with (personal access) token, instead of username/password --- lib/api/Confluence.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/api/Confluence.ts b/lib/api/Confluence.ts index c740560..72933ff 100644 --- a/lib/api/Confluence.ts +++ b/lib/api/Confluence.ts @@ -80,15 +80,16 @@ export class Confluence { public async getDocumentInfo(documentId: number): Promise { this._log.debug(`Getting document information of document ${documentId}`) const documentUrl = `${this.confluenceUrl}/rest/api/content/${documentId}?expand=ancestors,version,metadata.labels,history` + let document if (this.confluencePersonalAccessToken !== '') { - const document = await got(documentUrl, { + document = await got(documentUrl, { headers: { Authorization: 'Bearer ' + this.confluencePersonalAccessToken, }, // eslint-disable-next-line @typescript-eslint/no-explicit-any }).json() } else { - const document = await got(documentUrl, { + document = await got(documentUrl, { username: this.confluenceUser, password: this.confluencePassword, // eslint-disable-next-line @typescript-eslint/no-explicit-any From 82c975c9fbe61e5a98bc3a73b3c7b7c62b227f73 Mon Sep 17 00:00:00 2001 From: Dennis Ploeger Date: Mon, 18 Nov 2024 07:30:45 +0100 Subject: [PATCH 7/7] fix: Fix tests --- test/ConfluenceTest.ts | 35 ++++++++++++- test/MockServer.ts | 109 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 1 deletion(-) diff --git a/test/ConfluenceTest.ts b/test/ConfluenceTest.ts index a4688e0..6229224 100644 --- a/test/ConfluenceTest.ts +++ b/test/ConfluenceTest.ts @@ -11,7 +11,7 @@ describe('The Confluence API', (): void => { const mockServer = new MockServer('https://example.com') mockServer.addSearchEndpoint() mockServer.addDocumentEndpoint() - const confluence = new Confluence('https://example.com', 'nobody', '', 'nothing') + const confluence = new Confluence('https://example.com', 'nobody', 'nothing', '') const results = await confluence.findDocumentsOlderThan('', 1, 1) chai.expect(results).to.have.lengthOf(2) chai.expect(results[0].url).to.eq('https://example.com/display/SAMPLE/Test') @@ -35,6 +35,39 @@ describe('The Confluence API', (): void => { it('should add a configuration document', async (): Promise => { const mockServer = new MockServer('https://example.com') mockServer.addCreateEndpoint() + const confluence = new Confluence('https://example.com', 'nobody', 'nothing', '') + const result = await confluence.createConfigurationDocument('example', 'test', '0123') + chai.expect(result).to.eq('12345') + }) + + it('should search for old documents using an access token', async (): Promise => { + const mockServer = new MockServer('https://example.com') + mockServer.addSearchEndpointToken() + mockServer.addDocumentEndpointToken() + const confluence = new Confluence('https://example.com', 'nobody', '', 'nothing') + const results = await confluence.findDocumentsOlderThan('', 1, 1) + chai.expect(results).to.have.lengthOf(2) + chai.expect(results[0].url).to.eq('https://example.com/display/SAMPLE/Test') + chai.expect(results[0].shortUrl).to.eq('/display/SAMPLE/Test') + chai.expect(results[0].author).to.eq('author') + chai.expect(results[0].id).to.eq(123) + chai.expect(results[0].lastVersionDate).to.eq('2019-12-31T22:00:00.000Z') + chai.expect(results[0].lastVersionMessage).to.eq('Some change') + chai.expect(results[0].title).to.eq('Test') + chai.expect(results[1].url).to.eq('https://example.com/display/SAMPLE/Test2') + chai.expect(results[1].shortUrl).to.eq('/display/SAMPLE/Test2') + chai.expect(results[1].author).to.eq('author2') + chai.expect(results[1].id).to.eq(234) + chai.expect(results[1].lastVersionDate).to.eq('2020-01-31T22:00:00.000Z') + chai.expect(results[1].lastVersionMessage).to.eq('') + chai.expect(results[1].title).to.eq('Test2') + chai.expect(results[0].labels.length).to.eq(1) + chai.expect(results[0].labels[0]).to.eq('Test') + }) + + it('should add a configuration document using an access token', async (): Promise => { + const mockServer = new MockServer('https://example.com') + mockServer.addCreateEndpointToken() const confluence = new Confluence('https://example.com', 'nobody', '', 'nothing') const result = await confluence.createConfigurationDocument('example', 'test', '0123') chai.expect(result).to.eq('12345') diff --git a/test/MockServer.ts b/test/MockServer.ts index 7ae816c..65b3dad 100644 --- a/test/MockServer.ts +++ b/test/MockServer.ts @@ -665,6 +665,34 @@ export class MockServer { }) } + public addSearchEndpointToken(): void { + this._scope + .get(new RegExp('/rest/api/content/search\\?cql=.+&start=0')) + .matchHeader('authorization', 'Bearer nothing') + .reply(200, { + results: [ + { + id: 123, + }, + ], + start: 0, + size: 1, + totalSize: 2, + }) + .get(new RegExp('/rest/api/content/search\\?cql=.+&start=1')) + .matchHeader('authorization', 'Bearer nothing') + .reply(200, { + results: [ + { + id: 234, + }, + ], + start: 1, + size: 1, + totalSize: 2, + }) + } + public addDocumentEndpoint(): void { this._scope .get('/rest/api/content/123?expand=ancestors,version,metadata.labels,history') @@ -740,6 +768,75 @@ export class MockServer { }) } + public addDocumentEndpointToken(): void { + this._scope + .get('/rest/api/content/123?expand=ancestors,version,metadata.labels,history') + .matchHeader('authorization', 'Bearer nothing') + .reply(200, { + _links: { + base: 'https://example.com', + webui: '/display/SAMPLE/Test', + }, + ancestors: [ + { + title: 'main', + }, + ], + version: { + by: { + username: 'author', + }, + when: '2020-01-01T00:00:00.000+02:00', + message: 'Some change', + }, + history: { + createdBy: { + username: 'creator', + }, + }, + title: 'Test', + metadata: { + labels: { + results: [ + { + prefix: 'global', + name: 'Test', + id: '90734603', + }, + ], + }, + }, + }) + .get('/rest/api/content/234?expand=ancestors,version,metadata.labels,history') + .matchHeader('authorization', 'Bearer nothing') + .reply(200, { + _links: { + base: 'https://example.com', + webui: '/display/SAMPLE/Test2', + }, + ancestors: [ + { + title: 'main', + }, + { + title: 'Test', + }, + ], + history: { + createdBy: { + username: 'creator', + }, + }, + version: { + by: { + username: 'author2', + }, + when: '2020-02-01T00:00:00.000+02:00', + }, + title: 'Test2', + }) + } + public addCreateEndpoint(): void { this._scope .post('/rest/api/content') @@ -754,4 +851,16 @@ export class MockServer { return requestObject }) } + + public addCreateEndpointToken(): void { + this._scope + .post('/rest/api/content') + .matchHeader('authorization', 'Bearer nothing') + .reply(200, (uri, requestBody) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const requestObject = requestBody as Record + requestObject.id = '12345' + return requestObject + }) + } }