Skip to content

Conversation

@jacalata
Copy link
Contributor

@jacalata jacalata commented Jan 22, 2026

[Copilot summary]
This pull request introduces several improvements and updates across the codebase, focusing on modernizing Python support, enhancing packaging and build configuration, updating CI workflows, and adding new sample scripts. It also brings in new model imports and improves project metadata management.

Build & Packaging Improvements:

  • Updated pyproject.toml to require setuptools>=77.0, changed license to license-files, expanded urllib3 version range, and added new test dependencies including pytest-xdist and types-requests. Also, added explicit package discovery and dynamic version configuration. [1] [2] [3]
  • Removed explicit package list from setup.py and ensured cmdclass from versioneer is used for versioning.
  • Updated .gitattributes and MANIFEST.in to better manage version files and package data. [1] [2] [3]
  • Removed obsolete publish.sh script.
  • Updated many test files to use pytest

CI/CD Workflow Updates:

  • Updated GitHub Actions to trigger PyPI publishing on release publication, use Python 3.13 for builds, and clarified workflow triggers. [1] [2]
  • Extended test matrix to include Python 3.14 and 3.14t, enabled pre-release Python versions, and enabled parallel pytest runs for faster feedback. [1] [2] [3]

New Sample Scripts:

  • Added samples/create_user.py for creating a user via TSC.
  • Added samples/metadata_paginated_query.py demonstrating paginated metadata queries.
  • Added samples/update_connection_auth.py for updating a single connection's authentication.
  • Added samples/update_connections_auth.py for bulk updating connection authentication.

Model & API Surface Enhancements:

  • Added new imports to tableauserverclient/__init__.py, including CollectionItem, ExtensionsServer, ExtensionsSiteSettings, SafeExtension, and SiteOIDCConfiguration, and updated __all__ accordingly. [1] [2] [3] [4] [5]

Other Improvements:

  • Fixed a typo in samples/update_datasource_data.py.
  • Added ECCN and workflow info to CODEOWNERS.

Let me know if you want to discuss any of these changes in more detail!


References:
Build & Packaging: [1] [2] [3] [4] [5] [6] [7] [8]
CI/CD: [1] [2] [3] [4] [5]
Samples: [1] [2] [3] [4]
API Surface: [1] [2] [3] [4] [5]
Other: [1] [2]

vitorhonna and others added 30 commits May 15, 2025 16:07
Fix typo in update_datasource_data.py
PersonalAccessTokenAuth repr was malformed. Fixing it to be
more representative and show the actual class name used
in cases where the client user decides to subclass.
Also correct the type hints to clarify that it accepts any
Iterable.
chore: refactor XML payload into RequestFactory
…#1638)

Update multiple connections in a single workbook - Takes multiple connection, authType and credentials as input
Update multiple connections in a single datasource - Takes multiple connection, authType and credentials as input

---------

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
…nections

Minor fixes on update connections
* feat: enable toggling attribute capture for a site

According to https://help.tableau.com/current/api/embedding_api/en-us/docs/embedding_api_user_attributes.html#:~:text=For%20security%20purposes%2C%20user%20attributes,a%20site%20admin%20(on%20Tableau
setting this site setting to `true` is required to enable use of
user attributes with Tableau Server and embedding workflows.

* chore: fix mypy error

---------

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
Closes #1620

Sorting the fields prior to putting them in the query string
assures that '_all_' and '_default_' appear first in the field
list, satisfying the criteria of Tableau Server/Cloud to process
those first. Order of other fields appeared to be irrelevant, so
the test simply ensures their presence.

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
* feat: support OIDC endpoints

Add support for remaining OIDC endpoints, including getting an
OIDC configuration by ID, removing the configuration, creating,
and updating configurations.

* feat: add str and repr to oidc item

---------

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
* feat: SiteAuthConfiguration str and repr

Gives SiteAuthConfiguration methods for str and repr calls
to ensure consistent display of the object.

* style: black

---------

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
Co-authored-by: Jac <jacalata@users.noreply.github.com>
Closes #1626

VirtualConnections leverages the ConnectionItem object to parse
the database connections server response. Most of other endpoints
return "userName" and the VirtualConnections' "Get Database Connections"
endpoint returns "username." Resolves the issue by allowing the
ConnectionItem to read either. Update the test assets to reflect the
actual returned value.

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
According to the .xsd schema file, the tags:batchCreate and
tags:batchDelete need a "contentType" attribute on the "content"
elements. This PR adds the missing attribute and checks in the test that
the string is carried through in the request body.

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
jorwoods and others added 24 commits December 23, 2025 12:23
* fix: black ci errors

