fix: CSVImport bugs (AUTH column, case-insensitive validation, username case) + QuerysetEndpoint.find_by_name - #1811
Open
jacalata wants to merge 7 commits into
Open
Conversation
2 tasks
…servation; add find_by_name Fixes for UserItem.CSVImport (issue #1809): - MAX=8 (was 7=AUTH index): 8-column lines with auth type no longer rejected as "too many columns" - create_user_from_line no longer lowercases the whole line before splitting — username case is preserved - _validate_import_line_or_throw normalizes license/admin/publisher to lowercase and auth to canonical form before comparison, so 'Viewer', 'Creator', 'SAML', 'tableauidwithmfa' etc. are all accepted - Add TableauIDWithMFA to valid auth values in validation (was missing) - 5 new tests covering each fix Add QuerysetEndpoint.find_by_name(name) (issue #1810): - Thin wrapper over .filter(name=name) returning a list - Available on all content-item endpoints (workbooks, datasources, views, users, projects, groups) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
jacalata
force-pushed
the
jac/csv-import-fixes-and-find-by-name
branch
from
July 28, 2026 01:01
d3376c9 to
948e382
Compare
3 tasks
…rty setter Two related fixes so unmapped auth values fail loudly at CSV parse time rather than producing a UserItem with silently missing auth_setting: - create_user_from_line: raise ValueError instead of silently setting auth to None when the AUTH column value isn't in _auth_canonical(). - _set_values: route auth_setting through the @property_is_enum(Auth) setter rather than writing to _auth_setting directly, so any invalid auth string is rejected at assignment. These two together close bug #5 in #1809 (setter bypass) and the silent- None finding surfaced in an adversarial review of the earlier commits on this branch. Callers who want lenient behavior (skip invalid rows, keep going) can catch the exception in their own iteration loop — that's the model tabcmd uses today via its --complete/--no-complete flag. Once this lands, tabcmd can defer its per-line validation to TSC (see #1809 and #1836). Also tightens test_too_many_columns_raises to expect ValueError only (was accepting either ValueError or AttributeError).
find_by_name was bundled with the CSVImport fixes in earlier commits because it landed in the same working commit. It's orthogonal to the CSV work and closes a different issue (#1810), so it belongs in its own PR. Reverting the 3-line addition here; will land as a separate branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Call out the behavior change explicitly. The old code lowercased the entire CSV line including usernames, display names, fullnames, and emails; the new code preserves case for those fields and only normalizes the fields used for validation comparisons. Callers relying on the previous lowercased output need to know. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three small cleanups on UserItem.CSVImport surfaced by an adversarial code review of the earlier bug fixes: - MAX renamed to COLUMN_COUNT and moved out of the ColumnType IntEnum. ColumnType(8) used to return ColumnType.MAX, a fake column mixed in with real column indices. Now the count is a class-level constant. - _auth_canonical() no longer rebuilds its dict on every call. Promoted to _AUTH_CANONICAL class attribute. - _valid_attributes[AUTH] no longer hardcodes the accepted auth values. Derived from _AUTH_CANONICAL.values() instead so there's a single source of truth for what AUTH strings are accepted. No behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jacalata
enabled auto-merge (squash)
August 6, 2026 21:19
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Updates UserItem.CSVImport to preserve case for user-provided fields while making validation/parsing of role/admin/publisher/auth fields case-insensitive and stricter, with added regression coverage and a changelog note.
Changes:
- Preserve original casing in
create_user_from_line(notably username/display name/email) and normalize only comparison-relevant fields. - Add canonicalization + validation for the AUTH column (including
TableauIDWithMFA) and fix column-count off-by-one handling. - Add tests covering mixed-case inputs, auth parsing, and error conditions; document the behavior change in the changelog.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| test/test_user_model.py | Adds regression tests for mixed-case license/auth values, username case preservation, column count, and invalid auth handling. |
| tableauserverclient/models/user_item.py | Preserves casing for non-enum CSV fields, canonicalizes/validates AUTH, fixes column count logic, and routes auth_setting assignment through the enum-guarded setter. |
| CHANGELOG.md | Documents the behavior change that CSV parsing no longer lowercases the entire line. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+311
to
+314
| # Go through the @property_is_enum(Auth) setter rather than writing | ||
| # to _auth_setting directly, so CSV-parsed users can't carry an | ||
| # invalid auth_setting that only fails later at the API call. | ||
| self.auth_setting = auth_setting |
Comment on lines
+536
to
537
| if len(line) > UserItem.CSVImport.COLUMN_COUNT: | ||
| raise AttributeError("Too many attributes in line") |
Copilot review finding on #1811. The prior version of _set_values routed auth_setting through the @property_is_enum(Auth) setter to catch bad values in CSV import. But _set_values is also called by from_xml, _parse_xml, and populate — the server-response paths. If a future Tableau release adds a new Auth enum value that this TSC version doesn't yet know about, response parsing would raise ValueError instead of transparently carrying the new value forward. CSV callers already validate against CSVImport._AUTH_CANONICAL before reaching _set_values (create_user_from_line raises with a clean error message for unknown auth strings), so the enum guard on _set_values was redundant for the CSV path and harmful for the server-parse path. Write directly to _auth_setting instead. Add a regression test that parses a UserItem XML carrying a hypothetical future auth type and asserts it survives.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes all bugs in
UserItem.CSVImporttracked in #1809, so tabcmd can eventually defer its per-line validation to TSC (see #1836 for the follow-on file-import method).CSVImport fixes
Bug 1 — AUTH column unreachable (off-by-one on
MAX)ColumnType.AUTH = 7andColumnType.MAX = 7were equal, so any 8-column line was rejected as "too many columns".MAXis now8(number of columns, not last index).Bug 2 —
create_user_from_linelowercased the whole line including usernamesline.strip().lower()ran before splitting, destroying case for LDAP/mixed-case usernames. Now only the comparison-relevant fields (license, admin, publisher, auth) are normalized.Bug 3 —
TableauIDWithMFAmissing from validation allowlist_valid_attributesfor the auth column omittedTableauIDWithMFA, causing valid CSVs to be rejected.Bug 4 — Case-sensitive validation rejected valid mixed-case values
_validate_attribute_valuecompared raw input against lowercase allowlists, soViewer,Creator,SAMLetc. were all rejected. Validation now normalizes each field's value to lowercase (or canonical form for auth) before comparison.Bug 5 —
_set_valuesbypassed the@property_is_enumguard onauth_setting_set_valueswrote directly toself._auth_setting, so a CSV-parsedUserItemcould carry an invalidauth_settingthat only failed at the API call. Routed through the property setter now, so invalid values raise at assignment.Bug 6 — Unknown AUTH values silently set
auth_setting=Nonecreate_user_from_lineused_auth_canonical().get(...)which returnedNonefor anything not in the mapping. Now raisesValueErroron unknown AUTH values so bad input can't slip through unnoticed. Callers who want lenient behavior can catch the exception.Closes #1809
🤖 Generated with Claude Code