@telefonica/confluence-sync
Version:
Creates/updates/deletes Confluence pages based on a list of objects containing the page contents. Supports nested pages and attachments upload
304 lines (303 loc) • 11.9 kB
JavaScript
;
// SPDX-FileCopyrightText: 2025 Telefónica Innovación Digital
// SPDX-License-Identifier: Apache-2.0
Object.defineProperty(exports, "__esModule", { value: true });
exports.CustomConfluenceClient = void 0;
const confluence_js_1 = require("confluence.js");
const AttachmentsNotFoundError_1 = require("./errors/AttachmentsNotFoundError");
const AxiosErrors_1 = require("./errors/AxiosErrors");
const CreateAttachmentsError_1 = require("./errors/CreateAttachmentsError");
const CreatePageError_1 = require("./errors/CreatePageError");
const DeletePageError_1 = require("./errors/DeletePageError");
const PageNotFoundError_1 = require("./errors/PageNotFoundError");
const UpdatePageError_1 = require("./errors/UpdatePageError");
const CustomError_1 = require("./errors/CustomError");
const GET_CHILDREN_LIMIT = 100;
/**
* Type guard to check if the authentication is basic
* @param auth - Object to check
* @returns True if the authentication is basic, false otherwise
*/
function isBasicAuthentication(auth) {
return (auth.basic !== undefined);
}
/**
* Type guard to check if the authentication is OAuth2
* @param auth - Object to check
* @returns True if the authentication is OAuth2, false otherwise
*/
function isOAuth2Authentication(auth) {
return auth.oauth2 !== undefined;
}
/**
* Type guard to check if the authentication is JWT
* @param auth - Object to check
* @returns True if the authentication is JWT, false otherwise
*/
function isJWTAuthentication(auth) {
return (auth.jwt !== undefined);
}
/**
* Type guard to check if the authentication is valid
* @param auth The authentication object to check
* @returns True if the authentication is valid, false otherwise
*/
function isAuthentication(auth) {
if (typeof auth !== "object" || auth === null) {
return false;
}
return (isBasicAuthentication(auth) ||
isOAuth2Authentication(auth) ||
isJWTAuthentication(auth));
}
const CustomConfluenceClient = class CustomConfluenceClient {
_config;
_client;
_logger;
constructor(config) {
this._config = config;
if (!isAuthentication(config.authentication) &&
!config.personalAccessToken) {
throw new Error("Either authentication or personalAccessToken must be provided");
}
// Backward compatibility with personalAccessToken
const authentication = isAuthentication(config.authentication)
? config.authentication
: {
oauth2: {
accessToken: config.personalAccessToken,
},
};
const apiPrefix = config.apiPrefix ?? "/rest/";
this._client = new confluence_js_1.ConfluenceClient({
host: config.url,
authentication,
apiPrefix,
});
this._logger = config.logger;
}
// Exposed mainly for testing purposes
get logger() {
return this._logger;
}
async _getChildPages(parentId, start = 0, otherChildren = []) {
try {
this._logger.silly(`Getting child pages of parent with id ${parentId}`);
const response = await this._client.contentChildrenAndDescendants.getContentChildren({
id: parentId,
start,
limit: GET_CHILDREN_LIMIT,
expand: ["page"],
});
this._logger.silly(`Get child pages response of page ${parentId}, starting at ${start}: ${JSON.stringify(response.page, null, 2)}`);
const childrenResults = response.page?.results || [];
const size = response.page?.size || 0;
const allChildren = [
...otherChildren,
...childrenResults,
];
if (start + childrenResults.length < size) {
const newStart = start + GET_CHILDREN_LIMIT;
this._logger.silly(`There are more child pages of page with id ${parentId}, fetching next page starting from ${newStart}`);
return this._getChildPages(parentId, newStart, allChildren);
}
return allChildren;
}
catch (e) {
const error = (0, AxiosErrors_1.toConfluenceClientError)(e);
throw new PageNotFoundError_1.PageNotFoundError(parentId, { cause: error });
}
}
async getPage(id) {
try {
this._logger.silly(`Getting page with id ${id}`);
const childrenRequest = this._getChildPages(id);
const pageRequest = this._client.content.getContentById({
id,
expand: ["ancestors", "version.number"],
});
const [response, childrenResponse] = await Promise.all([
pageRequest,
childrenRequest,
]);
this._logger.silly(`Get page response: ${JSON.stringify(response, null, 2)}`);
this._logger.silly(`Get children response: ${JSON.stringify(childrenResponse, null, 2)}`);
return {
title: response.title,
id: response.id,
version: response.version?.number,
ancestors: response.ancestors?.map((ancestor) => this._convertToConfluencePageBasicInfo(ancestor)),
children: childrenResponse.map((child) => this._convertToConfluencePageBasicInfo(child)),
};
}
catch (e) {
if (!(e instanceof CustomError_1.CustomError)) {
const error = (0, AxiosErrors_1.toConfluenceClientError)(e);
throw new PageNotFoundError_1.PageNotFoundError(id, { cause: error });
}
throw e;
}
}
async createPage({ title, content, ancestors, }) {
if (!this._config.dryRun) {
const createContentBody = {
type: "page",
title,
space: {
key: this._config.spaceId,
},
ancestors: this.handleAncestors(ancestors),
body: {
storage: {
value: content || "",
representation: "storage",
},
},
};
try {
this._logger.silly(`Creating page with title ${title}`);
const response = await this._client.content.createContent(createContentBody);
this._logger.silly(`Create page response: ${JSON.stringify(response, null, 2)}`);
return {
title: response.title,
id: response.id,
version: response.version?.number,
ancestors: response.ancestors?.map((ancestor) => this._convertToConfluencePageBasicInfo(ancestor)),
};
}
catch (e) {
const error = (0, AxiosErrors_1.toConfluenceClientError)(e);
throw new CreatePageError_1.CreatePageError(title, { cause: error });
}
}
else {
this._logger.info(`Dry run: creating page with title ${title}`);
return {
title,
id: "1234",
version: 1,
ancestors,
};
}
}
async updatePage({ id, title, content, version, ancestors, }) {
if (!this._config.dryRun) {
const updateContentBody = {
id,
type: "page",
title,
ancestors: this.handleAncestors(ancestors),
version: {
number: version,
},
body: {
storage: {
value: content || "",
representation: "storage",
},
},
};
try {
this._logger.silly(`Updating page with title ${title}`);
const response = await this._client.content.updateContent(updateContentBody);
this._logger.silly(`Update page response: ${JSON.stringify(response, null, 2)}`);
return {
title: response.title,
id: response.id,
version: response.version?.number,
ancestors: response.ancestors?.map((ancestor) => this._convertToConfluencePageBasicInfo(ancestor)),
};
}
catch (e) {
const error = (0, AxiosErrors_1.toConfluenceClientError)(e);
throw new UpdatePageError_1.UpdatePageError(id, title, { cause: error });
}
}
else {
this._logger.info(`Dry run: updating page with title ${title}`);
return {
title,
id,
version,
ancestors,
};
}
}
async deleteContent(id) {
if (!this._config.dryRun) {
try {
this._logger.silly(`Deleting content with id ${id}`);
await this._client.content.deleteContent({ id });
}
catch (e) {
const error = (0, AxiosErrors_1.toConfluenceClientError)(e);
throw new DeletePageError_1.DeletePageError(id, { cause: error });
}
}
else {
this._logger.info(`Dry run: deleting content with id ${id}`);
}
}
async getAttachments(id) {
try {
this._logger.silly(`Getting attachments of page with id ${id}`);
const response = await this._client.contentAttachments.getAttachments({
id,
});
this._logger.silly(`Get attachments response: ${JSON.stringify(response, null, 2)}`);
return (response.results?.map((attachment) => ({
id: attachment.id,
title: attachment.title,
})) || []);
}
catch (e) {
const error = (0, AxiosErrors_1.toConfluenceClientError)(e);
throw new AttachmentsNotFoundError_1.AttachmentsNotFoundError(id, { cause: error });
}
}
async createAttachments(id, attachments) {
if (!this._config.dryRun) {
try {
const bodyRequest = attachments.map((attachment) => ({
minorEdit: true,
...attachment,
}));
this._logger.silly(`Creating attachments of page with id ${id}, attachments: ${attachments
.map((attachment) => attachment.filename)
.join(", ")}`);
const response = await this._client.contentAttachments.createAttachments({
id,
attachments: bodyRequest,
});
this._logger.silly(`Create attachments response: ${JSON.stringify(response, null, 2)}`);
}
catch (e) {
const error = (0, AxiosErrors_1.toConfluenceClientError)(e);
throw new CreateAttachmentsError_1.CreateAttachmentsError(id, { cause: error });
}
}
else {
this._logger
.info(`Dry run: creating attachments of page with id ${id}, attachments: ${attachments
.map((attachment) => attachment.filename)
.join(", ")}
`);
}
}
handleAncestors(ancestors) {
if (ancestors && ancestors.length) {
const id = ancestors.at(-1)?.id;
return [{ id }];
}
else {
return undefined;
}
}
_convertToConfluencePageBasicInfo(rawInfo) {
return {
id: rawInfo.id,
title: rawInfo.title,
};
}
};
exports.CustomConfluenceClient = CustomConfluenceClient;