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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [X.X.X] - Unreleased

### Added

- Added support for GET /2.0/sheets/{sheetId}/path endpoint (`getSheetPath`)
- Added support for GET /2.0/reports/{reportId}/path endpoint (`getReportPath`)
- Added support for GET /2.0/sights/{sightId}/path endpoint (`getSightPath`)
- Added support for GET /2.0/folders/{folderId}/path endpoint (`getFolderPath`)
- Added helper functions `getLeaf<Asset>()` and `getLeaf<Asset>Path()` for convenient traversal of the path node objects

## [5.0.1] - 2026-06-10

### Fixed
Expand Down
45 changes: 45 additions & 0 deletions lib/folders/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { FolderPathNode } from './types';

/**
* Returns the deepest {@link FolderPathNode} (the target folder).
* Use this for quick access to the leaf folder object from a path response.
*
* @param node - The root {@link FolderPathNode} returned by `getFolderPath`
* @returns The deepest (leaf) {@link FolderPathNode}
*
* @example
* ```typescript
* const path = await client.folders.getFolderPath({ folderId: 7116448184199044 });
* const leaf = getLeafFolder(path);
* console.log(leaf.name);
* ```
*/
export function getLeafFolder(node: FolderPathNode): FolderPathNode {
if (node.folders && node.folders.length > 0) {
return getLeafFolder(node.folders[0]);
}

return node;
}

/**
* Returns a Unix-like slash-separated path string from the given node to the target folder.
* Use this for quick access to the full path string of the leaf folder from a path response.
*
* @param node - The root {@link FolderPathNode} returned by `getFolderPath`
* @returns A slash-separated path string
*
* @example
* ```typescript
* const path = await client.folders.getFolderPath({ folderId: 7116448184199044 });
* const pathStr = getLeafFolderPath(path);
* console.log(pathStr); // '/Workspace/Folder/Subfolder'
* ```
*/
export function getLeafFolderPath(node: FolderPathNode): string {
if (node.folders && node.folders.length > 0) {
return `/${node.name}${getLeafFolderPath(node.folders[0])}`;
}

return `/${node.name}`;
}
11 changes: 11 additions & 0 deletions lib/folders/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import type {
MoveFolderOptions,
MoveFolderResponse,
CopyFolderResponse,
GetFolderPathOptions,
} from './types';
import type { FolderPathNode } from './types';

export function create(options: CreateOptions): FoldersApi {
const requestor = options.requestor;
Expand Down Expand Up @@ -83,6 +85,14 @@ export function create(options: CreateOptions): FoldersApi {
return requestor.get({ ...optionsToSend, ...urlOptions, ...getOptions }, callback);
};

const getFolderPath = (
getOptions: GetFolderPathOptions,
callback?: RequestCallback<FolderPathNode>
): Promise<FolderPathNode> => {
const urlOptions = { url: options.apiUrls.folders + '/' + getOptions.folderId + '/path' };
return requestor.get({ ...optionsToSend, ...urlOptions, ...getOptions }, callback);
};

return {
getFolderMetadata,
getFolderChildren,
Expand All @@ -91,5 +101,6 @@ export function create(options: CreateOptions): FoldersApi {
deleteFolder,
moveFolder,
copyFolder,
getFolderPath,
};
}
49 changes: 49 additions & 0 deletions lib/folders/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { RequestCallback } from '../types/RequestCallback';
import type { RequestOptions } from '../types/RequestOptions';
import type { BaseResponseStatus } from '../types/BaseResponseStatus';
import type { FailedItem } from '../types/FailedItem';
import type { PathNode } from '../types/PathNode';

// ============================================================================
// Folders API Interface
Expand Down Expand Up @@ -196,6 +197,25 @@ export interface FoldersApi {
options: MoveFolderOptions,
callback?: RequestCallback<MoveFolderResponse>
) => Promise<MoveFolderResponse>;

/**
* Gets the path from the workspace root to the specified folder.
*
* @param options - {@link GetFolderPathOptions} - Configuration options for the request
* @param callback - {@link RequestCallback}\<{@link FolderPathNode}\> - Optional callback function
* @returns Promise\<{@link FolderPathNode}\>
*
* @remarks
* It mirrors to the following Smartsheet REST API method: `GET /folders/{folderId}/path`
*
* @example
* ```typescript
* const path = await client.folders.getFolderPath({
* folderId: 7116448184199044
* });
* ```
*/
getFolderPath: (options: GetFolderPathOptions, callback?: RequestCallback<FolderPathNode>) => Promise<FolderPathNode>;
}

