Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 9 additions & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,19 @@ pnpm nx test:unit child-process-manager
To run a task in all components, use the following syntax: `pnpm nx run-many <task> --all`. For example, to run the unit tests in all components, use the following command:

```bash
pnpm nx run-many test:unit --all
pnpm nx run-many -t test:unit --all
```

This will run the `test:unit` task in all components and also the corresponding dependencies, in the right order, so everything is built and tested correctly.

## Running all checks in all components

To run all checks in all components, use the following command:

```bash
pnpm check:all
```

# Branching model

Here you have an schema of the branching model used in this repository:
Expand Down
6 changes: 6 additions & 0 deletions components/confluence-sync/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
#### Deprecated
#### Removed

## [2.2.0] - 2025-11-24

### Added

* feat: Add apiPrefix option to configure the Confluence API prefix (default: /rest/).

## [2.1.0] - 2025-10-17

### Added
Expand Down
1 change: 1 addition & 0 deletions components/confluence-sync/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ await confluenceSyncPages.sync([
The main class of the library. It receives a configuration object with the following properties:

* `url`: URL of the Confluence instance.
* `apiPrefix`: Optional prefix for the Confluence API urls. Default is `/rest/`.
* `personalAccessToken`: Personal access token to authenticate in Confluence. To be DEPRECATED in future versions. Use the `authentication` property instead.
* `authentication`: Authentication options to access Confluence. It supports the following methods:
* `oauth2`: OAuth2 authentication. It requires:
Expand Down
2 changes: 1 addition & 1 deletion components/confluence-sync/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@telefonica/confluence-sync",
"description": "Creates/updates/deletes Confluence pages based on a list of objects containing the page contents. Supports nested pages and attachments upload",
"version": "2.1.0",
"version": "2.2.0",
"license": "Apache-2.0",
"author": "Telefónica Innovación Digital",
"repository": {
Expand Down
2 changes: 2 additions & 0 deletions components/confluence-sync/src/ConfluenceSyncPages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const ConfluenceSyncPages: ConfluenceSyncPagesConstructor = class Conflue
dryRun,
syncMode,
rootPageId,
apiPrefix,
}: ConfluenceSyncPagesConfig) {
this._logger = new Logger(LOGGER_NAMESPACE, {
level: logLevel ?? DEFAULT_LOG_LEVEL,
Expand All @@ -70,6 +71,7 @@ export const ConfluenceSyncPages: ConfluenceSyncPagesConstructor = class Conflue
authentication,
logger: this._logger.namespace("confluence"),
dryRun,
apiPrefix,
});
this._syncMode = syncMode ?? SyncModes.TREE;

Expand Down
2 changes: 2 additions & 0 deletions components/confluence-sync/src/ConfluenceSyncPages.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export interface ConfluenceSyncPagesConfig {
logLevel?: LogLevel;
/** Dry run option */
dryRun?: boolean;
/** API Prefix */
apiPrefix?: string;
/** Sync mode */
syncMode?: SyncModes;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,12 @@ export const CustomConfluenceClient: ConfluenceClientConstructor = class CustomC
},
};

const apiPrefix = config.apiPrefix ?? "/rest/";

this._client = new ConfluenceClient({
host: config.url,
authentication,
apiPrefix: "/rest/",
apiPrefix,
});
this._logger = config.logger;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ export interface ConfluenceClientConfig {
authentication?: ConfluenceClientAuthenticationConfig;
/** Confluence url */
url: string;
/** API Prefix */
apiPrefix?: string;
/** Confluence space id */
spaceId: string;
/** Logger */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,35 @@ describe("customConfluenceClient class", () => {
apiPrefix: "/rest/",
});
});

it("should set apiPrefix when provided", () => {
const configWithBoth = {
spaceId: "foo-space-id",
url: "foo-url",
personalAccessToken: "foo-token",
authentication: {
basic: {
email: "[email protected]",
apiToken: "basic-token",
},
},
logger,
apiPrefix: "/foo/",
};

new CustomConfluenceClient(configWithBoth);

expect(ConfluenceClientConstructor).toHaveBeenCalledWith({
host: "foo-url",
authentication: {
basic: {
email: "[email protected]",
apiToken: "basic-token",
},
},
apiPrefix: "/foo/",
});
});
});

