Skip to content
Closed
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
11 changes: 7 additions & 4 deletions docs/user-guide/studio-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,15 @@ content-cli push analysis --help

## Pull and Push View Bookmarks

Enable users to pull and push view (board) bookmarks using content-cli. For pulling view bookmarks
you can specify --type (SHARED/ALL/USER), and by default it fetches USER bookmarks:
Enable users to pull and push view (board) bookmarks using content-cli. Views (boards) are
identified by their stable node key (`--rootNodeKeyWithBoardKey` and `--key`) rather than the board id, so
the same command works across teams even after a team-to-team copy regenerates the board id. For
pulling view bookmarks you can specify --type (SHARED/ALL/USER), and by default it fetches USER
bookmarks:

```
// Pull view bookmarks
content-cli pull view-bookmarks --profile my-profile-name --id 73d39112-73ae-4bbe-8051-3c0f14e065ec --type SHARED
content-cli pull view-bookmarks --profile my-profile-name --rootNodeKeyWithBoardKey my-package --key my-board --type SHARED
```

After you have pulled your view bookmarks,
Expand All @@ -225,5 +228,5 @@ the same command as with pushing other assets in Studio:

```
// Push view bookmarks to Studio
content-cli push view-bookmarks -p my-profile-name --id 73d39112-73ae-4bbe-8051-3c0f14e065ec --file studio_view_bookmarks_39c5bb7b-b486-4230-ab01-854a17ddbff2.json
content-cli push view-bookmarks -p my-profile-name --rootNodeKeyWithBoardKey my-package --key my-board --file studio_view_bookmarks_my-package.my-board.json
```
10 changes: 6 additions & 4 deletions src/commands/view/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,26 @@ class Module extends IModule {
.command("view-bookmarks")
.description("Command to pull view bookmarks")
.option("--type <type>", "Type of view bookmarks to pull: USER (default), SHARED, or ALL")
.requiredOption("--id <id>", "ID of the view (board) to pull bookmarks from")
.requiredOption("--rootNodeKeyWithBoardKey <rootNodeKeyWithBoardKey>", "Root node key of the package the view (board) belongs to")
.requiredOption("--key <key>", "Stable node key of the view (board) to pull bookmarks from")
.action(this.pullViewBookmarks);

const pushCommand = configurator.command("push");
pushCommand
.command("view-bookmarks")
.description("Command to push view bookmarks to a board")
.requiredOption("--id <id>", "ID of the view (board) to push bookmarks into")
.requiredOption("--rootNodeKeyWithBoardKey <rootNodeKeyWithBoardKey>", "Root node key of the package the view (board) belongs to")
.requiredOption("--key <key>", "Stable node key of the view (board) to push bookmarks into")
.requiredOption("-f, --file <file>", "The file to push")
.action(this.pushViewBookmarks);
}

private async pullViewBookmarks(context: Context, command: Command, options: OptionValues): Promise<void> {
await new ViewBookmarksCommandService(context).pullViewBookmarks(options.id, options.type);
await new ViewBookmarksCommandService(context).pullViewBookmarks(options.rootNodeKeyWithBoardKey, options.key, options.type);
}

private async pushViewBookmarks(context: Context, command: Command, options: OptionValues): Promise<void> {
await new ViewBookmarksCommandService(context).pushViewBookmarks(options.id, options.file);
await new ViewBookmarksCommandService(context).pushViewBookmarks(options.rootNodeKeyWithBoardKey, options.key, options.file);
}
}

Expand Down
16 changes: 11 additions & 5 deletions src/commands/view/view-bookmarks-command.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,21 @@ export class ViewBookmarksCommandService {
this.viewBookmarksManagerFactory = new ViewBookmarksManagerFactory(context);
}

