diff --git a/CHANGELOG.md b/CHANGELOG.md index 8238cb7..0669224 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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()` and `getLeafPath()` for convenient traversal of the path node objects + ## [5.0.1] - 2026-06-10 ### Fixed diff --git a/lib/folders/helpers.ts b/lib/folders/helpers.ts new file mode 100644 index 0000000..a680934 --- /dev/null +++ b/lib/folders/helpers.ts @@ -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}`; +} diff --git a/lib/folders/index.ts b/lib/folders/index.ts index 1e7c675..da44af2 100644 --- a/lib/folders/index.ts +++ b/lib/folders/index.ts @@ -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; @@ -83,6 +85,14 @@ export function create(options: CreateOptions): FoldersApi { return requestor.get({ ...optionsToSend, ...urlOptions, ...getOptions }, callback); }; + const getFolderPath = ( + getOptions: GetFolderPathOptions, + callback?: RequestCallback + ): Promise => { + const urlOptions = { url: options.apiUrls.folders + '/' + getOptions.folderId + '/path' }; + return requestor.get({ ...optionsToSend, ...urlOptions, ...getOptions }, callback); + }; + return { getFolderMetadata, getFolderChildren, @@ -91,5 +101,6 @@ export function create(options: CreateOptions): FoldersApi { deleteFolder, moveFolder, copyFolder, + getFolderPath, }; } diff --git a/lib/folders/types.ts b/lib/folders/types.ts index a5b4faf..27e9fb3 100644 --- a/lib/folders/types.ts +++ b/lib/folders/types.ts @@ -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 @@ -196,6 +197,25 @@ export interface FoldersApi { options: MoveFolderOptions, callback?: RequestCallback ) => Promise; + + /** + * 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) => Promise; } // ============================================================================ @@ -716,3 +736,32 @@ export interface MoveFolderResponse extends BaseResponseStatus { */ result: Folder; } + +// ============================================================================ +// Get Folder Path +// ============================================================================ + +/** + * 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 { + /** + * Folder Id. + */ + folderId: number; +} diff --git a/lib/reports/helpers.ts b/lib/reports/helpers.ts new file mode 100644 index 0000000..c5cc55b --- /dev/null +++ b/lib/reports/helpers.ts @@ -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; +} diff --git a/lib/reports/index.ts b/lib/reports/index.ts index fe1b47e..6f743ab 100644 --- a/lib/reports/index.ts +++ b/lib/reports/index.ts @@ -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 { @@ -140,6 +142,14 @@ export function create(options: CreateOptions): ReportsApi { return requestor.post({ ...optionsToSend, ...urlOptions, ...postOptions }, callback); }; + const getReportPath = ( + getOptions: GetReportPathOptions, + callback?: RequestCallback + ): Promise => { + const urlOptions = { url: options.apiUrls.reports + '/' + getOptions.reportId + '/path' }; + return requestor.get({ ...optionsToSend, ...urlOptions, ...getOptions }, callback); + }; + return { listReports, getReport, @@ -154,5 +164,6 @@ export function create(options: CreateOptions): ReportsApi { removeReportScope, addReportColumns, createReport, + getReportPath, }; } diff --git a/lib/reports/types.ts b/lib/reports/types.ts index 380dc3e..69eb1ae 100644 --- a/lib/reports/types.ts +++ b/lib/reports/types.ts @@ -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'; @@ -430,6 +432,25 @@ export interface ReportsApi { options: CreateReportOptions, callback?: RequestCallback ) => Promise; + + /** + * 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) => Promise; } // ============================================================================ @@ -1639,3 +1660,33 @@ export interface CreateReportResponse extends BaseResponseStatus { */ 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 { + /** + * Report Id. + */ + reportId: number; +} diff --git a/lib/sheets/helpers.ts b/lib/sheets/helpers.ts new file mode 100644 index 0000000..0fcc09e --- /dev/null +++ b/lib/sheets/helpers.ts @@ -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; +} diff --git a/lib/sheets/index.js b/lib/sheets/index.js index e18394b..38e207f 100644 --- a/lib/sheets/index.js +++ b/lib/sheets/index.js @@ -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, @@ -63,6 +68,7 @@ export function create(options) { deleteSheet: deleteSheet, moveSheet: moveSheet, sortRowsInSheet: sortRowsInSheet, + getSheetPath: getSheetPath, }; _.extend(sheetObject, attachments.create(options)); diff --git a/lib/sheets/types.ts b/lib/sheets/types.ts index ccdaf79..a59cbe9 100644 --- a/lib/sheets/types.ts +++ b/lib/sheets/types.ts @@ -1,3 +1,23 @@ +// ============================================================================ +// Get Sheet Path +// ============================================================================ + +import type { PathLeaf } from '../types/PathLeaf'; +import type { PathNode } from '../types/PathNode'; + +/** + * Represents a node in the path tree from the workspace root to the target sheet. + * Returned by `getSheetPath`. Use {@link getLeafSheet} to extract the leaf sheet object, + * or {@link getLeafSheetPath} to get the full slash-separated path string. + * + * @see getLeafSheet + * @see getLeafSheetPath + */ +export interface SheetPathNode extends PathNode { + folders?: SheetPathNode[]; + sheets?: PathLeaf[]; +} + export interface SheetUserSettings { /** * Does this user have "Show Critical Path" turned on for this sheet? NOTE: This setting only has an effect on project sheets with dependencies enabled. diff --git a/lib/sights/helpers.ts b/lib/sights/helpers.ts new file mode 100644 index 0000000..9b55b97 --- /dev/null +++ b/lib/sights/helpers.ts @@ -0,0 +1,54 @@ +import type { PathLeaf } from '../types/PathLeaf'; +import type { SightPathNode } from './types'; + +/** + * Returns the target {@link PathLeaf} sight, or undefined if not reachable. + * Use this for quick access to the leaf sight object from a path response. + * + * @param node - The root {@link SightPathNode} returned by `getSightPath` + * @returns The leaf {@link PathLeaf} sight, or `undefined` if not found + * + * @example + * ```typescript + * const path = await client.sights.getSightPath({ sightId: 123456789 }); + * const leaf = getLeafSight(path); + * console.log(leaf?.name); + * ``` + */ +export function getLeafSight(node: SightPathNode): PathLeaf | undefined { + if (node.sights && node.sights.length > 0) { + return node.sights[0]; + } + + if (node.folders && node.folders.length > 0) { + return getLeafSight(node.folders[0]); + } + + return undefined; +} + +/** + * Returns a Unix-like slash-separated path string from the given node to the target sight. + * Use this for quick access to the full path string of the leaf sight from a path response. + * + * @param node - The root {@link SightPathNode} returned by `getSightPath` + * @returns A slash-separated path string, or `undefined` if not reachable + * + * @example + * ```typescript + * const path = await client.sights.getSightPath({ sightId: 123456789 }); + * const pathStr = getLeafSightPath(path); + * console.log(pathStr); // '/Workspace/Folder/Dashboard' + * ``` + */ +export function getLeafSightPath(node: SightPathNode): string | undefined { + if (node.sights && node.sights.length > 0) { + return `/${node.sights[0].name}`; + } + + if (node.folders && node.folders.length > 0) { + return `/${node.name}${getLeafSightPath(node.folders[0])}`; + } + + return undefined; +} diff --git a/lib/sights/index.ts b/lib/sights/index.ts index 46bcf62..27d6ba7 100644 --- a/lib/sights/index.ts +++ b/lib/sights/index.ts @@ -19,7 +19,9 @@ import type { SightPublishStatus, SetSightPublishStatusOptions, SetSightPublishStatusResponse, + GetSightPathOptions, } from './types'; +import type { SightPathNode } from './types'; export function create(options: CreateOptions): SightsApi { const requestor = options.requestor; @@ -84,6 +86,14 @@ export function create(options: CreateOptions): SightsApi { return options.apiUrls.sights; }; + const getSightPath = ( + getOptions: GetSightPathOptions, + callback?: RequestCallback + ): Promise => { + const urlOptions = { url: buildUrl(getOptions.sightId) + '/path' }; + return requestor.get({ ...optionsToSend, ...urlOptions, ...getOptions }, callback); + }; + return { listSights, getSight, @@ -93,6 +103,7 @@ export function create(options: CreateOptions): SightsApi { moveSight, getSightPublishStatus, setSightPublishStatus, + getSightPath, }; } diff --git a/lib/sights/types.ts b/lib/sights/types.ts index e153206..d9d467d 100644 --- a/lib/sights/types.ts +++ b/lib/sights/types.ts @@ -2,6 +2,8 @@ import type { RequestCallback } from '../types/RequestCallback'; import type { RequestOptions } from '../types/RequestOptions'; import type { BaseResponseStatus } from '../types/BaseResponseStatus'; import type { APIAccessLevel } from '../types/ApiAccessLevel'; +import type { PathLeaf } from '../types/PathLeaf'; +import type { PathNode } from '../types/PathNode'; import type { TokenPaginationQueryParameters, TokenPaginationResponse } from '../types'; // ============================================================================ @@ -193,6 +195,25 @@ export interface SightsApi { options: SetSightPublishStatusOptions, callback?: RequestCallback ) => Promise; + + /** + * Gets the path from the workspace root to the specified sight (dashboard). + * + * @param options - {@link GetSightPathOptions} - Configuration options for the request + * @param callback - {@link RequestCallback}\<{@link SightPathNode}\> - Optional callback function + * @returns Promise\<{@link SightPathNode}\> + * + * @remarks + * It mirrors to the following Smartsheet REST API method: `GET /sights/{sightId}/path` + * + * @example + * ```typescript + * const path = await client.sights.getSightPath({ + * sightId: 123456789 + * }); + * ``` + */ + getSightPath: (options: GetSightPathOptions, callback?: RequestCallback) => Promise; } // ============================================================================ @@ -585,3 +606,33 @@ export interface SetSightPublishStatusResponse extends BaseResponseStatus { */ result: SightPublishStatus; } + +// ============================================================================ +// Get Sight Path +// ============================================================================ + +/** + * Represents a node in the path tree from the workspace root to the target sight (dashboard). + * Returned by `getSightPath`. Use {@link getLeafSight} to extract the leaf sight object, + * or {@link getLeafSightPath} to get the full slash-separated path string. + * + * @see getLeafSight + * @see getLeafSightPath + */ +export interface SightPathNode extends PathNode { + folders?: SightPathNode[]; + sights?: PathLeaf[]; +} + +/** + * Options for getting the path from the workspace root to the specified sight (dashboard). + * + * @remarks + * It mirrors to the following Smartsheet REST API method: `GET /sights/{sightId}/path` + */ +export interface GetSightPathOptions extends RequestOptions { + /** + * Sight Id. + */ + sightId: number; +} diff --git a/lib/types/PathLeaf.ts b/lib/types/PathLeaf.ts new file mode 100644 index 0000000..967d24d --- /dev/null +++ b/lib/types/PathLeaf.ts @@ -0,0 +1,7 @@ +import type { PathNode } from './PathNode'; + +export interface PathLeaf extends PathNode { + accessLevel: NonNullable; + createdAt: string; + modifiedAt: string; +} diff --git a/lib/types/PathNode.ts b/lib/types/PathNode.ts new file mode 100644 index 0000000..b4f235f --- /dev/null +++ b/lib/types/PathNode.ts @@ -0,0 +1,8 @@ +import type { APIAccessLevel } from './ApiAccessLevel'; + +export interface PathNode { + id: number; + name: string; + permalink: string; + accessLevel?: APIAccessLevel; +} diff --git a/lib/types/index.ts b/lib/types/index.ts index 59bd2d4..7b71feb 100644 --- a/lib/types/index.ts +++ b/lib/types/index.ts @@ -7,6 +7,8 @@ export * from './CreateClientOptions'; export * from './CreateOptions'; export * from './FailedItem'; export * from './ObjectValue'; +export * from './PathLeaf'; +export * from './PathNode'; export * from './RequestCallback'; export * from './RequestOptions'; export * from './SmartsheetClient'; diff --git a/test/functional/client.spec.ts b/test/functional/client.spec.ts index 164b8d1..5210b99 100644 --- a/test/functional/client.spec.ts +++ b/test/functional/client.spec.ts @@ -87,12 +87,13 @@ describe('Client Unit Tests', () => { describe('#folders', () => { it('should have folders object', () => { expect(smartsheet).toHaveProperty('folders'); - expect(Object.keys(smartsheet.folders)).toHaveLength(7); + expect(Object.keys(smartsheet.folders)).toHaveLength(8); }); it('should have get methods', () => { expect(smartsheet.folders).toHaveProperty('getFolderMetadata'); expect(smartsheet.folders).toHaveProperty('getFolderChildren'); + expect(smartsheet.folders).toHaveProperty('getFolderPath'); }); it('should have create methods', () => { @@ -178,7 +179,7 @@ describe('Client Unit Tests', () => { describe('#reports', () => { it('should have reports object', () => { expect(smartsheet).toHaveProperty('reports'); - expect(Object.keys(smartsheet.reports)).toHaveLength(13); + expect(Object.keys(smartsheet.reports)).toHaveLength(14); }); it('should have get methods', () => { @@ -187,6 +188,7 @@ describe('Client Unit Tests', () => { expect(smartsheet.reports).toHaveProperty('getReportAsExcel'); expect(smartsheet.reports).toHaveProperty('getReportAsCSV'); expect(smartsheet.reports).toHaveProperty('getReportPublishStatus'); + expect(smartsheet.reports).toHaveProperty('getReportPath'); }); it('should have create methods', () => { @@ -314,13 +316,14 @@ describe('Client Unit Tests', () => { describe('#Sights', () => { it('should have Sights object', () => { expect(smartsheet).toHaveProperty('sights'); - expect(Object.keys(smartsheet.sights)).toHaveLength(8); + expect(Object.keys(smartsheet.sights)).toHaveLength(9); }); it('should have Sights get methods', () => { expect(smartsheet.sights).toHaveProperty('getSight'); expect(smartsheet.sights).toHaveProperty('listSights'); expect(smartsheet.sights).toHaveProperty('getSightPublishStatus'); + expect(smartsheet.sights).toHaveProperty('getSightPath'); }); it('should have Sights update methods', () => { diff --git a/test/mock-api/folders/common_test_constants.ts b/test/mock-api/folders/common_test_constants.ts index 137af8f..298310e 100644 --- a/test/mock-api/folders/common_test_constants.ts +++ b/test/mock-api/folders/common_test_constants.ts @@ -1,3 +1,9 @@ +// Common Workspace Properties (used for path tests) +export const TEST_PATH_WORKSPACE_ID = 4509918431602564; +export const TEST_PATH_WORKSPACE_NAME = 'Sample Workspace'; +export const TEST_PATH_WORKSPACE_PERMALINK = + 'https://app.smartsheet.com/workspaces/mock_workspace_id'; + // Common Folder IDs export const TEST_FOLDER_ID = 7116448184199044; export const TEST_CHILD_FOLDER_ID_1 = 1234567890123456; diff --git a/test/mock-api/folders/get_folder_path.spec.ts b/test/mock-api/folders/get_folder_path.spec.ts new file mode 100644 index 0000000..f885c86 --- /dev/null +++ b/test/mock-api/folders/get_folder_path.spec.ts @@ -0,0 +1,164 @@ +import crypto from 'crypto'; +import { createClient, findWireMockRequest } from '../utils/utils'; +import { expect } from '@jest/globals'; +import { + TEST_FOLDER_ID, + TEST_PATH_WORKSPACE_ID, + TEST_PATH_WORKSPACE_NAME, + TEST_PATH_WORKSPACE_PERMALINK, + ERROR_500_STATUS_CODE, + ERROR_500_MESSAGE, + ERROR_400_STATUS_CODE, + ERROR_400_MESSAGE, +} from './common_test_constants'; +import { APIAccessLevel } from '@smartsheet/types'; +import { getLeafFolder, getLeafFolderPath } from '@smartsheet/folders/helpers'; + +describe('Folders - getFolderPath endpoint tests', () => { + const client = createClient(); + + it('getFolderPath generated url is correct', async () => { + const requestId = crypto.randomUUID(); + const options = { + folderId: TEST_FOLDER_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/folders/get-nested-folder-path/all-response-body-properties', + }, + }; + await client.folders.getFolderPath(options); + const matchedRequest = await findWireMockRequest(requestId); + const parsedUrl = new URL(matchedRequest.absoluteUrl); + expect(parsedUrl.pathname).toEqual(`/2.0/folders/${TEST_FOLDER_ID}/path`); + expect(matchedRequest.method).toEqual('GET'); + const queryParamsObject = Object.fromEntries(parsedUrl.searchParams); + expect(queryParamsObject).toEqual({}); + }); + + it('getFolderPath all response body properties', async () => { + const requestId = crypto.randomUUID(); + const options = { + folderId: TEST_FOLDER_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/folders/get-nested-folder-path/all-response-body-properties', + }, + }; + const response = await client.folders.getFolderPath(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(matchedRequest.body).toEqual(''); + expect(response).toEqual({ + id: TEST_PATH_WORKSPACE_ID, + name: TEST_PATH_WORKSPACE_NAME, + permalink: TEST_PATH_WORKSPACE_PERMALINK, + accessLevel: APIAccessLevel.owner, + folders: [ + { + id: 1234567890123456, + name: 'Project Plans', + permalink: 'https://app.smartsheet.com/folders/1234567890123456', + folders: [ + { + id: 2345678901234567, + name: 'Project Plans Subfolder', + permalink: 'https://app.smartsheet.com/folders/2345678901234567', + folders: [ + { + id: 3456789012345678, + name: 'Project Plans Sub-Subfolder', + permalink: 'https://app.smartsheet.com/folders/3456789012345678', + }, + ], + }, + ], + }, + ], + }); + }); + + it('getFolderPath root level response body properties', async () => { + const requestId = crypto.randomUUID(); + const options = { + folderId: TEST_FOLDER_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/folders/get-root-folder-path/all-response-body-properties', + }, + }; + const response = await client.folders.getFolderPath(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(matchedRequest.body).toEqual(''); + expect(response).toEqual({ + id: TEST_PATH_WORKSPACE_ID, + name: TEST_PATH_WORKSPACE_NAME, + permalink: TEST_PATH_WORKSPACE_PERMALINK, + accessLevel: APIAccessLevel.owner, + folders: [ + { + id: 5678901234567890, + name: 'Root Level Folder', + permalink: 'https://app.smartsheet.com/folders/rootlevel', + }, + ], + }); + }); + + it('getFolderPath returns FolderPathNode compatible with getLeafFolder and getLeafFolderPath', async () => { + const requestId = crypto.randomUUID(); + const options = { + folderId: TEST_FOLDER_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/folders/get-nested-folder-path/all-response-body-properties', + }, + }; + const response = await client.folders.getFolderPath(options); + + expect(getLeafFolder(response)).toEqual({ + id: 3456789012345678, + name: 'Project Plans Sub-Subfolder', + permalink: 'https://app.smartsheet.com/folders/3456789012345678', + }); + expect(getLeafFolderPath(response)).toEqual( + '/Sample Workspace/Project Plans/Project Plans Subfolder/Project Plans Sub-Subfolder' + ); + }); + + it('getFolderPath error 500 response', async () => { + const requestId = crypto.randomUUID(); + const options = { + folderId: TEST_FOLDER_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/errors/500-response', + }, + }; + try { + await client.folders.getFolderPath(options); + expect(true).toBe(false); + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_500_STATUS_CODE); + expect(error.message).toBe(ERROR_500_MESSAGE); + } + }); + + it('getFolderPath error 400 response', async () => { + const requestId = crypto.randomUUID(); + const options = { + folderId: TEST_FOLDER_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/errors/400-response', + }, + }; + try { + await client.folders.getFolderPath(options); + expect(true).toBe(false); + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_400_STATUS_CODE); + expect(error.message).toBe(ERROR_400_MESSAGE); + } + }); +}); diff --git a/test/mock-api/reports/common_test_constants.ts b/test/mock-api/reports/common_test_constants.ts index b33eb68..79ff748 100644 --- a/test/mock-api/reports/common_test_constants.ts +++ b/test/mock-api/reports/common_test_constants.ts @@ -1,5 +1,15 @@ import { ReportColumnType } from '@smartsheet/reports/types'; +// Common Workspace Properties (used for path tests) +export const TEST_PATH_WORKSPACE_ID = 4509918431602564; +export const TEST_PATH_WORKSPACE_NAME = 'Sample Workspace'; +export const TEST_PATH_WORKSPACE_PERMALINK = + 'https://app.smartsheet.com/workspaces/mock_workspace_id'; + +// Common Path Timestamps +export const TEST_REPORT_CREATED_AT = '2024-01-01T00:00:00Z'; +export const TEST_REPORT_MODIFIED_AT = '2024-06-01T00:00:00Z'; + // Common Report IDs export const TEST_REPORT_ID = 4583173393803140; diff --git a/test/mock-api/reports/get_report_path.spec.ts b/test/mock-api/reports/get_report_path.spec.ts new file mode 100644 index 0000000..86e110e --- /dev/null +++ b/test/mock-api/reports/get_report_path.spec.ts @@ -0,0 +1,173 @@ +import crypto from 'crypto'; +import { createClient, findWireMockRequest } from '../utils/utils'; +import { expect } from '@jest/globals'; +import { APIAccessLevel } from '@smartsheet/types'; +import { getLeafReport, getLeafReportPath } from '@smartsheet/reports/helpers'; +import { + TEST_REPORT_ID, + TEST_REPORT_CREATED_AT, + TEST_REPORT_MODIFIED_AT, + TEST_PATH_WORKSPACE_ID, + TEST_PATH_WORKSPACE_NAME, + TEST_PATH_WORKSPACE_PERMALINK, + ERROR_500_STATUS_CODE, + ERROR_500_MESSAGE, + ERROR_400_STATUS_CODE, + ERROR_400_MESSAGE, +} from './common_test_constants'; + +describe('Reports - getReportPath endpoint tests', () => { + const client = createClient(); + + it('getReportPath generated url is correct', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/get-nested-report-path/all-response-body-properties', + }, + }; + await client.reports.getReportPath(options); + const matchedRequest = await findWireMockRequest(requestId); + const parsedUrl = new URL(matchedRequest.absoluteUrl); + expect(parsedUrl.pathname).toEqual(`/2.0/reports/${TEST_REPORT_ID}/path`); + expect(matchedRequest.method).toEqual('GET'); + const queryParamsObject = Object.fromEntries(parsedUrl.searchParams); + expect(queryParamsObject).toEqual({}); + }); + + it('getReportPath all response body properties', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/get-nested-report-path/all-response-body-properties', + }, + }; + const response = await client.reports.getReportPath(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(matchedRequest.body).toEqual(''); + expect(response).toEqual({ + id: TEST_PATH_WORKSPACE_ID, + name: TEST_PATH_WORKSPACE_NAME, + permalink: TEST_PATH_WORKSPACE_PERMALINK, + accessLevel: APIAccessLevel.owner, + folders: [ + { + id: 1234567890123456, + name: 'Project Plans', + permalink: 'https://app.smartsheet.com/folders/1234567890123456', + folders: [ + { + id: 2345678901234567, + name: 'Project Plans Subfolder', + permalink: 'https://app.smartsheet.com/folders/2345678901234567', + reports: [ + { + id: 3456789012345678, + name: 'Project Report', + permalink: 'https://app.smartsheet.com/reports/3456789012345678', + accessLevel: APIAccessLevel.admin, + createdAt: TEST_REPORT_CREATED_AT, + modifiedAt: TEST_REPORT_MODIFIED_AT, + }, + ], + }, + ], + }, + ], + }); + }); + + it('getReportPath root level response body properties', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/get-root-report-path/all-response-body-properties', + }, + }; + const response = await client.reports.getReportPath(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(matchedRequest.body).toEqual(''); + expect(response).toEqual({ + id: TEST_PATH_WORKSPACE_ID, + name: TEST_PATH_WORKSPACE_NAME, + permalink: TEST_PATH_WORKSPACE_PERMALINK, + accessLevel: APIAccessLevel.owner, + reports: [ + { + id: 5678901234567890, + name: 'Root Level Report', + permalink: 'https://app.smartsheet.com/reports/rootlevel', + accessLevel: APIAccessLevel.admin, + createdAt: TEST_REPORT_CREATED_AT, + modifiedAt: TEST_REPORT_MODIFIED_AT, + }, + ], + }); + }); + + it('getReportPath returns ReportPathNode compatible with getLeafReport and getLeafReportPath', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/get-nested-report-path/all-response-body-properties', + }, + }; + const response = await client.reports.getReportPath(options); + + expect(getLeafReport(response)).toEqual({ + id: 3456789012345678, + name: 'Project Report', + permalink: 'https://app.smartsheet.com/reports/3456789012345678', + accessLevel: APIAccessLevel.admin, + createdAt: TEST_REPORT_CREATED_AT, + modifiedAt: TEST_REPORT_MODIFIED_AT, + }); + expect(getLeafReportPath(response)).toEqual('/Sample Workspace/Project Plans/Project Report'); + }); + + it('getReportPath error 500 response', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/errors/500-response', + }, + }; + try { + await client.reports.getReportPath(options); + expect(true).toBe(false); + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_500_STATUS_CODE); + expect(error.message).toBe(ERROR_500_MESSAGE); + } + }); + + it('getReportPath error 400 response', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/errors/400-response', + }, + }; + try { + await client.reports.getReportPath(options); + expect(true).toBe(false); + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_400_STATUS_CODE); + expect(error.message).toBe(ERROR_400_MESSAGE); + } + }); +}); diff --git a/test/mock-api/sheets/common_test_constants.ts b/test/mock-api/sheets/common_test_constants.ts new file mode 100644 index 0000000..c7e9966 --- /dev/null +++ b/test/mock-api/sheets/common_test_constants.ts @@ -0,0 +1,16 @@ +// Common Workspace Properties (used for path tests) +export const TEST_PATH_WORKSPACE_ID = 4509918431602564; +export const TEST_PATH_WORKSPACE_NAME = 'Sample Workspace'; +export const TEST_PATH_WORKSPACE_PERMALINK = + 'https://app.smartsheet.com/workspaces/mock_workspace_id'; + +// Common Sheet Data +export const TEST_SHEET_ID = 9876543210987654; +export const TEST_SHEET_CREATED_AT = '2024-01-01T00:00:00Z'; +export const TEST_SHEET_MODIFIED_AT = '2024-06-01T00:00:00Z'; + +// Common Error Status Codes +export const ERROR_500_STATUS_CODE = 500; +export const ERROR_500_MESSAGE = 'Internal Server Error'; +export const ERROR_400_STATUS_CODE = 400; +export const ERROR_400_MESSAGE = 'Malformed Request'; diff --git a/test/mock-api/sheets/get_sheet_path.spec.ts b/test/mock-api/sheets/get_sheet_path.spec.ts new file mode 100644 index 0000000..507a22a --- /dev/null +++ b/test/mock-api/sheets/get_sheet_path.spec.ts @@ -0,0 +1,173 @@ +import crypto from 'crypto'; +import { createClient, findWireMockRequest } from '../utils/utils'; +import { expect } from '@jest/globals'; +import { APIAccessLevel } from '@smartsheet/types'; +import { getLeafSheet, getLeafSheetPath } from '@smartsheet/sheets/helpers'; +import { + TEST_SHEET_ID, + TEST_SHEET_CREATED_AT, + TEST_SHEET_MODIFIED_AT, + TEST_PATH_WORKSPACE_ID, + TEST_PATH_WORKSPACE_NAME, + TEST_PATH_WORKSPACE_PERMALINK, + ERROR_500_STATUS_CODE, + ERROR_500_MESSAGE, + ERROR_400_STATUS_CODE, + ERROR_400_MESSAGE, +} from './common_test_constants'; + +describe('Sheets - getSheetPath endpoint tests', () => { + const client = createClient(); + + it('getSheetPath generated url is correct', async () => { + const requestId = crypto.randomUUID(); + const options = { + sheetId: TEST_SHEET_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/sheets/get-nested-sheet-path/all-response-body-properties', + }, + }; + await client.sheets.getSheetPath(options); + const matchedRequest = await findWireMockRequest(requestId); + const parsedUrl = new URL(matchedRequest.absoluteUrl); + expect(parsedUrl.pathname).toEqual(`/2.0/sheets/${TEST_SHEET_ID}/path`); + expect(matchedRequest.method).toEqual('GET'); + const queryParamsObject = Object.fromEntries(parsedUrl.searchParams); + expect(queryParamsObject).toEqual({}); + }); + + it('getSheetPath all response body properties', async () => { + const requestId = crypto.randomUUID(); + const options = { + sheetId: TEST_SHEET_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/sheets/get-nested-sheet-path/all-response-body-properties', + }, + }; + const response = await client.sheets.getSheetPath(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(matchedRequest.body).toEqual(''); + expect(response).toEqual({ + id: TEST_PATH_WORKSPACE_ID, + name: TEST_PATH_WORKSPACE_NAME, + permalink: TEST_PATH_WORKSPACE_PERMALINK, + accessLevel: APIAccessLevel.owner, + folders: [ + { + id: 1234567890123456, + name: 'Project Plans', + permalink: 'https://app.smartsheet.com/folders/1234567890123456', + folders: [ + { + id: 2345678901234567, + name: 'Project Plans Subfolder', + permalink: 'https://app.smartsheet.com/folders/2345678901234567', + sheets: [ + { + id: 3456789012345678, + name: 'Project Plan', + permalink: 'https://app.smartsheet.com/sheets/3456789012345678', + accessLevel: APIAccessLevel.admin, + createdAt: TEST_SHEET_CREATED_AT, + modifiedAt: TEST_SHEET_MODIFIED_AT, + }, + ], + }, + ], + }, + ], + }); + }); + + it('getSheetPath root level response body properties', async () => { + const requestId = crypto.randomUUID(); + const options = { + sheetId: TEST_SHEET_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/sheets/get-root-sheet-path/all-response-body-properties', + }, + }; + const response = await client.sheets.getSheetPath(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(matchedRequest.body).toEqual(''); + expect(response).toEqual({ + id: TEST_PATH_WORKSPACE_ID, + name: TEST_PATH_WORKSPACE_NAME, + permalink: TEST_PATH_WORKSPACE_PERMALINK, + accessLevel: APIAccessLevel.owner, + sheets: [ + { + id: 5678901234567890, + name: 'Root Level Sheet', + permalink: 'https://app.smartsheet.com/sheets/rootlevel', + accessLevel: APIAccessLevel.admin, + createdAt: TEST_SHEET_CREATED_AT, + modifiedAt: TEST_SHEET_MODIFIED_AT, + }, + ], + }); + }); + + it('getSheetPath returns SheetPathNode compatible with getLeafSheet and getLeafSheetPath', async () => { + const requestId = crypto.randomUUID(); + const options = { + sheetId: TEST_SHEET_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/sheets/get-nested-sheet-path/all-response-body-properties', + }, + }; + const response = await client.sheets.getSheetPath(options); + + expect(getLeafSheet(response)).toEqual({ + id: 3456789012345678, + name: 'Project Plan', + permalink: 'https://app.smartsheet.com/sheets/3456789012345678', + accessLevel: APIAccessLevel.admin, + createdAt: TEST_SHEET_CREATED_AT, + modifiedAt: TEST_SHEET_MODIFIED_AT, + }); + expect(getLeafSheetPath(response)).toEqual('/Sample Workspace/Project Plans/Project Plan'); + }); + + it('getSheetPath error 500 response', async () => { + const requestId = crypto.randomUUID(); + const options = { + sheetId: TEST_SHEET_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/errors/500-response', + }, + }; + try { + await client.sheets.getSheetPath(options); + expect(true).toBe(false); + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_500_STATUS_CODE); + expect(error.message).toBe(ERROR_500_MESSAGE); + } + }); + + it('getSheetPath error 400 response', async () => { + const requestId = crypto.randomUUID(); + const options = { + sheetId: TEST_SHEET_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/errors/400-response', + }, + }; + try { + await client.sheets.getSheetPath(options); + expect(true).toBe(false); + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_400_STATUS_CODE); + expect(error.message).toBe(ERROR_400_MESSAGE); + } + }); +}); diff --git a/test/mock-api/sights/common_test_constants.ts b/test/mock-api/sights/common_test_constants.ts new file mode 100644 index 0000000..9369e77 --- /dev/null +++ b/test/mock-api/sights/common_test_constants.ts @@ -0,0 +1,16 @@ +// Common Workspace Properties (used for path tests) +export const TEST_PATH_WORKSPACE_ID = 4509918431602564; +export const TEST_PATH_WORKSPACE_NAME = 'Sample Workspace'; +export const TEST_PATH_WORKSPACE_PERMALINK = + 'https://app.smartsheet.com/workspaces/mock_workspace_id'; + +// Common Sight IDs +export const TEST_SIGHT_ID = 9876543210123456; +export const TEST_SIGHT_CREATED_AT = '2024-01-01T00:00:00Z'; +export const TEST_SIGHT_MODIFIED_AT = '2024-06-01T00:00:00Z'; + +// Common Error Status Codes +export const ERROR_500_STATUS_CODE = 500; +export const ERROR_500_MESSAGE = 'Internal Server Error'; +export const ERROR_400_STATUS_CODE = 400; +export const ERROR_400_MESSAGE = 'Malformed Request'; diff --git a/test/mock-api/sights/get_sight_path.spec.ts b/test/mock-api/sights/get_sight_path.spec.ts new file mode 100644 index 0000000..93abf86 --- /dev/null +++ b/test/mock-api/sights/get_sight_path.spec.ts @@ -0,0 +1,173 @@ +import crypto from 'crypto'; +import { createClient, findWireMockRequest } from '../utils/utils'; +import { expect } from '@jest/globals'; +import { APIAccessLevel } from '@smartsheet/types'; +import { getLeafSight, getLeafSightPath } from '@smartsheet/sights/helpers'; +import { + TEST_SIGHT_ID, + TEST_SIGHT_CREATED_AT, + TEST_SIGHT_MODIFIED_AT, + TEST_PATH_WORKSPACE_ID, + TEST_PATH_WORKSPACE_NAME, + TEST_PATH_WORKSPACE_PERMALINK, + ERROR_500_STATUS_CODE, + ERROR_500_MESSAGE, + ERROR_400_STATUS_CODE, + ERROR_400_MESSAGE, +} from './common_test_constants'; + +describe('Sights - getSightPath endpoint tests', () => { + const client = createClient(); + + it('getSightPath generated url is correct', async () => { + const requestId = crypto.randomUUID(); + const options = { + sightId: TEST_SIGHT_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/sights/get-nested-sight-path/all-response-body-properties', + }, + }; + await client.sights.getSightPath(options); + const matchedRequest = await findWireMockRequest(requestId); + const parsedUrl = new URL(matchedRequest.absoluteUrl); + expect(parsedUrl.pathname).toEqual(`/2.0/sights/${TEST_SIGHT_ID}/path`); + expect(matchedRequest.method).toEqual('GET'); + const queryParamsObject = Object.fromEntries(parsedUrl.searchParams); + expect(queryParamsObject).toEqual({}); + }); + + it('getSightPath all response body properties', async () => { + const requestId = crypto.randomUUID(); + const options = { + sightId: TEST_SIGHT_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/sights/get-nested-sight-path/all-response-body-properties', + }, + }; + const response = await client.sights.getSightPath(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(matchedRequest.body).toEqual(''); + expect(response).toEqual({ + id: TEST_PATH_WORKSPACE_ID, + name: TEST_PATH_WORKSPACE_NAME, + permalink: TEST_PATH_WORKSPACE_PERMALINK, + accessLevel: APIAccessLevel.owner, + folders: [ + { + id: 1234567890123456, + name: 'Project Plans', + permalink: 'https://app.smartsheet.com/folders/1234567890123456', + folders: [ + { + id: 2345678901234567, + name: 'Project Plans Subfolder', + permalink: 'https://app.smartsheet.com/folders/2345678901234567', + sights: [ + { + id: 3456789012345678, + name: 'Project Dashboard', + permalink: 'https://app.smartsheet.com/dashboards/3456789012345678', + accessLevel: APIAccessLevel.admin, + createdAt: TEST_SIGHT_CREATED_AT, + modifiedAt: TEST_SIGHT_MODIFIED_AT, + }, + ], + }, + ], + }, + ], + }); + }); + + it('getSightPath root level response body properties', async () => { + const requestId = crypto.randomUUID(); + const options = { + sightId: TEST_SIGHT_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/sights/get-root-sight-path/all-response-body-properties', + }, + }; + const response = await client.sights.getSightPath(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(matchedRequest.body).toEqual(''); + expect(response).toEqual({ + id: TEST_PATH_WORKSPACE_ID, + name: TEST_PATH_WORKSPACE_NAME, + permalink: TEST_PATH_WORKSPACE_PERMALINK, + accessLevel: APIAccessLevel.owner, + sights: [ + { + id: 5678901234567890, + name: 'Root Level Dashboard', + permalink: 'https://app.smartsheet.com/dashboards/rootlevel', + accessLevel: APIAccessLevel.admin, + createdAt: TEST_SIGHT_CREATED_AT, + modifiedAt: TEST_SIGHT_MODIFIED_AT, + }, + ], + }); + }); + + it('getSightPath returns SightPathNode compatible with getLeafSight and getLeafSightPath', async () => { + const requestId = crypto.randomUUID(); + const options = { + sightId: TEST_SIGHT_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/sights/get-nested-sight-path/all-response-body-properties', + }, + }; + const response = await client.sights.getSightPath(options); + + expect(getLeafSight(response)).toEqual({ + id: 3456789012345678, + name: 'Project Dashboard', + permalink: 'https://app.smartsheet.com/dashboards/3456789012345678', + accessLevel: APIAccessLevel.admin, + createdAt: TEST_SIGHT_CREATED_AT, + modifiedAt: TEST_SIGHT_MODIFIED_AT, + }); + expect(getLeafSightPath(response)).toEqual('/Sample Workspace/Project Plans/Project Dashboard'); + }); + + it('getSightPath error 500 response', async () => { + const requestId = crypto.randomUUID(); + const options = { + sightId: TEST_SIGHT_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/errors/500-response', + }, + }; + try { + await client.sights.getSightPath(options); + expect(true).toBe(false); + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_500_STATUS_CODE); + expect(error.message).toBe(ERROR_500_MESSAGE); + } + }); + + it('getSightPath error 400 response', async () => { + const requestId = crypto.randomUUID(); + const options = { + sightId: TEST_SIGHT_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/errors/400-response', + }, + }; + try { + await client.sights.getSightPath(options); + expect(true).toBe(false); + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_400_STATUS_CODE); + expect(error.message).toBe(ERROR_400_MESSAGE); + } + }); +});