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

## [x.x.x] - Unreleased

### Added

- Support for POST /2.0/reports/{reportId}/scope endpoint
- Support for DELETE /2.0/reports/{ReportId}/scope endpoint
- Added wiremock integration tests for POST /2.0/reports/{reportId}/scope endpoint
- Added wiremock integration tests for DELETE /2.0/reports/{reportId}/scope endpoint

## [3.7.2] - 2026-01-29

### Added
Expand Down
8 changes: 8 additions & 0 deletions docs-source/smartsheet_enums.rst
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,14 @@ PublishAccessibleBy
:undoc-members:
:show-inheritance:

ReportAssetType
------------------------------------------------------

.. automodule:: smartsheet.models.enums.report_asset_type
:members:
:undoc-members:
:show-inheritance:

ScheduleType
---------------------------------------------

Expand Down
8 changes: 8 additions & 0 deletions docs-source/smartsheet_models.rst
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,14 @@ ReportColumn
:undoc-members:
:show-inheritance:

ReportScopeInclusion
------------

.. automodule:: smartsheet.models.report_scope_inclusion
:members:
:undoc-members:
:show-inheritance:

ReportPublish
-------------

Expand Down
1 change: 1 addition & 0 deletions smartsheet/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
from .report_column import ReportColumn
from .report_publish import ReportPublish
from .report_row import ReportRow
from .report_scope_inclusion import ReportScopeInclusion
from .result import Result
from .row import Row
from .row_email import RowEmail
Expand Down
1 change: 1 addition & 0 deletions smartsheet/models/enums/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from .paper_type import PaperType
from .predecessor_type import PredecessorType
from .publish_accessible_by import PublishAccessibleBy
from .report_asset_type import ReportAssetType
from .schedule_type import ScheduleType
from .share_scope import ShareScope
from .share_type import ShareType
Expand Down
22 changes: 22 additions & 0 deletions smartsheet/models/enums/report_asset_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# pylint: disable=C0111,R0902,R0904,R0912,R0913,R0915,E1101
# Smartsheet Python SDK.
#
# Copyright 2018 Smartsheet.com, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"): you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from enum import Enum


class ReportAssetType(str, Enum):
SHEET = 'SHEET'
WORKSPACE = 'WORKSPACE'
64 changes: 64 additions & 0 deletions smartsheet/models/report_scope_inclusion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# pylint: disable=C0111,R0902,R0904,R0912,R0913,R0915,E1101
# Smartsheet Python SDK.
#
# Copyright 2018 Smartsheet.com, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"): you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from __future__ import absolute_import

from smartsheet.models.enums.report_asset_type import ReportAssetType

from ..types import EnumeratedValue, Number, json
from ..util import deserialize, serialize

class ReportScopeInclusion:

"""Smartsheet ReportScopeInclusion data model."""

def __init__(self, props=None, base_obj=None):
"""Initialize the ReportScopeInclusion model."""
self._base = None
if base_obj is not None:
self._base = base_obj

self._asset_type = EnumeratedValue(ReportAssetType)
self._asset_id = Number()

if props:
deserialize(self, props)

@property
def asset_id(self) -> int:
return self._asset_id.value

@asset_id.setter
def asset_id(self, value: int):
self._asset_id.value = value

@property
def asset_type(self) -> ReportAssetType:
return self._asset_type

@asset_type.setter
def asset_type(self, value: ReportAssetType):
self._asset_type.set(value)

def to_dict(self) -> dict:
return serialize(self)

def to_json(self) -> str:
return json.dumps(self.to_dict())

def __str__(self) -> str:
return self.to_json()
46 changes: 46 additions & 0 deletions smartsheet/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import os.path
from datetime import datetime

from smartsheet.models.report_scope_inclusion import ReportScopeInclusion

from .util import fresh_operation
from .models import Error, DownloadedFile, IndexResult, Report, ReportPublish, Result, Share

Expand Down Expand Up @@ -383,3 +385,47 @@ def set_publish_status(self, report_id, report_publish_obj) -> Union[Result[Repo
response = self._base.request(prepped_request, expected, _op)

return response

def add_report_scope(self, report_id: int, scopes: list[ReportScopeInclusion]) -> Union[Result[None], Error]:
"""Add one or more scopes to the report.

Args:
report_id (int): Report ID
scopes (list[ReportScopeInclusion]): List of scopes to add.

Returns:
Union[Result[None], Error]: The result of the operation, or an Error object if the request fails.
"""
_op = fresh_operation("add_report_scope")
_op["method"] = "POST"
_op["path"] = "/reports/" + str(report_id) + "/scope"
_op["json"] = scopes

expected = ["Result", None]

prepped_request = self._base.prepare_request(_op)
response = self._base.request(prepped_request, expected, _op)

return response

def remove_report_scope(self, report_id: int, scopes: list[ReportScopeInclusion]) -> Union[Result[None], Error]:
"""Remove one or more scopes from the report.

Args:
report_id (int): Report ID
scopes (list[ReportScopeInclusion]): List of scopes to remove.

Returns:
Union[Result[None], Error]: The result of the operation, or an Error object if the request fails.
"""
_op = fresh_operation("remove_report_scope")
_op["method"] = "DELETE"
_op["path"] = "/reports/" + str(report_id) + "/scope"
_op["json"] = scopes

expected = ["Result", None]

prepped_request = self._base.prepare_request(_op)
response = self._base.request(prepped_request, expected, _op)

return response
1 change: 0 additions & 1 deletion smartsheet/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ def deserialize(obj, props):
key_ = _camel_to_underscore(key)
if hasattr(obj, key_):
setattr(obj, key_, value)

else:
_log.debug(
"object '%s' is missing property '%s'", obj.__class__.__name__, key_
Expand Down
5 changes: 5 additions & 0 deletions tests/mock_api/reports/common_test_constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
TEST_SUCCESS_MESSAGE = "SUCCESS"
TEST_RESULT_CODE = 0
TEST_SHEET_ID = 9876543210
TEST_WORKSPACE_ID = 1122334455
TEST_REPORT_ID = 2233445566
97 changes: 97 additions & 0 deletions tests/mock_api/reports/test_add_report_scope.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import json
import uuid
from urllib.parse import urlparse

from smartsheet.models import Error
from smartsheet.models.enums.report_asset_type import ReportAssetType
from smartsheet.models.report_scope_inclusion import ReportScopeInclusion
from tests.mock_api.reports.common_test_constants import TEST_REPORT_ID, TEST_SHEET_ID, TEST_SUCCESS_MESSAGE, TEST_RESULT_CODE
from tests.mock_api.mock_api_test_helper import (
get_mock_api_client,
get_wiremock_request,
)


def test_add_report_scope_generated_url_is_correct():
request_id = uuid.uuid4().hex
client = get_mock_api_client(
"/reports/add-report-scope/all-response-body-properties", request_id
)

scopes = [ReportScopeInclusion({
"assetId": TEST_SHEET_ID, "assetType": ReportAssetType.SHEET
}
)]

client.Reports.add_report_scope(
report_id=TEST_REPORT_ID,
scopes=scopes
)

wiremock_request = get_wiremock_request(request_id)
url = urlparse(wiremock_request["absoluteUrl"])
assert url.path == f'/2.0/reports/{TEST_REPORT_ID}/scope'
assert wiremock_request["method"] == "POST"

def test_add_report_scope_all_response_properties():
request_id = uuid.uuid4().hex
client = get_mock_api_client(
"/reports/add-report-scope/all-response-body-properties", request_id
)

scopes = [ReportScopeInclusion({
"assetId": TEST_SHEET_ID, "assetType": ReportAssetType.SHEET
}
)]

response = client.Reports.add_report_scope(
report_id=TEST_REPORT_ID,
scopes=scopes
)

assert response.message == TEST_SUCCESS_MESSAGE
assert response.result_code == TEST_RESULT_CODE

wiremock_request = get_wiremock_request(request_id)
body = json.loads(wiremock_request["body"])
assert body == [{"assetId": TEST_SHEET_ID, "assetType": "SHEET"}]



def test_add_report_scope_error_4xx():
request_id = uuid.uuid4().hex
client = get_mock_api_client(
"/errors/400-response", request_id
)

scopes = [ReportScopeInclusion({
"assetId": TEST_SHEET_ID, "assetType": ReportAssetType.SHEET
}
)]

response = client.Reports.add_report_scope(
report_id=TEST_REPORT_ID,
scopes=scopes
)

assert isinstance(response, Error)


def test_add_report_scope_error_5xx():
request_id = uuid.uuid4().hex
client = get_mock_api_client(
"/errors/500-response", request_id
)

scopes = [ReportScopeInclusion({
"assetId": TEST_SHEET_ID, "assetType": ReportAssetType.SHEET
}
)]

response = client.Reports.add_report_scope(
report_id=TEST_REPORT_ID,
scopes=scopes
)

assert isinstance(response, Error)

Loading
Loading