describe("when using basic authentication", () => {
Expand Down
6 changes: 6 additions & 0 deletions components/markdown-confluence-sync/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
#### Deprecated
#### Removed

## Unreleased

#### Added

- feat(#63): Add apiPrefix option to configure the Confluence API prefix (default: /rest/).

## [2.2.0] - 2025-10-17

### Added
Expand Down
1 change: 1 addition & 0 deletions components/markdown-confluence-sync/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ The namespace for the configuration of this library is `markdown-confluence-sync
| `filesMetadata` | `array` | Array of objects with the metadata of the files to sync. Each object must have the `path` property with the path of the file. For the rest of properties read the [Configuration per page](#configuration-per-page) section | |
| `docsDir` | `string` | Path to the docs directory. | `./docs` |
| `confluence.url` | `string` | URL of the Confluence instance. | |
| `confluence.apiPrefix` | `string` | Optional prefix for the Confluence API urls | `/rest/` |
| `confluence.personalAccessToken` | `string` | Deprecated. Personal access token to authenticate against the Confluence instance. | |
| `confluence.authentication` | `object` | Object containing authentication options to access the Confluence instance. It supports the same methods [as the `confluence.js` library](https://github.com/MrRefactoring/confluence.js?tab=readme-ov-file#authentication). | |
| `confluence.authentication.oauth2` | `object` | Object containing OAuth2 authentication options. | |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import type {
NoticeTemplateOption,
AuthenticationOptionDefinition,
AuthenticationOption,
ApiPrefixOption,
ApiPrefixOptionDefinition,
} from "./ConfluenceSync.types.js";
import { ConfluencePageTransformer } from "./transformer/ConfluencePageTransformer.js";
import type { ConfluencePageTransformerInterface } from "./transformer/ConfluencePageTransformer.types.js";
Expand All @@ -44,6 +46,12 @@ const urlOption: UrlOptionDefinition = {
type: "string",
};

const apiPrefixOption: ApiPrefixOptionDefinition = {
name: "apiPrefix",
type: "string",
default: "/rest/",
};

const personalAccessTokenOption: PersonalAccessTokenOptionDefinition = {
name: "personalAccessToken",
type: "string",
Expand Down Expand Up @@ -91,6 +99,7 @@ export const ConfluenceSync: ConfluenceSyncConstructor = class ConfluenceSync
private _confluencePageTransformer: ConfluencePageTransformerInterface;
private _confluenceSyncPages: ConfluenceSyncPagesInterface;
private _urlOption: UrlOption;
private _apiPrefixOption: ApiPrefixOption;
private _personalAccessTokenOption: PersonalAccessTokenOption;
private _spaceKeyOption: SpaceKeyOption;
private _rootPageIdOption: RootPageIdOption;
Expand All @@ -105,6 +114,9 @@ export const ConfluenceSync: ConfluenceSyncConstructor = class ConfluenceSync

constructor({ config, logger, mode }: ConfluenceSyncOptions) {
this._urlOption = config.addOption(urlOption) as UrlOption;
this._apiPrefixOption = config.addOption(
apiPrefixOption,
) as ApiPrefixOption;
this._personalAccessTokenOption = config.addOption(
personalAccessTokenOption,
) as PersonalAccessTokenOption;
Expand Down Expand Up @@ -132,6 +144,9 @@ export const ConfluenceSync: ConfluenceSyncConstructor = class ConfluenceSync
public async sync(confluencePages: ConfluenceSyncPage[]): Promise<void> {
await this._init();
this._logger.debug(`confluence.url option is ${this._urlOption.value}`);
this._logger.debug(
`confluence.apiPrefix option is ${this._apiPrefixOption.value}`,
);
this._logger.debug(
`confluence.spaceKey option is ${this._spaceKeyOption.value}`,
);
Expand Down Expand Up @@ -193,6 +208,7 @@ export const ConfluenceSync: ConfluenceSyncConstructor = class ConfluenceSync

this._confluenceSyncPages = new ConfluenceSyncPages({
url: this._urlOption.value,
apiPrefix: this._apiPrefixOption.value,
personalAccessToken: this._personalAccessTokenOption.value,
authentication: this._authenticationOption.value,
spaceId: this._spaceKeyOption.value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
import type { ModeOption } from "../MarkdownConfluenceSync.types";

type UrlOptionValue = string;
type ApiPrefixOptionValue = string;
type PersonalAccessTokenOptionValue = string;
type SpaceKeyOptionValue = string;
type RootPageIdOptionValue = string;
Expand All @@ -30,6 +31,8 @@ declare global {
confluence?: {
/** Confluence URL */
url?: UrlOptionValue;
/** Optional prefix for the Confluence API urls. Default is `/rest/`. */
apiPrefix?: string;
/**
* Confluence personal access token
* @deprecated Use authentication.oauth2.accessToken instead
Expand All @@ -55,6 +58,7 @@ declare global {
}

export type UrlOptionDefinition = OptionDefinition<UrlOptionValue>;
export type ApiPrefixOptionDefinition = OptionDefinition<ApiPrefixOptionValue>;
export type PersonalAccessTokenOptionDefinition =
OptionDefinition<PersonalAccessTokenOptionValue>;
export type SpaceKeyOptionDefinition = OptionDefinition<SpaceKeyOptionValue>;
Expand All @@ -77,6 +81,7 @@ export type AuthenticationOptionDefinition =
export type AuthenticationOption =
OptionInterfaceOfType<ConfluenceClientAuthenticationConfig>;
export type UrlOption = OptionInterfaceOfType<UrlOptionValue>;
export type ApiPrefixOption = OptionInterfaceOfType<ApiPrefixOptionValue>;
export type PersonalAccessTokenOption =
OptionInterfaceOfType<PersonalAccessTokenOptionValue>;
export type SpaceKeyOption = OptionInterfaceOfType<SpaceKeyOptionValue>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,59 @@ describe("configuration", () => {
expect(cleanLogs(logs)).toContain(`mode option is flat`);
});

it("should set apiPrefix to default value when not provided", async () => {
cli = new ChildProcessManager(
[
getBinaryPathFromFixtureFolder(),
"--confluence.url=https://foo-confluence.com",
"--confluence.spaceKey=FOO",
"--mode=tree",
"--confluence.rootPageId=123456",
"--confluence.personalAccessToken=abcd1234",
],
{
cwd: getFixtureFolder("basic"),
silent: true,
},
);
const { exitCode, logs } = await cli.run();

expect(exitCode).toBe(1);
expect(cleanLogs(logs)).toEqual(
expect.arrayContaining([
expect.stringContaining(`confluence.apiPrefix option is /rest/`),
]),
);
});

it("should set apiPrefix to given value when provided", async () => {
cli = new ChildProcessManager(
[
getBinaryPathFromFixtureFolder(),
"--confluence.url=https://foo-confluence.com",
"--confluence.spaceKey=FOO",
"--mode=tree",
"--confluence.rootPageId=123456",
"--confluence.personalAccessToken=abcd1234",
"--confluence.apiPrefix=/custom-api/",
],
{
cwd: getFixtureFolder("basic"),
silent: true,
},
);
const { exitCode, logs } = await cli.run();

expect(exitCode).toBe(1);
expect(cleanLogs(logs)).toEqual(
expect.arrayContaining([
expect.stringContaining(
`confluence.apiPrefix option is /custom-api/`,
),
]),
);
});

it(`should fail and throw log error when mode isn't valid mode`, async () => {
cli = new ChildProcessManager(
[getBinaryPathFromFixtureFolder(), "--mode=foo"],
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"lint": "eslint *.* --no-warn-ignored",
"lint:staged": "lint-staged",
"prepare": "husky install",
"check:all": "pnpm nx run-many -t check:all --all",
"check:spell": "cspell *.* .husky/*.* .github/*.*"
},
"devDependencies": {
Expand Down
Loading