// ============================================================================
Expand Down Expand Up @@ -716,3 +736,32 @@ export interface MoveFolderResponse extends BaseResponseStatus {
*/
result: Folder;
}

// ============================================================================
// Get Folder Path
Comment thread
astrinski-smartsheet marked this conversation as resolved.
// ============================================================================

/**
* Represents a node in the path tree from the workspace root to the target folder.
* Returned by `getFolderPath`. Use {@link getLeafFolder} to extract the deepest folder object,
* or {@link getLeafFolderPath} to get the full slash-separated path string.
*
* @see getLeafFolder
* @see getLeafFolderPath
*/
export interface FolderPathNode extends PathNode {
folders?: FolderPathNode[];
}

/**
* Options for getting the path from the workspace root to the specified folder.
*
* @remarks
* It mirrors to the following Smartsheet REST API method: `GET /folders/{folderId}/path`
*/
export interface GetFolderPathOptions extends RequestOptions<undefined, undefined> {
Comment thread
astrinski-smartsheet marked this conversation as resolved.
/**
* Folder Id.
*/
folderId: number;
}
54 changes: 54 additions & 0 deletions lib/reports/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { PathLeaf } from '../types/PathLeaf';
import type { ReportPathNode } from './types';

/**
* Returns the target {@link PathLeaf} report, or undefined if not reachable.
* Use this for quick access to the leaf report object from a path response.
*
* @param node - The root {@link ReportPathNode} returned by `getReportPath`
* @returns The leaf {@link PathLeaf} report, or `undefined` if not found
*
* @example
* ```typescript
* const path = await client.reports.getReportPath({ reportId: 4583173393803140 });
* const leaf = getLeafReport(path);
* console.log(leaf?.name);
* ```
*/
export function getLeafReport(node: ReportPathNode): PathLeaf | undefined {
if (node.reports && node.reports.length > 0) {
return node.reports[0];
}

if (node.folders && node.folders.length > 0) {
return getLeafReport(node.folders[0]);
}

return undefined;
}

/**
* Returns a Unix-like slash-separated path string from the given node to the target report.
* Use this for quick access to the full path string of the leaf report from a path response.
*
* @param node - The root {@link ReportPathNode} returned by `getReportPath`
* @returns A slash-separated path string, or `undefined` if not reachable
*
* @example
* ```typescript
* const path = await client.reports.getReportPath({ reportId: 4583173393803140 });
* const pathStr = getLeafReportPath(path);
* console.log(pathStr); // '/Workspace/Folder/Report'
* ```
*/
export function getLeafReportPath(node: ReportPathNode): string | undefined {
if (node.reports && node.reports.length > 0) {
return `/${node.reports[0].name}`;
}

if (node.folders && node.folders.length > 0) {
return `/${node.name}${getLeafReportPath(node.folders[0])}`;
}

return undefined;
}
11 changes: 11 additions & 0 deletions lib/reports/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import type {
AddReportColumnsResponse,
CreateReportOptions,
CreateReportResponse,
GetReportPathOptions,
} from './types';
import type { ReportPathNode } from './types';
import * as constants from '../utils/constants';

export function create(options: CreateOptions): ReportsApi {
Expand Down Expand Up @@ -140,6 +142,14 @@ export function create(options: CreateOptions): ReportsApi {
return requestor.post({ ...optionsToSend, ...urlOptions, ...postOptions }, callback);
};

const getReportPath = (
getOptions: GetReportPathOptions,
callback?: RequestCallback<ReportPathNode>
): Promise<ReportPathNode> => {
const urlOptions = { url: options.apiUrls.reports + '/' + getOptions.reportId + '/path' };
return requestor.get({ ...optionsToSend, ...urlOptions, ...getOptions }, callback);
};

return {
listReports,
getReport,
Expand All @@ -154,5 +164,6 @@ export function create(options: CreateOptions): ReportsApi {
removeReportScope,
addReportColumns,
createReport,
getReportPath,
};
}
51 changes: 51 additions & 0 deletions lib/reports/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { PathLeaf } from '../types/PathLeaf';
import type { PathNode } from '../types/PathNode';
import type { RequestCallback } from '../types/RequestCallback';
import type { RequestOptions } from '../types/RequestOptions';
import type { BaseResponseStatus } from '../types/BaseResponseStatus';
Expand Down Expand Up @@ -430,6 +432,25 @@
options: CreateReportOptions,
callback?: RequestCallback<CreateReportResponse>
) => Promise<CreateReportResponse>;

/**
* Gets the path from the workspace root to the specified report.
*
* @param options - {@link GetReportPathOptions} - Configuration options for the request
* @param callback - {@link RequestCallback}\<{@link ReportPathNode}\> - Optional callback function
* @returns Promise\<{@link ReportPathNode}\>
*
* @remarks
* It mirrors to the following Smartsheet REST API method: `GET /reports/{reportId}/path`
*
* @example
* ```typescript
* const path = await client.reports.getReportPath({
* reportId: 4583173393803140
* });
* ```
*/
getReportPath: (options: GetReportPathOptions, callback?: RequestCallback<ReportPathNode>) => Promise<ReportPathNode>;
}

// ============================================================================
Expand Down Expand Up @@ -1418,7 +1439,7 @@
fill: string;
/**
* The prefix. Can include these date tokens:
* - {DD}

Check warning on line 1442 in lib/reports/types.ts

View workflow job for this annotation

GitHub Actions / lint

tsdoc-malformed-inline-tag: Expecting a TSDoc tag starting with "{@"
* - {MM}
* - {YY}
* - {YYYY}
Expand Down Expand Up @@ -1639,3 +1660,33 @@
*/
result: CreateReportResult;
}

// ============================================================================
// Get Report Path
// ============================================================================

/**
* Represents a node in the path tree from the workspace root to the target report.
* Returned by `getReportPath`. Use {@link getLeafReport} to extract the leaf report object,
* or {@link getLeafReportPath} to get the full slash-separated path string.
*
* @see getLeafReport
* @see getLeafReportPath
*/
export interface ReportPathNode extends PathNode {
folders?: ReportPathNode[];
reports?: PathLeaf[];
}

/**
* Options for getting the path from the workspace root to the specified report.
*
* @remarks
* It mirrors to the following Smartsheet REST API method: `GET /reports/{reportId}/path`
*/
export interface GetReportPathOptions extends RequestOptions<undefined, undefined> {
/**
* Report Id.
*/
reportId: number;
}
54 changes: 54 additions & 0 deletions lib/sheets/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { PathLeaf } from '../types/PathLeaf';
import type { SheetPathNode } from './types';

/**
* Returns the target {@link PathLeaf} sheet, or undefined if not reachable.
* Use this for quick access to the leaf sheet object from a path response.
*
* @param node - The root {@link SheetPathNode} returned by `getSheetPath`
* @returns The leaf {@link PathLeaf} sheet, or `undefined` if not found
*
* @example
* ```typescript
* const path = await client.sheets.getSheetPath({ sheetId: 123456789 });
* const leaf = getLeafSheet(path);
* console.log(leaf?.name);
* ```
*/
export function getLeafSheet(node: SheetPathNode): PathLeaf | undefined {
if (node.sheets && node.sheets.length > 0) {
return node.sheets[0];
}

if (node.folders && node.folders.length > 0) {
return getLeafSheet(node.folders[0]);
}

return undefined;
}

/**
* Returns a Unix-like slash-separated path string from the given node to the target sheet.
* Use this for quick access to the full path string of the leaf sheet from a path response.
*
* @param node - The root {@link SheetPathNode} returned by `getSheetPath`
* @returns A slash-separated path string, or `undefined` if not reachable
*
* @example
* ```typescript
* const path = await client.sheets.getSheetPath({ sheetId: 123456789 });
* const pathStr = getLeafSheetPath(path);
* console.log(pathStr); // '/Workspace/Folder/Sheet'
* ```
*/
export function getLeafSheetPath(node: SheetPathNode): string | undefined {
if (node.sheets && node.sheets.length > 0) {
return `/${node.sheets[0].name}`;
}

if (node.folders && node.folders.length > 0) {
return `/${node.name}${getLeafSheetPath(node.folders[0])}`;
}

return undefined;
}
6 changes: 6 additions & 0 deletions lib/sheets/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ export function create(options) {
return requestor.post(_.extend({}, optionsToSend, urlOptions, postOptions), callback);
};

const getSheetPath = (getOptions, callback) => {
const urlOptions = { url: options.apiUrls.sheets + '/' + getOptions.sheetId + '/path' };
return requestor.get(_.extend({}, optionsToSend, urlOptions, getOptions), callback);
};

const sheetObject = {
sendSheetViaEmail: sendSheetViaEmail,
getPublishStatus: getPublishStatus,
Expand All @@ -63,6 +68,7 @@ export function create(options) {
deleteSheet: deleteSheet,
moveSheet: moveSheet,
sortRowsInSheet: sortRowsInSheet,
getSheetPath: getSheetPath,
};

_.extend(sheetObject, attachments.create(options));
Expand Down
Loading
Loading