public async pullViewBookmarks(boardId: string, type?: string): Promise<void> {
public async pullViewBookmarks(rootNodeKeyWithBoardKey: string, key: string, type?: string): Promise<void> {
if (type !== undefined && !ALLOWED_VIEW_BOOKMARK_TYPES.includes(type.toUpperCase())) {
logger.error(new FatalError(`Invalid type "${type}". Allowed values are: ${ALLOWED_VIEW_BOOKMARK_TYPES.join(", ")}.`));
logger.error(
new FatalError(`Invalid type "${type}". Allowed values are: ${ALLOWED_VIEW_BOOKMARK_TYPES.join(", ")}.`)
);
return;
}
await this.viewBookmarksManagerFactory.createViewBookmarksManager(null, boardId, type).pull();
await this.viewBookmarksManagerFactory
.createViewBookmarksManager(null, rootNodeKeyWithBoardKey, key, type)
.pull();
}

public async pushViewBookmarks(boardId: string, filename: string): Promise<void> {
await this.viewBookmarksManagerFactory.createViewBookmarksManager(filename, boardId).push();
public async pushViewBookmarks(rootNodeKeyWithBoardKey: string, key: string, filename: string): Promise<void> {
await this.viewBookmarksManagerFactory
.createViewBookmarksManager(filename, rootNodeKeyWithBoardKey, key)
.push();
}
}
10 changes: 8 additions & 2 deletions src/commands/view/view-bookmarks.manager-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,15 @@ export class ViewBookmarksManagerFactory {
this.context = context;
}

public createViewBookmarksManager(filename: string, boardId: string, type?: string): ViewBookmarksManager {
public createViewBookmarksManager(
filename: string,
rootNodeKeyWithBoardKey: string,
key: string,
type?: string
): ViewBookmarksManager {
const viewBookmarksManager = new ViewBookmarksManager(this.context);
viewBookmarksManager.boardId = boardId;
viewBookmarksManager.rootNodeKeyWithBoardKey = rootNodeKeyWithBoardKey;
viewBookmarksManager.key = key;
type = (type ?? "USER").toUpperCase();

viewBookmarksManager.type = type;
Expand Down
26 changes: 18 additions & 8 deletions src/commands/view/view-bookmarks.manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ export class ViewBookmarksManager extends BaseManager {
private static readonly BASE_URL = "/blueprint/api/bookmarks";
private static readonly VIEW_BOOKMARKS_FILE_PREFIX = "studio_view_bookmarks_";

private _boardId: string;
private _rootNodeKeyWithBoardKey: string;
private _key: string;
private _filePath: string;
private _type: string;

Expand All @@ -24,12 +25,20 @@ export class ViewBookmarksManager extends BaseManager {
this._filePath = value;
}

public get boardId(): string {
return this._boardId;
public get rootNodeKeyWithBoardKey(): string {
return this._rootNodeKeyWithBoardKey;
}

public set boardId(value: string) {
this._boardId = value;
public set rootNodeKeyWithBoardKey(value: string) {
this._rootNodeKeyWithBoardKey = value;
}

public get key(): string {
return this._key;
}

public set key(value: string) {
this._key = value;
}

public get type(): string {
Expand All @@ -41,10 +50,11 @@ export class ViewBookmarksManager extends BaseManager {
}

public getConfig(): ManagerConfig {
const keyParams = `rootNodeKeyWithBoardKey=${encodeURIComponent(this.rootNodeKeyWithBoardKey)}&key=${encodeURIComponent(this.key)}`;
return {
pushUrl: `${ViewBookmarksManager.BASE_URL}/import?boardId=${encodeURIComponent(this.boardId)}`,
pullUrl: `${ViewBookmarksManager.BASE_URL}/export?boardId=${encodeURIComponent(this.boardId)}&type=${encodeURIComponent(this.type)}`,
exportFileName: `${ViewBookmarksManager.VIEW_BOOKMARKS_FILE_PREFIX}${this.boardId}.json`,
pushUrl: `${ViewBookmarksManager.BASE_URL}/import?${keyParams}`,
pullUrl: `${ViewBookmarksManager.BASE_URL}/export?${keyParams}&type=${encodeURIComponent(this.type)}`,
exportFileName: `${ViewBookmarksManager.VIEW_BOOKMARKS_FILE_PREFIX}${this.rootNodeKeyWithBoardKey}.${this.key}.json`,
onPushSuccessMessage: (): string => {
return "View Bookmarks were pushed successfully.";
},
Expand Down
12 changes: 6 additions & 6 deletions tests/commands/view/view-bookmarks-module.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,16 @@ describe("View Bookmarks Module", () => {
.mockImplementation(() => mockService);
});

it("should call pullViewBookmarks with id and type", async () => {
const options: OptionValues = { id: "board-123", type: "SHARED" };
it("should call pullViewBookmarks with rootNodeKeyWithBoardKey, key and type", async () => {
const options: OptionValues = { rootNodeKeyWithBoardKey: "my-package", key: "my-board", type: "SHARED" };
await (module as any).pullViewBookmarks(testContext, mockCommand, options);
expect(mockService.pullViewBookmarks).toHaveBeenCalledWith("board-123", "SHARED");
expect(mockService.pullViewBookmarks).toHaveBeenCalledWith("my-package", "my-board", "SHARED");
});

it("should call pushViewBookmarks with id and file", async () => {
const options: OptionValues = { id: "board-123", file: "bookmarks.json" };
it("should call pushViewBookmarks with rootNodeKeyWithBoardKey, key and file", async () => {
const options: OptionValues = { rootNodeKeyWithBoardKey: "my-package", key: "my-board", file: "bookmarks.json" };
await (module as any).pushViewBookmarks(testContext, mockCommand, options);
expect(mockService.pushViewBookmarks).toHaveBeenCalledWith("board-123", "bookmarks.json");
expect(mockService.pushViewBookmarks).toHaveBeenCalledWith("my-package", "my-board", "bookmarks.json");
});

describe("register", () => {
Expand Down
16 changes: 9 additions & 7 deletions tests/commands/view/view-bookmarks.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import { getJsonFromDownloadedFile, writeJsonTempFile } from "../../utls/fs-util

describe("View bookmarks", () => {

const boardId = "73d39112-73ae-4bbe-8051-3c0f14e065ec";
const exportBaseUrl = `https://myTeam.celonis.cloud/blueprint/api/bookmarks/export?boardId=${boardId}`;
const importUrl = `https://myTeam.celonis.cloud/blueprint/api/bookmarks/import?boardId=${boardId}`;
const rootNodeKeyWithBoardKey = "my-package";
const key = "my-board";
const keyParams = `rootNodeKeyWithBoardKey=${rootNodeKeyWithBoardKey}&key=${key}`;
const exportBaseUrl = `https://myTeam.celonis.cloud/blueprint/api/bookmarks/export?${keyParams}`;
const importUrl = `https://myTeam.celonis.cloud/blueprint/api/bookmarks/import?${keyParams}`;
const bookmarksResponse = [
{
bookmark: { name: "My View Bookmark", ownerId: "user-1", userPreferenceId: "pref-1" },
Expand All @@ -23,7 +25,7 @@ describe("View bookmarks", () => {
it("Should call export API with the default USER type and write the response to a file", async () => {
mockAxiosGet(`${exportBaseUrl}&type=USER`, bookmarksResponse);

await new ViewBookmarksCommandService(testContext).pullViewBookmarks(boardId, undefined);
await new ViewBookmarksCommandService(testContext).pullViewBookmarks(rootNodeKeyWithBoardKey, key, undefined);

expect(mockedAxiosInstance.get).toHaveBeenCalledWith(`${exportBaseUrl}&type=USER`, expect.anything());
expect(getJsonFromDownloadedFile()).toEqual(bookmarksResponse);
Expand All @@ -34,7 +36,7 @@ describe("View bookmarks", () => {
it("Should call export API with the provided type", async () => {
mockAxiosGet(`${exportBaseUrl}&type=SHARED`, bookmarksResponse);

await new ViewBookmarksCommandService(testContext).pullViewBookmarks(boardId, "SHARED");
await new ViewBookmarksCommandService(testContext).pullViewBookmarks(rootNodeKeyWithBoardKey, key, "SHARED");

expect(mockedAxiosInstance.get).toHaveBeenCalledWith(`${exportBaseUrl}&type=SHARED`, expect.anything());
expect(getJsonFromDownloadedFile()).toEqual(bookmarksResponse);
Expand All @@ -46,7 +48,7 @@ describe("View bookmarks", () => {
mockAxiosPost(importUrl, {});
writeJsonTempFile("bookmarks.json", bookmarksResponse);

await new ViewBookmarksCommandService(testContext).pushViewBookmarks(boardId, "bookmarks.json");
await new ViewBookmarksCommandService(testContext).pushViewBookmarks(rootNodeKeyWithBoardKey, key, "bookmarks.json");

expect(mockedAxiosInstance.post).toHaveBeenCalledWith(importUrl, expect.anything(), expect.anything());
expect(loggingTestTransport.logMessages.length).toBe(1);
Expand All @@ -58,7 +60,7 @@ describe("View bookmarks", () => {
it("Should report a fatal error when the push file does not exist", () => {
const exitSpy = jest.spyOn(process, "exit").mockImplementation((() => undefined) as never);

new ViewBookmarksManagerFactory(testContext).createViewBookmarksManager("missing.json", boardId);
new ViewBookmarksManagerFactory(testContext).createViewBookmarksManager("missing.json", rootNodeKeyWithBoardKey, key);

expect(exitSpy).toHaveBeenCalledWith(1);
});
Expand Down
Loading