diff --git a/CHANGELOG.md b/CHANGELOG.md index 6132767..00ad334 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/reports/{reportId}/scope endpoint (`listReportScope`) +- Added support for GET /2.0/reports/{reportId}/columns endpoint (`listReportColumns`) +- Added support for GET /2.0/reports/{reportId}/columns/{columnVirtualId} endpoint (`getReportColumn`) +- Added support for PUT /2.0/reports/{reportId}/columns/{columnVirtualId} endpoint (`updateReportColumn`) +- Added support for DELETE /2.0/reports/{reportId}/columns/{columnVirtualId} endpoint (`deleteReportColumn`) +- Added support for GET /2.0/reports/{reportId}/definition endpoint (`getReportDefinition`) + ### Deprecated - Deprecated `Event.objectId`; use `Event.objectIdStr` instead. `objectId` is numeric only and contains -1 for non-numeric identifiers. It is not scheduled for removal. diff --git a/lib/reports/index.ts b/lib/reports/index.ts index 6f743ab..093a89b 100644 --- a/lib/reports/index.ts +++ b/lib/reports/index.ts @@ -26,6 +26,17 @@ import type { CreateReportOptions, CreateReportResponse, GetReportPathOptions, + ListReportScopeOptions, + ListReportScopeResponse, + ListReportColumnsOptions, + ListReportColumnsResponse, + GetReportColumnOptions, + ReportColumn, + UpdateReportColumnOptions, + UpdateReportColumnResponse, + DeleteReportColumnOptions, + GetReportDefinitionOptions, + ReportDefinition, } from './types'; import type { ReportPathNode } from './types'; import * as constants from '../utils/constants'; @@ -150,6 +161,60 @@ export function create(options: CreateOptions): ReportsApi { return requestor.get({ ...optionsToSend, ...urlOptions, ...getOptions }, callback); }; + const listReportScope = ( + getOptions: ListReportScopeOptions, + callback?: RequestCallback + ): Promise => { + const urlOptions = { url: options.apiUrls.reports + '/' + getOptions.reportId + '/scope' }; + return requestor.get({ ...optionsToSend, ...urlOptions, ...getOptions }, callback); + }; + + const listReportColumns = ( + getOptions: ListReportColumnsOptions, + callback?: RequestCallback + ): Promise => { + const urlOptions = { url: options.apiUrls.reports + '/' + getOptions.reportId + '/columns' }; + return requestor.get({ ...optionsToSend, ...urlOptions, ...getOptions }, callback); + }; + + const getReportColumn = ( + getOptions: GetReportColumnOptions, + callback?: RequestCallback + ): Promise => { + const urlOptions = { + url: options.apiUrls.reports + '/' + getOptions.reportId + '/columns/' + getOptions.columnVirtualId, + }; + return requestor.get({ ...optionsToSend, ...urlOptions, ...getOptions }, callback); + }; + + const updateReportColumn = ( + putOptions: UpdateReportColumnOptions, + callback?: RequestCallback + ): Promise => { + const urlOptions = { + url: options.apiUrls.reports + '/' + putOptions.reportId + '/columns/' + putOptions.columnVirtualId, + }; + return requestor.put({ ...optionsToSend, ...urlOptions, ...putOptions }, callback); + }; + + const deleteReportColumn = ( + deleteOptions: DeleteReportColumnOptions, + callback?: RequestCallback + ): Promise => { + const urlOptions = { + url: options.apiUrls.reports + '/' + deleteOptions.reportId + '/columns/' + deleteOptions.columnVirtualId, + }; + return requestor.delete({ ...optionsToSend, ...urlOptions, ...deleteOptions }, callback); + }; + + const getReportDefinition = ( + getOptions: GetReportDefinitionOptions, + callback?: RequestCallback + ): Promise => { + const urlOptions = { url: options.apiUrls.reports + '/' + getOptions.reportId + '/definition' }; + return requestor.get({ ...optionsToSend, ...urlOptions, ...getOptions }, callback); + }; + return { listReports, getReport, @@ -165,5 +230,11 @@ export function create(options: CreateOptions): ReportsApi { addReportColumns, createReport, getReportPath, + listReportScope, + listReportColumns, + getReportColumn, + updateReportColumn, + deleteReportColumn, + getReportDefinition, }; } diff --git a/lib/reports/types.ts b/lib/reports/types.ts index 69eb1ae..2d393b5 100644 --- a/lib/reports/types.ts +++ b/lib/reports/types.ts @@ -3,6 +3,8 @@ import type { PathNode } from '../types/PathNode'; import type { RequestCallback } from '../types/RequestCallback'; import type { RequestOptions } from '../types/RequestOptions'; import type { BaseResponseStatus } from '../types/BaseResponseStatus'; +import type { TokenPaginationQueryParameters } from '../types/TokenPaginationQueryParameters'; +import type { TokenPaginationResponse } from '../types/TokenPaginationResponse'; import type { FailedItem } from '../types/FailedItem'; import type { Attachment } from '../attachments/types'; import type { Column } from '../columns/types'; @@ -451,6 +453,127 @@ export interface ReportsApi { * ``` */ getReportPath: (options: GetReportPathOptions, callback?: RequestCallback) => Promise; + + /** + * Gets the scope of a Report. + * + * @param options - {@link ListReportScopeOptions} - Configuration options for the request + * @param callback - {@link RequestCallback}\<{@link ListReportScopeResponse}\> - Optional callback function + * @returns Promise\<{@link ListReportScopeResponse}\> + * + * @remarks + * It mirrors to the following Smartsheet REST API method: `GET /reports/{reportId}/scope` + * + * @example + * ```typescript + * const scope = await client.reports.listReportScope({ reportId: 4583173393803140 }); + * ``` + */ + listReportScope: ( + options: ListReportScopeOptions, + callback?: RequestCallback + ) => Promise; + + /** + * Gets the columns of a Report. + * + * @param options - {@link ListReportColumnsOptions} - Configuration options for the request + * @param callback - {@link RequestCallback}\<{@link ListReportColumnsResponse}\> - Optional callback function + * @returns Promise\<{@link ListReportColumnsResponse}\> + * + * @remarks + * It mirrors to the following Smartsheet REST API method: `GET /reports/{reportId}/columns` + * + * @example + * ```typescript + * const columns = await client.reports.listReportColumns({ reportId: 4583173393803140 }); + * ``` + */ + listReportColumns: ( + options: ListReportColumnsOptions, + callback?: RequestCallback + ) => Promise; + + /** + * Gets a single column of a Report. + * + * @param options - {@link GetReportColumnOptions} - Configuration options for the request + * @param callback - {@link RequestCallback}\<{@link ReportColumn}\> - Optional callback function + * @returns Promise\<{@link ReportColumn}\> + * + * @remarks + * It mirrors to the following Smartsheet REST API method: `GET /reports/{reportId}/columns/{columnVirtualId}` + * + * @example + * ```typescript + * const column = await client.reports.getReportColumn({ reportId: 4583173393803140, columnVirtualId: 123 }); + * ``` + */ + getReportColumn: (options: GetReportColumnOptions, callback?: RequestCallback) => Promise; + + /** + * Updates a column of a Report. + * + * @param options - {@link UpdateReportColumnOptions} - Configuration options for the request + * @param callback - {@link RequestCallback}\<{@link UpdateReportColumnResponse}\> - Optional callback function + * @returns Promise\<{@link UpdateReportColumnResponse}\> + * + * @remarks + * It mirrors to the following Smartsheet REST API method: `PUT /reports/{reportId}/columns/{columnVirtualId}` + * + * @example + * ```typescript + * const result = await client.reports.updateReportColumn({ + * reportId: 4583173393803140, + * columnVirtualId: 123, + * body: { title: 'New Title', hidden: false } + * }); + * ``` + */ + updateReportColumn: ( + options: UpdateReportColumnOptions, + callback?: RequestCallback + ) => Promise; + + /** + * Deletes a column from a Report. + * + * @param options - {@link DeleteReportColumnOptions} - Configuration options for the request + * @param callback - {@link RequestCallback}\<{@link BaseResponseStatus}\> - Optional callback function + * @returns Promise\<{@link BaseResponseStatus}\> + * + * @remarks + * It mirrors to the following Smartsheet REST API method: `DELETE /reports/{reportId}/columns/{columnVirtualId}` + * + * @example + * ```typescript + * const result = await client.reports.deleteReportColumn({ reportId: 4583173393803140, columnVirtualId: 123 }); + * ``` + */ + deleteReportColumn: ( + options: DeleteReportColumnOptions, + callback?: RequestCallback + ) => Promise; + + /** + * Gets the definition of a Report. + * + * @param options - {@link GetReportDefinitionOptions} - Configuration options for the request + * @param callback - {@link RequestCallback}\<{@link ReportDefinition}\> - Optional callback function + * @returns Promise\<{@link ReportDefinition}\> + * + * @remarks + * It mirrors to the following Smartsheet REST API method: `GET /reports/{reportId}/definition` + * + * @example + * ```typescript + * const definition = await client.reports.getReportDefinition({ reportId: 4583173393803140 }); + * ``` + */ + getReportDefinition: ( + options: GetReportDefinitionOptions, + callback?: RequestCallback + ) => Promise; } // ============================================================================ @@ -1252,7 +1375,7 @@ export interface ReportFilterObjectValue { * For DATE objects, this would be a date string. * For CURRENT_USER, this represents the user identifier. */ - value: string; + value?: string; } /** @@ -1690,3 +1813,125 @@ export interface GetReportPathOptions extends RequestOptions { + /** + * Report ID. + */ + reportId: number; +} + +export type ListReportScopeResponse = TokenPaginationResponse; + +// ============================================================================ +// Get Report Columns +// ============================================================================ + +export interface ListReportColumnsQueryParameters extends TokenPaginationQueryParameters { + /** + * Compatibility level. + */ + level?: number; +} + +export interface ListReportColumnsOptions extends RequestOptions { + /** + * Report ID. + */ + reportId: number; +} + +export type ListReportColumnsResponse = TokenPaginationResponse; + +// ============================================================================ +// Get Report Column +// ============================================================================ + +export interface GetReportColumnQueryParameters { + /** + * Compatibility level. + */ + level?: number; +} + +export interface GetReportColumnOptions extends RequestOptions { + /** + * Report ID. + */ + reportId: number; + /** + * The virtual ID of the report column. + */ + columnVirtualId: number; +} + +// ============================================================================ +// Update Report Column +// ============================================================================ + +export interface UpdateReportColumnRequest { + /** + * Column title. + */ + title?: string; + /** + * Column index or position. + */ + index?: number; + /** + * Indicates whether the column is hidden. + */ + hidden?: boolean; + /** + * Display width of the column in pixels. + */ + width?: number; +} + +export interface UpdateReportColumnOptions extends RequestOptions { + /** + * Report ID. + */ + reportId: number; + /** + * The virtual ID of the report column. + */ + columnVirtualId: number; +} + +export interface UpdateReportColumnResponse extends BaseResponseStatus { + /** + * The updated report column. + */ + result: ReportColumn; +} + +// ============================================================================ +// Delete Report Column +// ============================================================================ + +export interface DeleteReportColumnOptions extends RequestOptions { + /** + * Report ID. + */ + reportId: number; + /** + * The virtual ID of the report column. + */ + columnVirtualId: number; +} + +// ============================================================================ +// Get Report Definition +// ============================================================================ + +export interface GetReportDefinitionOptions extends RequestOptions { + /** + * Report ID. + */ + reportId: number; +} diff --git a/test/functional/client.spec.ts b/test/functional/client.spec.ts index 5210b99..51e179a 100644 --- a/test/functional/client.spec.ts +++ b/test/functional/client.spec.ts @@ -179,7 +179,7 @@ describe('Client Unit Tests', () => { describe('#reports', () => { it('should have reports object', () => { expect(smartsheet).toHaveProperty('reports'); - expect(Object.keys(smartsheet.reports)).toHaveLength(14); + expect(Object.keys(smartsheet.reports)).toHaveLength(20); }); it('should have get methods', () => { @@ -189,6 +189,10 @@ describe('Client Unit Tests', () => { expect(smartsheet.reports).toHaveProperty('getReportAsCSV'); expect(smartsheet.reports).toHaveProperty('getReportPublishStatus'); expect(smartsheet.reports).toHaveProperty('getReportPath'); + expect(smartsheet.reports).toHaveProperty('listReportScope'); + expect(smartsheet.reports).toHaveProperty('listReportColumns'); + expect(smartsheet.reports).toHaveProperty('getReportColumn'); + expect(smartsheet.reports).toHaveProperty('getReportDefinition'); }); it('should have create methods', () => { @@ -200,11 +204,13 @@ describe('Client Unit Tests', () => { it('should have update methods', () => { expect(smartsheet.reports).toHaveProperty('setReportPublishStatus'); expect(smartsheet.reports).toHaveProperty('sendReportViaEmail'); + expect(smartsheet.reports).toHaveProperty('updateReportColumn'); }); it('should have delete methods', () => { expect(smartsheet.reports).toHaveProperty('deleteReport'); expect(smartsheet.reports).toHaveProperty('removeReportScope'); + expect(smartsheet.reports).toHaveProperty('deleteReportColumn'); }); }); diff --git a/test/mock-api/reports/delete_report_column.spec.ts b/test/mock-api/reports/delete_report_column.spec.ts new file mode 100644 index 0000000..3341fd1 --- /dev/null +++ b/test/mock-api/reports/delete_report_column.spec.ts @@ -0,0 +1,98 @@ +import crypto from 'crypto'; +import { createClient, findWireMockRequest } from '../utils/utils'; +import { expect } from '@jest/globals'; +import { + TEST_REPORT_ID, + TEST_SUCCESS_MESSAGE, + TEST_SUCCESS_RESULT_CODE, + ERROR_500_STATUS_CODE, + ERROR_500_MESSAGE, + ERROR_400_STATUS_CODE, + ERROR_400_MESSAGE, +} from './common_test_constants'; + +describe('Reports - deleteReportColumn endpoint tests', () => { + const client = createClient(); + + const TEST_COLUMN_VIRTUAL_ID = 7001; + + const expectedAllResponseProperties = { + message: TEST_SUCCESS_MESSAGE, + resultCode: TEST_SUCCESS_RESULT_CODE, + }; + + it('deleteReportColumn generated url is correct', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + columnVirtualId: TEST_COLUMN_VIRTUAL_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/delete-report-column/all-response-body-properties', + }, + }; + await client.reports.deleteReportColumn(options); + const matchedRequest = await findWireMockRequest(requestId); + + const parsedUrl = new URL(matchedRequest.absoluteUrl); + expect(parsedUrl.pathname).toEqual(`/2.0/reports/${TEST_REPORT_ID}/columns/${TEST_COLUMN_VIRTUAL_ID}`); + expect(matchedRequest.method).toEqual('DELETE'); + const queryParamsObject = Object.fromEntries(parsedUrl.searchParams); + expect(queryParamsObject).toEqual({}); + }); + + it('deleteReportColumn all response body properties', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + columnVirtualId: TEST_COLUMN_VIRTUAL_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/delete-report-column/all-response-body-properties', + }, + }; + const response = await client.reports.deleteReportColumn(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(response).toEqual(expectedAllResponseProperties); + expect(matchedRequest.body).toEqual(''); + }); + + it('deleteReportColumn error 400 response', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + columnVirtualId: TEST_COLUMN_VIRTUAL_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/errors/400-response', + }, + }; + try { + await client.reports.deleteReportColumn(options); + expect(true).toBe(false); // Expected an error to be thrown + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_400_STATUS_CODE); + expect(error.message).toBe(ERROR_400_MESSAGE); + } + }); + + it('deleteReportColumn error 500 response', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + columnVirtualId: TEST_COLUMN_VIRTUAL_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/errors/500-response', + }, + }; + try { + await client.reports.deleteReportColumn(options); + expect(true).toBe(false); // Expected an error to be thrown + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_500_STATUS_CODE); + expect(error.message).toBe(ERROR_500_MESSAGE); + } + }); +}); diff --git a/test/mock-api/reports/get_report_column.spec.ts b/test/mock-api/reports/get_report_column.spec.ts new file mode 100644 index 0000000..dbddeb7 --- /dev/null +++ b/test/mock-api/reports/get_report_column.spec.ts @@ -0,0 +1,159 @@ +import crypto from 'crypto'; +import { createClient, findWireMockRequest } from '../utils/utils'; +import { expect } from '@jest/globals'; +import { + TEST_REPORT_ID, + ERROR_500_STATUS_CODE, + ERROR_500_MESSAGE, + ERROR_400_STATUS_CODE, + ERROR_400_MESSAGE, +} from './common_test_constants'; +import { ReportColumnType } from '@smartsheet/reports/types'; + +describe('Reports - getReportColumn endpoint tests', () => { + const client = createClient(); + + const TEST_COLUMN_VIRTUAL_ID = 7001; + + const expectedAllResponseProperties = { + virtualId: 7001, + index: 0, + title: 'Task Name', + type: ReportColumnType.TEXT_NUMBER, + primary: true, + width: 150, + hidden: false, + validation: true, + version: 0, + autoNumberFormat: { + fill: '0001', + prefix: 'TASK-', + startingNumber: 1, + suffix: '', + }, + }; + + const expectedRequiredResponseProperties = { + index: 0, + title: 'Task Name', + type: ReportColumnType.TEXT_NUMBER, + primary: true, + }; + + it('getReportColumn generated url is correct', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + columnVirtualId: TEST_COLUMN_VIRTUAL_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/get-report-column/all-response-body-properties', + }, + }; + await client.reports.getReportColumn(options); + const matchedRequest = await findWireMockRequest(requestId); + + const parsedUrl = new URL(matchedRequest.absoluteUrl); + expect(parsedUrl.pathname).toEqual(`/2.0/reports/${TEST_REPORT_ID}/columns/${TEST_COLUMN_VIRTUAL_ID}`); + expect(matchedRequest.method).toEqual('GET'); + const queryParamsObject = Object.fromEntries(parsedUrl.searchParams); + expect(queryParamsObject).toEqual({}); + }); + + it('getReportColumn with level generated url is correct', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + columnVirtualId: TEST_COLUMN_VIRTUAL_ID, + queryParameters: { + level: 3 + }, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/get-report-column/all-response-body-properties', + }, + }; + await client.reports.getReportColumn(options); + const matchedRequest = await findWireMockRequest(requestId); + + const parsedUrl = new URL(matchedRequest.absoluteUrl); + expect(parsedUrl.pathname).toEqual(`/2.0/reports/${TEST_REPORT_ID}/columns/${TEST_COLUMN_VIRTUAL_ID}`); + expect(matchedRequest.method).toEqual('GET'); + const queryParamsObject = Object.fromEntries(parsedUrl.searchParams); + expect(queryParamsObject).toEqual({ + level: '3' + }); + }); + + it('getReportColumn all response body properties', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + columnVirtualId: TEST_COLUMN_VIRTUAL_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/get-report-column/all-response-body-properties', + }, + }; + const response = await client.reports.getReportColumn(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(response).toEqual(expectedAllResponseProperties); + expect(matchedRequest.body).toEqual(''); + }); + + it('getReportColumn required response body properties', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + columnVirtualId: TEST_COLUMN_VIRTUAL_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/get-report-column/required-response-body-properties', + }, + }; + const response = await client.reports.getReportColumn(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(response).toEqual(expectedRequiredResponseProperties); + expect(matchedRequest.body).toEqual(''); + }); + + it('getReportColumn error 400 response', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + columnVirtualId: TEST_COLUMN_VIRTUAL_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/errors/400-response', + }, + }; + try { + await client.reports.getReportColumn(options); + expect(true).toBe(false); // Expected an error to be thrown + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_400_STATUS_CODE); + expect(error.message).toBe(ERROR_400_MESSAGE); + } + }); + + it('getReportColumn error 500 response', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + columnVirtualId: TEST_COLUMN_VIRTUAL_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/errors/500-response', + }, + }; + try { + await client.reports.getReportColumn(options); + expect(true).toBe(false); // Expected an error to be thrown + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_500_STATUS_CODE); + expect(error.message).toBe(ERROR_500_MESSAGE); + } + }); +}); diff --git a/test/mock-api/reports/get_report_definition.spec.ts b/test/mock-api/reports/get_report_definition.spec.ts new file mode 100644 index 0000000..f6525b5 --- /dev/null +++ b/test/mock-api/reports/get_report_definition.spec.ts @@ -0,0 +1,219 @@ +import crypto from 'crypto'; +import { createClient, findWireMockRequest } from '../utils/utils'; +import { expect } from '@jest/globals'; +import { + TEST_REPORT_ID, + ERROR_500_STATUS_CODE, + ERROR_500_MESSAGE, + ERROR_400_STATUS_CODE, + ERROR_400_MESSAGE, +} from './common_test_constants'; +import { + ReportFilterOperator, + ReportFilterConditionOperator, + ReportColumnType, + ReportSortingDirection, + ReportAggregationType, + SystemColumnType, +} from '@smartsheet/reports/types'; + +describe('Reports - getReportDefinition endpoint tests', () => { + const client = createClient(); + + const expectedAllResponseProperties = { + filters: { + operator: ReportFilterOperator.AND, + criteria: [ + { + column: { + title: 'Primary Column', + type: ReportColumnType.TEXT_NUMBER, + primary: true, + }, + operator: ReportFilterConditionOperator.EQUAL, + values: ['Test Value'], + }, + { + column: { + title: 'Status', + type: ReportColumnType.PICKLIST, + }, + operator: ReportFilterConditionOperator.NOT_EQUAL, + values: ['Complete'], + }, + { + column: { + title: 'Amount', + type: ReportColumnType.TEXT_NUMBER, + }, + operator: ReportFilterConditionOperator.GREATER_THAN, + values: [42], + }, + { + column: { + type: ReportColumnType.DATETIME, + systemColumnType: SystemColumnType.MODIFIED_DATE, + }, + operator: ReportFilterConditionOperator.LESS_THAN, + values: [{ objectType: 'DATE', value: '2025-01-14' }], + }, + { + column: { + title: 'Assigned To', + type: ReportColumnType.CONTACT_LIST, + }, + operator: ReportFilterConditionOperator.EQUAL, + values: [{ objectType: 'CURRENT_USER' }], + }, + { + column: { + title: 'Notes', + type: ReportColumnType.TEXT_NUMBER, + }, + operator: ReportFilterConditionOperator.EQUAL, + values: [null], + }, + ], + }, + groupingCriteria: [ + { + column: { + title: 'Primary Column', + type: ReportColumnType.TEXT_NUMBER, + primary: true, + }, + sortingDirection: ReportSortingDirection.ASCENDING, + isExpanded: true, + }, + { + column: { + title: 'Category', + type: ReportColumnType.TEXT_NUMBER, + }, + sortingDirection: ReportSortingDirection.DESCENDING, + isExpanded: false, + }, + ], + summarizingCriteria: [ + { + column: { + title: 'Primary Column', + type: ReportColumnType.TEXT_NUMBER, + primary: true, + }, + aggregationType: ReportAggregationType.COUNT, + }, + { + column: { + title: 'Amount', + type: ReportColumnType.TEXT_NUMBER, + }, + aggregationType: ReportAggregationType.SUM, + }, + ], + sortingCriteria: [ + { + column: { + title: 'Primary Column', + type: ReportColumnType.TEXT_NUMBER, + primary: true, + }, + sortingDirection: ReportSortingDirection.ASCENDING, + }, + { + column: { + type: ReportColumnType.DATETIME, + systemColumnType: SystemColumnType.MODIFIED_DATE, + }, + sortingDirection: ReportSortingDirection.DESCENDING, + }, + ], + }; + + it('getReportDefinition 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-report-definition/all-response-body-properties', + }, + }; + await client.reports.getReportDefinition(options); + const matchedRequest = await findWireMockRequest(requestId); + + const parsedUrl = new URL(matchedRequest.absoluteUrl); + expect(parsedUrl.pathname).toEqual(`/2.0/reports/${TEST_REPORT_ID}/definition`); + expect(matchedRequest.method).toEqual('GET'); + const queryParamsObject = Object.fromEntries(parsedUrl.searchParams); + expect(queryParamsObject).toEqual({}); + }); + + it('getReportDefinition 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-report-definition/all-response-body-properties', + }, + }; + const response = await client.reports.getReportDefinition(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(response).toEqual(expectedAllResponseProperties); + expect(matchedRequest.body).toEqual(''); + }); + + it('getReportDefinition required response body properties', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/get-report-definition/required-response-body-properties', + }, + }; + const response = await client.reports.getReportDefinition(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(response).toEqual({}); + expect(matchedRequest.body).toEqual(''); + }); + + it('getReportDefinition 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.getReportDefinition(options); + expect(true).toBe(false); // Expected an error to be thrown + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_400_STATUS_CODE); + expect(error.message).toBe(ERROR_400_MESSAGE); + } + }); + + it('getReportDefinition 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.getReportDefinition(options); + expect(true).toBe(false); // Expected an error to be thrown + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_500_STATUS_CODE); + expect(error.message).toBe(ERROR_500_MESSAGE); + } + }); +}); diff --git a/test/mock-api/reports/list_report_columns.spec.ts b/test/mock-api/reports/list_report_columns.spec.ts new file mode 100644 index 0000000..2c1079f --- /dev/null +++ b/test/mock-api/reports/list_report_columns.spec.ts @@ -0,0 +1,211 @@ +import crypto from 'crypto'; +import { createClient, findWireMockRequest } from '../utils/utils'; +import { expect } from '@jest/globals'; +import { + TEST_REPORT_ID, + ERROR_500_STATUS_CODE, + ERROR_500_MESSAGE, + ERROR_400_STATUS_CODE, + ERROR_400_MESSAGE, +} from './common_test_constants'; +import { ReportColumnType, SystemColumnType } from '@smartsheet/reports/types'; + +describe('Reports - listReportColumns endpoint tests', () => { + const client = createClient(); + + const TEST_LAST_KEY = 'someLastKey'; + const TEST_MAX_ITEMS = 50; + + const expectedAllResponseProperties = { + data: [ + { + virtualId: 7001, + index: 0, + title: 'Task Name', + type: ReportColumnType.TEXT_NUMBER, + primary: true, + width: 150, + hidden: false, + validation: true, + version: 0, + autoNumberFormat: { + fill: '0001', + prefix: 'TASK-', + startingNumber: 1, + suffix: '', + }, + }, + { + virtualId: 7002, + index: 1, + title: 'Status', + type: ReportColumnType.PICKLIST, + width: 120, + hidden: false, + validation: false, + version: 0, + }, + { + virtualId: 7003, + index: 2, + title: 'Created By', + type: ReportColumnType.CONTACT_LIST, + systemColumnType: SystemColumnType.CREATED_BY, + width: 150, + hidden: false, + validation: false, + version: 1, + }, + { + virtualId: 7004, + index: 3, + title: 'Sheet Name', + type: ReportColumnType.TEXT_NUMBER, + sheetNameColumn: true, + width: 200, + hidden: false, + validation: false, + version: 0, + }, + ], + lastKey: null, + }; + + const expectedRequiredResponseProperties = { + data: [ + { + index: 0, + title: 'Task Name', + type: ReportColumnType.TEXT_NUMBER, + primary: true, + }, + { + index: 1, + type: ReportColumnType.DATETIME, + systemColumnType: SystemColumnType.CREATED_DATE, + }, + ], + }; + + it('listReportColumns generated url is correct', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + queryParameters: { + lastKey: TEST_LAST_KEY, + maxItems: TEST_MAX_ITEMS, + }, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/list-report-columns/all-response-body-properties', + }, + }; + await client.reports.listReportColumns(options); + const matchedRequest = await findWireMockRequest(requestId); + + const parsedUrl = new URL(matchedRequest.absoluteUrl); + expect(parsedUrl.pathname).toEqual(`/2.0/reports/${TEST_REPORT_ID}/columns`); + expect(matchedRequest.method).toEqual('GET'); + const queryParamsObject = Object.fromEntries(parsedUrl.searchParams); + expect(queryParamsObject).toEqual({ + lastKey: TEST_LAST_KEY, + maxItems: TEST_MAX_ITEMS.toString(), + }); + }); + + it('listReportColumns with level generated url is correct', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + queryParameters: { + lastKey: TEST_LAST_KEY, + maxItems: TEST_MAX_ITEMS, + level: 3 + }, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/list-report-columns/all-response-body-properties', + }, + }; + await client.reports.listReportColumns(options); + const matchedRequest = await findWireMockRequest(requestId); + + const parsedUrl = new URL(matchedRequest.absoluteUrl); + expect(parsedUrl.pathname).toEqual(`/2.0/reports/${TEST_REPORT_ID}/columns`); + expect(matchedRequest.method).toEqual('GET'); + const queryParamsObject = Object.fromEntries(parsedUrl.searchParams); + expect(queryParamsObject).toEqual({ + lastKey: TEST_LAST_KEY, + maxItems: TEST_MAX_ITEMS.toString(), + level: '3' + }); + }); + + it('listReportColumns all response body properties', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/list-report-columns/all-response-body-properties', + }, + }; + const response = await client.reports.listReportColumns(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(response).toEqual(expectedAllResponseProperties); + expect(matchedRequest.body).toEqual(''); + }); + + it('listReportColumns required response body properties', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/list-report-columns/required-response-body-properties', + }, + }; + const response = await client.reports.listReportColumns(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(response).toEqual(expectedRequiredResponseProperties); + expect(matchedRequest.body).toEqual(''); + }); + + it('listReportColumns 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.listReportColumns(options); + expect(true).toBe(false); // Expected an error to be thrown + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_400_STATUS_CODE); + expect(error.message).toBe(ERROR_400_MESSAGE); + } + }); + + it('listReportColumns 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.listReportColumns(options); + expect(true).toBe(false); // Expected an error to be thrown + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_500_STATUS_CODE); + expect(error.message).toBe(ERROR_500_MESSAGE); + } + }); +}); diff --git a/test/mock-api/reports/list_report_scope.spec.ts b/test/mock-api/reports/list_report_scope.spec.ts new file mode 100644 index 0000000..b0b6452 --- /dev/null +++ b/test/mock-api/reports/list_report_scope.spec.ts @@ -0,0 +1,127 @@ +import crypto from 'crypto'; +import { createClient, findWireMockRequest } from '../utils/utils'; +import { expect } from '@jest/globals'; +import { + TEST_REPORT_ID, + ERROR_500_STATUS_CODE, + ERROR_500_MESSAGE, + ERROR_400_STATUS_CODE, + ERROR_400_MESSAGE, +} from './common_test_constants'; +import { ReportAssetType } from '@smartsheet/reports/types'; + +describe('Reports - listReportScope endpoint tests', () => { + const client = createClient(); + + const TEST_LAST_KEY = 'someLastKey'; + const TEST_MAX_ITEMS = 50; + + const expectedAllResponseProperties = { + data: [ + { assetType: ReportAssetType.SHEET, assetId: 2331373580117892 }, + { assetType: ReportAssetType.WORKSPACE, assetId: Number('7879278542455688') }, + { assetType: ReportAssetType.SHEET, assetId: 1234567890123456 }, + ], + lastKey: null, + }; + + const expectedRequiredResponseProperties = { + data: [ + { assetType: ReportAssetType.SHEET, assetId: 2331373580117892 }, + ], + }; + + it('listReportScope generated url is correct', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + queryParameters: { + lastKey: TEST_LAST_KEY, + maxItems: TEST_MAX_ITEMS, + }, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/list-report-scope/all-response-body-properties', + }, + }; + await client.reports.listReportScope(options); + const matchedRequest = await findWireMockRequest(requestId); + + const parsedUrl = new URL(matchedRequest.absoluteUrl); + expect(parsedUrl.pathname).toEqual(`/2.0/reports/${TEST_REPORT_ID}/scope`); + expect(matchedRequest.method).toEqual('GET'); + const queryParamsObject = Object.fromEntries(parsedUrl.searchParams); + expect(queryParamsObject).toEqual({ + lastKey: TEST_LAST_KEY, + maxItems: TEST_MAX_ITEMS.toString(), + }); + }); + + it('listReportScope all response body properties', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/list-report-scope/all-response-body-properties', + }, + }; + const response = await client.reports.listReportScope(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(response).toEqual(expectedAllResponseProperties); + expect(matchedRequest.body).toEqual(''); + }); + + it('listReportScope required response body properties', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/list-report-scope/required-response-body-properties', + }, + }; + const response = await client.reports.listReportScope(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(response).toEqual(expectedRequiredResponseProperties); + expect(matchedRequest.body).toEqual(''); + }); + + it('listReportScope 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.listReportScope(options); + expect(true).toBe(false); // Expected an error to be thrown + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_400_STATUS_CODE); + expect(error.message).toBe(ERROR_400_MESSAGE); + } + }); + + it('listReportScope 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.listReportScope(options); + expect(true).toBe(false); // Expected an error to be thrown + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_500_STATUS_CODE); + expect(error.message).toBe(ERROR_500_MESSAGE); + } + }); +}); diff --git a/test/mock-api/reports/update_report_column.spec.ts b/test/mock-api/reports/update_report_column.spec.ts new file mode 100644 index 0000000..0964a82 --- /dev/null +++ b/test/mock-api/reports/update_report_column.spec.ts @@ -0,0 +1,160 @@ +import crypto from 'crypto'; +import { createClient, findWireMockRequest } from '../utils/utils'; +import { expect } from '@jest/globals'; +import { + TEST_REPORT_ID, + TEST_SUCCESS_MESSAGE, + TEST_SUCCESS_RESULT_CODE, + ERROR_500_STATUS_CODE, + ERROR_500_MESSAGE, + ERROR_400_STATUS_CODE, + ERROR_400_MESSAGE, +} from './common_test_constants'; +import { ReportColumnType } from '@smartsheet/reports/types'; + +describe('Reports - updateReportColumn endpoint tests', () => { + const client = createClient(); + + const TEST_COLUMN_VIRTUAL_ID = 7001; + + const testRequestBody = { + index: 2, + title: 'Updated Task Name', + width: 200, + hidden: false, + }; + + const expectedAllResponseProperties = { + message: TEST_SUCCESS_MESSAGE, + resultCode: TEST_SUCCESS_RESULT_CODE, + result: { + virtualId: 7001, + index: 2, + title: 'Updated Task Name', + type: ReportColumnType.TEXT_NUMBER, + primary: true, + width: 200, + hidden: false, + validation: true, + version: 0, + autoNumberFormat: { + fill: '0001', + prefix: 'TASK-', + startingNumber: 1, + suffix: '', + }, + }, + }; + + const expectedRequiredResponseProperties = { + message: TEST_SUCCESS_MESSAGE, + resultCode: TEST_SUCCESS_RESULT_CODE, + result: { + index: 1, + title: 'Updated Column', + type: ReportColumnType.TEXT_NUMBER, + primary: true, + }, + }; + + it('updateReportColumn generated url is correct', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + columnVirtualId: TEST_COLUMN_VIRTUAL_ID, + body: testRequestBody, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/update-report-column/all-response-body-properties', + }, + }; + await client.reports.updateReportColumn(options); + const matchedRequest = await findWireMockRequest(requestId); + + const parsedUrl = new URL(matchedRequest.absoluteUrl); + expect(parsedUrl.pathname).toEqual(`/2.0/reports/${TEST_REPORT_ID}/columns/${TEST_COLUMN_VIRTUAL_ID}`); + expect(matchedRequest.method).toEqual('PUT'); + const queryParamsObject = Object.fromEntries(parsedUrl.searchParams); + expect(queryParamsObject).toEqual({}); + }); + + it('updateReportColumn all response body properties', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + columnVirtualId: TEST_COLUMN_VIRTUAL_ID, + body: testRequestBody, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/update-report-column/all-response-body-properties', + }, + }; + const response = await client.reports.updateReportColumn(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(response).toEqual(expectedAllResponseProperties); + + const body = JSON.parse(matchedRequest.body); + expect(body).toEqual(testRequestBody); + }); + + it('updateReportColumn required response body properties', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + columnVirtualId: TEST_COLUMN_VIRTUAL_ID, + body: testRequestBody, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/reports/update-report-column/required-response-body-properties', + }, + }; + const response = await client.reports.updateReportColumn(options); + const matchedRequest = await findWireMockRequest(requestId); + + expect(response).toEqual(expectedRequiredResponseProperties); + + const body = JSON.parse(matchedRequest.body); + expect(body).toEqual(testRequestBody); + }); + + it('updateReportColumn error 400 response', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + columnVirtualId: TEST_COLUMN_VIRTUAL_ID, + body: testRequestBody, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/errors/400-response', + }, + }; + try { + await client.reports.updateReportColumn(options); + expect(true).toBe(false); // Expected an error to be thrown + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_400_STATUS_CODE); + expect(error.message).toBe(ERROR_400_MESSAGE); + } + }); + + it('updateReportColumn error 500 response', async () => { + const requestId = crypto.randomUUID(); + const options = { + reportId: TEST_REPORT_ID, + columnVirtualId: TEST_COLUMN_VIRTUAL_ID, + body: testRequestBody, + customProperties: { + 'x-request-id': requestId, + 'x-test-name': '/errors/500-response', + }, + }; + try { + await client.reports.updateReportColumn(options); + expect(true).toBe(false); // Expected an error to be thrown + } catch (error: any) { + expect(error.statusCode).toBe(ERROR_500_STATUS_CODE); + expect(error.message).toBe(ERROR_500_MESSAGE); + } + }); +});