* chore: pytestify wb_model

---------

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
* fix: black ci errors

* chore: pytestify webhooks

---------

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
* fix: black ci errors

* chore: pytestify virtuall connections

---------

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
* fix: black ci errors

* chore: pytestify ssl_config

---------

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
* fix: black ci errors

* chore: pytestify filesys helpers

---------

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
* fix: black ci errors

* chore: pytestify metrics

---------

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
* fix: black ci errors

* chore: pytestify http_requests

---------

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
* fix: black ci errors

* chore: pytestify test_site

* chore: pytestify test_site_model

---------

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
* fix: black ci errors

* chore: pytestify file uploads

---------

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
* fix: black ci errors

* chore: pytestify views

* chore: pytestify view acceleration

* feat: delete_view

Starting in Server 2025.3, views can be deleted.

---------

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
* chore: pytestify schedule

* chore: remove unused imports

* feat: batch update schedule state

---------

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
* fix: black ci errors

* feat: enable bulk adding users

* feat: ensure domain name is included if provided

* style: black

* chore: test missing user name

* feat: implement users bulk_remove

* chore: suppress deprecation warning in test

* chore: split csv add creation to own test

* chore: use subTests in remove_users

* chore: user factory function in make_user

* docs: bulk_add docstring

* fix: assert on warning instead of ignore

* chore: missed an absolute import

* docs: bulk_add docstring

* docs: create_users_csv docstring

* chore: deprecate add_all method

* test: test add_all and check DeprecationWarning

* docs: docstring updates for bulk add operations

* docs: add examples to docstrings

* chore: update deprecated version #

* feat: enable idp_configuration_id in bulk_add

* chore: remove outdated docstring text

* test: remove_users_csv

* chore: update deprecated version number

* chore: pytestify test_user

* chore: pytestify test_user_model

* style: black

---------

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
Running locally on my Mac:
- pytest: 1min 20sec
- pytest -n auto: 15sec

https://pypi.org/project/pytest-xdist/

Co-authored-by: Jac <jacalata@users.noreply.github.com>
Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
* fix: add workbook and view setter for custom view
* chore: use workbook setter in tests

Closes #1729

---------
Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
Adding support for the REST APIs which provide access to Tableau Extensions configuration.

---------

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
Co-authored-by: Brian Cantoni <bcantoni@salesforce.com>
)

* Add test for retrieving Customized Monthly schedule
* Add "Customized Monthly" as a possible schedule interval type that might be returned
* fix: black ci errors

* chore: pytestify request_option

* fix: encoding error

* fix: handle parameters for view filters

Closes #1632

Parameters need to be prefixed with "vf_Parameters." in order
to be properly registered as setting a parameter value. This PR
adds that prefix where it was missing, but leaves parameter names
that already included the prefix unmodified.

* docs: case sensitivity in the test's query string

* chore: pytest style asserts

* fix: black ci errors

---------

Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
* update publish workflow

* Update pyproject.toml and setup

* add init files to find test_repr, fix it to pass
* implement project.get_by_id

* format
@jacalata jacalata requested review from bcantoni and Copilot January 22, 2026 23:13
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request delivers a major release (v0.39) focused on modernizing the Python testing infrastructure, expanding API coverage, and improving package configuration. The changes transition the entire test suite from unittest to pytest, add support for newer Python versions, and introduce new endpoints for OIDC authentication and dashboard extensions.

Changes:

  • Migrated all test files from unittest.TestCase to pytest-style functions and fixtures
  • Added OIDC and Extensions endpoints for enhanced Tableau Server configuration
  • Updated build configuration with improved dependency management and package discovery

Reviewed changes

Copilot reviewed 127 out of 131 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
test/*.py Converted 20+ test files from unittest to pytest with fixtures
tableauserverclient/server/endpoint/oidc_endpoint.py Added new OIDC configuration endpoint
tableauserverclient/server/endpoint/extensions_endpoint.py Added dashboard extensions management endpoint
tableauserverclient/models/oidc_item.py Created OIDC configuration model
tableauserverclient/models/extensions_item.py Created extensions settings models
pyproject.toml Updated dependencies and build requirements
.github/workflows/*.yml Extended CI matrix to Python 3.14 with parallel testing
samples/*.py Added utility scripts for connection updates and user creation
tableauserverclient/server/endpoint/*_endpoint.py Added bulk connection updates and view deletion methods

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

with mocked_time() as mock_time:
exponentialBackoff = ExponentialBackoffTimer()
# The creation of our mock shouldn't sleep
pytest.approx(mock_time(), 0)
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pytest.approx function returns a comparison object but is not being used in an assertion. This should be assert mock_time() == pytest.approx(0) to actually perform the comparison.

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +22
pytest.approx(mock_time(), 0.5)
exponentialBackoff.sleep()
pytest.approx(mock_time(), 1.2)
exponentialBackoff.sleep()
pytest.approx(mock_time(), 2.18)
exponentialBackoff.sleep()
pytest.approx(mock_time(), 3.552)
exponentialBackoff.sleep()
pytest.approx(mock_time(), 5.4728)
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the previous issue, pytest.approx is being called but not used in assertions. These should all be assert mock_time() == pytest.approx(value) to properly verify the timing values.

Copilot uses AI. Check for mistakes.
exponentialBackoff.sleep()
self.assertAlmostEqual(mock_time(), 2.18)
slept = mock_time() - s
pytest.approx(slept, 30)
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pytest.approx calls are not being used in assertions. These should be assert slept == pytest.approx(30) and assert mock_time() == pytest.approx(value) respectively.

Copilot uses AI. Check for mistakes.
for _ in range(4):
exponentialBackoff.sleep()
self.assertAlmostEqual(mock_time(), 3.552)
pytest.approx(mock_time(), 3.552)
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pytest.approx calls are not being used in assertions. These should be assert slept == pytest.approx(30) and assert mock_time() == pytest.approx(value) respectively.

Copilot uses AI. Check for mistakes.
# the timeout we wait less; thereby we make sure to take the timeout
# into account as good as possible
exponentialBackoff.sleep()
pytest.approx(mock_time(), 4.5)
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pytest.approx calls are not being used in assertions. These should be assert slept == pytest.approx(30) and assert mock_time() == pytest.approx(value) respectively.

Copilot uses AI. Check for mistakes.
first_dqw = dqws.pop()
assert first_dqw.id == "c2e0e406-84fb-4f4e-9998-f20dd9306710"
assert first_dqw.warning_type == "WARNING"
assert first_dqw.message, "Hello == World!"
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion appears to have incorrect syntax. The comma should likely be == to check equality: assert first_dqw.message == \"Hello, World!\"

Copilot uses AI. Check for mistakes.
Comment on lines 13 to +15
def get_unimplemented_models():
return [
# these items should have repr , please fix
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected spacing in comment: 'repr , please' should be 'repr, please'

Copilot uses AI. Check for mistakes.
@api(version="3.27")
def batch_update_state(self, schedules, state, update_all=False) -> list[str]:
"""
Batch update the status of one or more scheudles. If update_all is set,
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected spelling of 'schedules' in docstring.

Copilot uses AI. Check for mistakes.
Parameters
----------
schedules: Iterable[ScheudleItem | str] | Any
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected spelling of 'ScheduleItem' in type annotation.

Copilot uses AI. Check for mistakes.
"pytest-xdist", "requests-mock>=1.0,<2.0", "types-requests>=2.32.4.20250913"]

[tool.setuptools.packages.find]
where = ["tableauserverclient", "tableauserverclient.helpers", "tableauserverclient.models", "tableauserverclient.server", "tableauserverclient.server.endpoint"]
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The where parameter in [tool.setuptools.packages.find] should typically be set to the root directory (e.g., [\".\"]), not individual package paths. The current configuration may not correctly discover packages. Consider using where = [\".\"] with include to specify packages, or rely on automatic discovery.

Copilot uses AI. Check for mistakes.
@jacalata
Copy link
Contributor Author

New feature work

  • support for Collections, added 'auth_type' to Connections, made Groups and GroupSets more robust, added attribute_capture_enabled to Sites, added isUat attribute to JWT, defined a model for HyperActions
  • projects.get_by_id
  • update connections for Datasources and Workbooks
  • batch update methods for Extract Refreshes and Users
  • delete a View from a Workbook
  • request all fields of an object in searches instead of specifying individually
  • implemented read/update for Extensions settings, OIDC configuration,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants