From bbfaf4c828d9b1ed3da643f375a9b2ed5dce564a Mon Sep 17 00:00:00 2001 From: Mauro Fujihara Date: Wed, 9 Sep 2026 08:42:48 +0900 Subject: [PATCH 1/7] Fix publish failures when using embedded db credentials (#458) * Require --db-server for embedded credentials/oauth workbook publishing, fix connections payload * Translations for publish.options.db-server message * Simplify changes * Adjust tests --- .../publish_command.py | 4 +- tabcmd/execution/global_options.py | 1 + tabcmd/locales/de/LC_MESSAGES/tabcmd.mo | Bin 19443 -> 20705 bytes .../locales/de/tabcmd_messages_de.properties | 1 + .../locales/en/tabcmd_messages_en.properties | 1 + tabcmd/locales/es/LC_MESSAGES/tabcmd.mo | Bin 19175 -> 20423 bytes .../locales/es/tabcmd_messages_es.properties | 1 + tabcmd/locales/fr/LC_MESSAGES/tabcmd.mo | Bin 19599 -> 20860 bytes .../locales/fr/tabcmd_messages_fr.properties | 1 + tabcmd/locales/ga/LC_MESSAGES/tabcmd.mo | Bin 19517 -> 20739 bytes .../locales/ga/tabcmd_messages_ga.properties | 1 + tabcmd/locales/it/LC_MESSAGES/tabcmd.mo | Bin 19329 -> 20566 bytes .../locales/it/tabcmd_messages_it.properties | 1 + tabcmd/locales/ja/LC_MESSAGES/tabcmd.mo | Bin 21856 -> 23120 bytes .../locales/ja/tabcmd_messages_ja.properties | 1 + tabcmd/locales/ko/LC_MESSAGES/tabcmd.mo | Bin 19611 -> 20852 bytes .../locales/ko/tabcmd_messages_ko.properties | 1 + tabcmd/locales/pt/LC_MESSAGES/tabcmd.mo | Bin 18559 -> 19726 bytes .../locales/pt/tabcmd_messages_pt.properties | 1 + tabcmd/locales/sv/LC_MESSAGES/tabcmd.mo | Bin 17966 -> 19192 bytes .../locales/sv/tabcmd_messages_sv.properties | 1 + tabcmd/locales/zh/LC_MESSAGES/tabcmd.mo | Bin 16694 -> 17876 bytes .../locales/zh/tabcmd_messages_zh.properties | 1 + tests/commands/test_publish_command.py | 39 ++++++++++++++++++ 24 files changed, 54 insertions(+), 1 deletion(-) diff --git a/tabcmd/commands/datasources_and_workbooks/publish_command.py b/tabcmd/commands/datasources_and_workbooks/publish_command.py index 1dd2bfc1..dfd26b28 100644 --- a/tabcmd/commands/datasources_and_workbooks/publish_command.py +++ b/tabcmd/commands/datasources_and_workbooks/publish_command.py @@ -69,11 +69,13 @@ def run_command(cls, args): if args.db_username: creds = TSC.models.ConnectionCredentials(args.db_username, args.db_password, embed=args.save_db_password) workbook_connections = TSC.ConnectionItem() + workbook_connections.server_address = args.db_server workbook_connections.connection_credentials = creds datasource_credentials = creds elif args.oauth_username: creds = TSC.models.ConnectionCredentials(args.oauth_username, None, embed=False, oauth=args.save_oauth) workbook_connections = TSC.ConnectionItem() + workbook_connections.server_address = args.db_server workbook_connections.connection_credentials = creds datasource_credentials = creds else: @@ -183,7 +185,7 @@ def publish_workbook_file(args, logger, server, project_id, str_filename, publis publish_mode, # args.thumbnail_username, not yet implemented in tsc # args.thumbnail_group, - connections=credentials, + connections=[credentials] if credentials else None, as_job=False, skip_connection_check=args.skip_connection_check, ) diff --git a/tabcmd/execution/global_options.py b/tabcmd/execution/global_options.py index 1a24133c..142aed58 100644 --- a/tabcmd/execution/global_options.py +++ b/tabcmd/execution/global_options.py @@ -258,6 +258,7 @@ def set_publish_args(parser): creds.add_argument("--db-username", help=_("publish.options.db-username")) parser.add_argument("--save-oauth", action="store_true", help=_("publish.options.save-oauth")) + parser.add_argument("--db-server", help=_("publish.options.db-server")) parser.add_argument("--db-password", help=_("publish.options.db-password")) parser.add_argument("--save-db-password", action="store_true", help=_("publish.options.save-db-password")) diff --git a/tabcmd/locales/de/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/de/LC_MESSAGES/tabcmd.mo index cac49e59979b37c6f1ff8c94d05485f8d51a94ca..91b71d3f466b8beee63ab0fca808d4ff020665cd 100644 GIT binary patch delta 4245 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 Cu-=>i delta 3034 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 ad%n|lZ~s1b-?h4@y}cUV(>7z}@c#iT*GHrP diff --git a/tabcmd/locales/fr/tabcmd_messages_fr.properties b/tabcmd/locales/fr/tabcmd_messages_fr.properties index 4121acbb..32744778 100644 --- a/tabcmd/locales/fr/tabcmd_messages_fr.properties +++ b/tabcmd/locales/fr/tabcmd_messages_fr.properties @@ -77,6 +77,7 @@ publish.errors.unexpected_server_response=Réponse imprévue du serveur : {0} publish.errors.server_resource_not_found=La ressource spécifiée n’existe pas, ou vous n’êtes pas autorisé à la voir. Consultez le nom de votre projet et les autorisations. publish.options.append=Ajout d’un fichier d’extrait à une source de données existante publish.options.db-password=Mot de passe de base de données pour toutes les sources de données. +publish.options.db-server=Adresse du serveur de la base de données à associer aux informations d’identification de connexion intégrées. Obligatoire lors de l’utilisation de --db-username ou --oauth-username pour publier un classeur. publish.options.db-username=Nom d’utilisateur de base de données pour toutes les sources de données. publish.options.encrypt_extracts=Crypter les extraits sur le serveur publish.options.name=Nom du classeur/de la source de données sur le serveur. S’il est omis, le classeur/la source de données prend le nom du fichier sans l’extension twb(x), tds(x) ou tde. La publication d’un fichier .tde crée une source de données diff --git a/tabcmd/locales/ga/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/ga/LC_MESSAGES/tabcmd.mo index ecc45de222bfa7f4ac973c88497140198c7e605d..27474b6b2ace2a884da58613bf07cf3246e12699 100644 GIT binary patch delta 4236 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 delta 3034 zcmYM#3v8Cv83*u#4$`em$E|i;b( zMkF8zcErWV#AIHuP3G8KHn*`gQz4A7W+P@ocEeycFx$FFvfrP+$(r{2o%4R@J?D9z zbKbY#e0JFD+lSTP8U2y1#m{U0Ci*)vJiY(_e7CWb@wz*CE??ti{0(PtkW)BiWGUmg zfah~5Yuv!|xTA6p$LjZUR4MiHjz(_$2PbjF$A@m5$_w>xVkTV63%IHJzLyz)h@*Lu z33T{5rA%gxS*VSl;u0poZ?MLloMip-)9Q;O6j2#uGHR4=7Py#MU=e5Wb4*~}%(y-_ z@i4Q{2{yC&6Qzi$T+YlhpEcgY1p4*#tzULj1NJft4KNwJ$7IkbszhF60=&*g7Vcoi zujNdBi#5K;Ebul{`oCBGVFrt>Ol0oAi1kE%hsGR!fz#N-7XF-B_|MElXN^$}j%PAm z$ee)(nR$9>XnD2j|BBh#Bh~ejmBVHAN!Odls(&(^?IR_-g(=zRX&1^G&f~*O0DGw@ zYqb(%7tXsB#kW4&P=qV>aS%2 zc$5kBX1PsrkJ;LfStNlrQvmfcLn9?z#6-57)+}GE+{Q%y zN@YJ2$Whw0GdHCoO>Ry}FJ=O}k=gTQ%>7-=)^1?}dyb>>{O{8cTp3^{I(6m?ky<6! zC(^sA%%zuIX=fJrG&Au^Cg4X{V-HiIpD_3Dr{8kiN9J!(j*OoitzWLz$iO?RFV-*% zbTg;;dzHUtCVHQlV5Iae;dD;r66O$hG2icID*Fa|c$mrjftqCaB4EmYO|HPDhklr!n9Ft^AYRm$yyq;?~z!#Z>zGLIN(r;1!c8!_B zH%D2`RzATR_c0m&l~-}Nx6@s`o{PDad0`#(A-U3E_Ed5vuiyfvBAv{OY8U75EoS`L zGwMSNwfHsz+I`IDDy9-Y;C1|v3G5mVc`{qeWcC7Y<=@!O*&cr9vUD;5Z($O6lh^P# z6W}zj&&<sCXNFy2a(D^98U?TksGw>`QnW&Zd{>#i+*~rO!g{oQ( zF!TJEIkXqJ$y()Z*0_<1T3%-6`2(lr`9G~;tJ|besCCTDej>$!rzx z|Ikfbj5x5Z5cJDxwr4!fyUnNPxxxX V*$=JzV(-+cKW^%M;@WlN{s*ShOjZB@ diff --git a/tabcmd/locales/ga/tabcmd_messages_ga.properties b/tabcmd/locales/ga/tabcmd_messages_ga.properties index cbcff215..320638ea 100644 --- a/tabcmd/locales/ga/tabcmd_messages_ga.properties +++ b/tabcmd/locales/ga/tabcmd_messages_ga.properties @@ -77,6 +77,7 @@ publish.errors.unexpected_server_response=dbf6-表:Unexpected response from the publish.errors.server_resource_not_found=b7e8-表:The resource you specified does not exist, or you do not have permission to see it. Check your project name and permissions|桜 publish.options.append=6393-表:Append extract file to existing data source|桜 publish.options.db-password=ab47-表:Database password for all data sources|桜 +publish.options.db-server=3166-表:Server address of the database to associate with the embedded connection credentials. Required when using --db-username or --oauth-username to publish a workbook.|桜 publish.options.db-username=139f-表:Database username for all data sources|桜 publish.options.encrypt_extracts=cfbc-表:Encrypt extracts on the server|桜 publish.options.name=b1bc-表:Workbook/data source name on the server. If omitted, the workbook/data source will be named after the file name, without the twb(x), tds(x), or tde extension. Publishing a .tde file will create a data source|桜 diff --git a/tabcmd/locales/it/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/it/LC_MESSAGES/tabcmd.mo index 50a2e9f7482bc20238163ed7a34ee46fb1377735..3535bc68479601021d43f43153c2d30ec4a2a17d 100644 GIT binary patch delta 4237 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 diff --git a/tabcmd/locales/it/tabcmd_messages_it.properties b/tabcmd/locales/it/tabcmd_messages_it.properties index 02ce5f78..e31ef9b8 100644 --- a/tabcmd/locales/it/tabcmd_messages_it.properties +++ b/tabcmd/locales/it/tabcmd_messages_it.properties @@ -77,6 +77,7 @@ publish.errors.unexpected_server_response=Risposta inaspettata dal server: {0} publish.errors.server_resource_not_found=La risorsa specificata non esiste o non si ha l’autorizzazione a visualizzarla. Controlla il nome del progetto e le autorizzazioni publish.options.append=Aggiungi un file di estrazione all’origine dati esistente publish.options.db-password=Password del database per tutte le origini dati +publish.options.db-server=Indirizzo del server del database da associare alle credenziali di connessione incorporate. Obbligatorio quando si usa --db-username o --oauth-username per pubblicare una cartella di lavoro. publish.options.db-username=Nome utente del database per tutte le origini dati publish.options.encrypt_extracts=Crittografare le estrazioni su un server publish.options.name=Cartella di lavoro/nome dell’origine dati sul server. Se omessa, la cartella di lavoro/origine dati sarà rinominata secondo il nome del file, senza l’estensione twb(x), tds(x) o tde. La pubblicazione di un file .tde creerà un’origine dati diff --git a/tabcmd/locales/ja/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/ja/LC_MESSAGES/tabcmd.mo index ce6b79faf1cfce2aa07564598b25344c194db3db..7d06038c6d3d0505c10eda266bf184663cd3364d 100644 GIT binary patch delta 4262 zcmYk-3s9A19>?(qH7|=7LIecF*IQwMm&!XPUcyb(#LyO2+At~81zA#yFc0S(Tr{mE zMG$iZFJ%;phOrClt(n?!oz0x4opqg7cIR-Najc14cV@?SzrXW7H2Vzy_wzjOdEe*$ zy!_*XkZWf{JU7Gp?Kh-mB93Ug&zS#UeJB^wfnmnPU^Vh*&T^T8pW=gf2gl&3Fk?pG zJdDRx=-_6I!u{5h7(w|nyx$m)xj{xR-ojDXhr13s_%O~#RhWlkagY7I0rmVf49DB3 zj)qVejd7@klJOx-M-6ZrI#`XPXy3HhAFd;Hm^-Ku4H)h=FcG0>X$e$U&^r+`?s1D9THJpZe zJ`WSH1RbnHHE;zr_3gHN54D8R^j7aDphrDl!etuf-~_C~ad;lp@IO%%{eVdrNkJoB zgxUj}ke^Hyszax2`4gl~bKTy*Z4F_1MshzQn)%lVCvihlwvdaaYz+>@4Va8Y$h)Qq z*(T;Hs^V7ENbjKbNEo%o;$(aP(@;}ih?>!ITRx2r<*Or^e{HI7xS^4SQ=1w{K~dEFp6Q;g2lW)rG`H&9D;1T~P)P&3zsTI;*$-~gsU&n2J+uoU^pcyjFzKI?l( zAIudTgB|GLKzc{kOtdaWt#vM{qp#X>IjV!l(7_9+dRmb`6T&)Zi5;Y)9y5uIdNvm| zl{u)MZ9&b%UTZz7=YO=eqB`_%)Lw{m+!;wk&FDf@$9{%9VhT|2m!p=r4#xz~KN%gX zuTed}jhfm)bX)fw)cr?rFfK!V9J5djynw2BJF3G~=-?UD41I}uza73c5?A# zkU2@FKlb79Q@0s}g_ww{s0J6{=e8V1Z?s1qM^&7K8fiWbLa#0F#(9(v;|jcq6GJ#T zd^A*VW&-nnnoJpI=UJS|*-;0oa1pkk8v3s_maond$}_M4eYhTbP&2oN?=rJ&UO{!J z8s}p@PRH9g1t%nO0&sOA^Uu5H7&o+rS5Z^biOX;>-CT=VxDwA}GKMkV8rd?`$lgSK z4{o9cGL(WgWgcoEe$+rN;}UE`b#StW?x=?ga5R>p8aj@Cyo;Bxc&b}r@+0gI$}3S7 zoWnWz1*+qrkGdV7g0m@a$GO;y%keI%W2uk1&wJL8d7K+wY`{y{g$2_X4Q4WLWX;SO zbPn?|8+(x1H0d*4Uq?MxhgsN)88~|u@8WMz|0^zI5q^vG#bY+mO|98s)B~50*)d%> z9HSW?t6>t6TP71DumWSS4judynFQ00Y;Y6JI?;ehMs>6RHA9DxN6aS}uk(M;{t(AJ z(J3<@wT8v`XS{*x&9~_4r1x*2 z_S}etlxg3jkfBafjOsuO>c#J^yQuv&%JEO|HN$Gu@86+jtSr@C^9GEjd=FJmiUg@;h}?pn(Hk0WztDQ6o0f+;wJRy0M+@wZrn?_d;LxCI-L zI!yjk?t3j5M)^C`+V)-HzCRB&BO7od`cWPE24iqoI`gj{Kg0s*#V2tC?m$iXx0rw% znYY=jX$5KtzId8-$Ak=Hm@HF*I#z!|#$Z~p0TWjl^K1MXwZzY_c1L~`+bCc0kog^% z25Ms;nir{kG9E{D>?=HqJ(!J^neMLs9yNstS^P^y?IWeZF6KRJf2)q>R^pf~i&`T1 z-fWd@D#P6b^&4i>c!^wMJ<&*Lc197Q#3ABCqK@c8Fz8@<+p6QZn0SuRpq?eF2@Oao zPVI-0(U(a{GtirKc9jkimBeNuk5Eb^-VNsXu%PzFNMb&rS?Nt$!XdVBy!WPcEe_Q9 zwO&dmh`z)o6-fPDe!Q0397`Ge_TOjAT32l@rFF!PV9xz0eMJ6p$1t;6w#K7ExGg@4bwCW6V_I4I+j(MZ8Ef5K3>k1V6FLW)p+#Jw6-3;~9?Q zh?B$~LPvTvk)rh3!k=F+c^<85i>thlxeROyU88gLp9XU!nb9&Mn-U zTe$DJsEEe$sN#WrS7oF&4s{-lUDsZ-JK*znT{su;UI_U11iUpJZ@t^ubg-+jtfTf^ zhkr+V?U{i0!|uwX-6hA`{%|1ReLLXW9q^Y0{O1C`ivfRiFw-3Ho(lLrR^DIE)d~2H z2K>7M{`!EgdEd41(b=8VEnP?UbX5DO%}GjHw>D`DoyyP2%XQSWw r-%q~1ZRam`R=gkZR?}{K?b-J7ojlooaYuJ8nG?ES;%iKqZrM9F0Agh>88o24fBm$HnO22D}IB zd_Tl!>K!=9%(V*?bmOl$48!_+Hzwg&>a$S;R^kZU_5T(l@fIr3&;e!%=%6Ob z#(S|CwZP}m!3G@0{PwYb;T#f?^`KT1&T=)uc+>>*a155B0;@swYsDBmgPQ1PjK$c2 zX2fI@QRB=&2bZ7%tyZ1+tODUU!O4(9m7pwxaa5E}^b|fj*i5mDKYNb7> zGZIZ>Jf>kX22iPg7M0Q6e!UeP>YanhzYf)H8nm()mZ1r9Q3DnG^-5F#FQ5W_4e!Gi zWKnhzwFQ5n&WJ;2ik6QWXPNJ2bg0)MFKjTL@~J1{Gq?)N@H{GWdAx;)&6c48+KMxAFHXTOOvMPE(E`jyy})*+ zxZWPN(V*0PgAd`KxEynNm>9De!Gr6ht7nn|dGe{xH6Zy6+ltENm3{R=p7U*bcrjncq%O(2XISELn$D zI3b&~<2$JKV2-pV+=trhqZo_*XL$=qM(y=dd=Q&ZXQ~J1U_p*||89(@egs_&)J1`V zZOJT86BHun&NiT~H=@qQ@5nK+^RvCjZ9txv%2W)eJ%GCYC??=bcmv-rr zTXuO4`=3o=*jz6~rPx4yJJ#c9meG!%;XJJ78Pbg>a3BWLNY*V1mEr&@BUMFHqAR?mF@96uKI_&#-jbA*7GE1VBfX_8`^u3203AJ0f&c&j diff --git a/tabcmd/locales/ja/tabcmd_messages_ja.properties b/tabcmd/locales/ja/tabcmd_messages_ja.properties index 28f1b0b9..27c911a2 100644 --- a/tabcmd/locales/ja/tabcmd_messages_ja.properties +++ b/tabcmd/locales/ja/tabcmd_messages_ja.properties @@ -77,6 +77,7 @@ publish.errors.unexpected_server_response=サーバーからの予期しない publish.errors.server_resource_not_found=指定されたリソースは存在しないか、そのリソースを表示できるパーミッションがありません。プロジェクト名とパーミッションを確認してください。 publish.options.append=抽出ファイルを既存のデータ ソースに追加 publish.options.db-password=すべてのデータ ソースのデータベース パスワード +publish.options.db-server=埋め込まれた接続資格情報に関連付けるデータベースのサーバー アドレス。ワークブックの発行時に --db-username または --oauth-username を使用する場合は必須です。 publish.options.db-username=すべてのデータ ソースのデータベース ユーザー名 publish.options.encrypt_extracts=サーバーで抽出を暗号化する publish.options.name=サーバー上のワークブック/データ ソース名。省略した場合は、ファイル名から twb(x)、tds(x)、または tde 拡張子を除いた名前に従ってワークブック/データ ソースの名前が付けられます。.tde ファイルをパブリッシュすると、データ ソースが作成されます diff --git a/tabcmd/locales/ko/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/ko/LC_MESSAGES/tabcmd.mo index 0b1ea7d1b37198615bba9ed3bd786b4c6803ae1e..54c8d41cffc8916eb2e18c5e5ddb8640a1b41025 100644 GIT binary patch delta 4259 zcmYM$3vgA%9mnwv#1KUuLP)|R$q6VBB!P-RXu`_`C@(Dqazr7XX`%|yjkfD4?3?Q!EXUxB`zAFbxMGs^8;bG*@oaQhRe~Clz4yI#D zPhi#dV z7v4e*G=@eR4nTF3i^FgZYJ%I*!NZtB|K_Z{a0PjWxr3Tfm)_A1GLTbd8fM^P)WH0x z`@)!rmr)&kgUOgc@7*vHHP9S%a1m;t+tj9i6S6lnqdNK>Y6gEn{!ACrqmd6l4R9Q) ziuoLOa9KsQJ3R!N`idurdqV~uDeyog4MLlOJ2f8p@ZTlcPv`_h{C<9-h zW^f&KV=Q@BhpE;v=+J%=>BAJGI@pI=s-vihTta2;CTgv}LkGK%2HiIlHGx^kCF3i! z7j|1uAY(9}VLIMG2jdwXRg+N%~*pNU}|w8RcFP@l=7 zqLGb9rLq7uvW=)r9I)1-M*fMl6*ZuLp!Pzd6V1qQR7Ueq16zpPVqQf3K8RZ4I!yQ8 ze=2&dzCw-s7Am#(Gi;rAQ0E`R2XQv)c8FL0NVGHgU#m>iE zrm+Nz@k#tOvTCOP=%{(fpIOU6*NqrPn4pziypwKof@>zSzU#{y*4ObN0tO)V-zpCEHJ*RZ$V|3ns6 z4;qGy$mF9kvIQIP46>;V8tF>ZNQ1Zrn{h3svD21g0B53$+Wpy+qPw{k^J!l|4KR-0Sbj4G z_1djQ`ZNtV16`bpW1k@ZDO3Vfv?=P4Wi@ATAiAjQJ@TUeCS;;AG#$_54s3 zWzl5?LjaF)R9^T7&E>j#j7|Nvl*pQy9{HHd3fbP>j`U|mABArB97U%s3pALv4QWEz0?AP zcCKc+izp=45KV;As3p_-RuS(Kbwnp(9-;EnDBgR|3Yq1ECiN_Fn9zh&*b_c)<1l%z z@W$LLT3?k);t)|n6cH-JiJyBl))vYwaLtEAw$8ai48#UDw+BO59`X4r*zS+V&HeMGPcLiRXyT9qUHjLk z&BSYj$|FPpu~QAon^Eo^bEusr^u>JHo_!IA+4>UPXzT1@@1Nh7(Dxw+_LNsvapY_4 zmF*nY6Xit6J7dhxFp*eH=rue*yh5nd6Y0bbqCXKPI!bpcXNbp%E%szJvf+K+mr3X5 z5+jKu!cVAdAb1nK@AW?HM>G(tiBp8iPoj9Qt*UxU?ziW7+YGO~_usHVc#`-#^Hl^POh3gM4PfBPCCcPZr zdH%eqP2HU@6MgM()VGJ;js&Y5w>;d|76|^^9;$UB`@?Sa z>2`lO(pXEy-FMuru5es`Rix4HxW~@9ua&tqdm@3-NZ@#u;~pt%-&G%J-sM*Nowmj@ zH&Eq0Bvj^PX0BY3xsfrg%PS}E@WGKK}znV-9@) delta 3034 zcmYM#4~*Ak9S88|P~6q22zMNZApFP!tUMG=&`uQO1OfGEMjI3dB7a;l76sT)`&BYy zG0F{f!%J6=5zQHNM@m#S-PCRkWu!(*G^JK#u|hXT>)-3`eK&7+zt{78zt8V^KF{a# ze82bZ;|;w#8|ueLoc&PobHLwNf5(QV_rcG1n@YJ*_fZbx0bb5uaWcE@C$LHP-kjFS34lrn+&AA}VK?9W~i*7U*CWSiq06hY4&eGj2ay z`5Lp(-`U2t3rZ1Fnaa#_J!@Rb1p3wVtzULl0}e6^9c6a#HnW2!Q6=&k6W|OVS-6`S zzn+(H3u}CmS>Pom^(U)-gTZ1eW0~(?%6cNdRpTnI;zaiHVm`|({3m9jAa;RO6cD=1d{|2b6exR9N-+C~=WVkTNt_17~2 ze1i$}5njfA+Eh8gl;Az)jMNO)Ec2OpZm+zTHT|6wb2(VoNaQawDLF;IrO_&}#u~3? zcDk6Ecmq?SZOjJtG8sF<)c8%-_)lis2$O4;X-rAGnd|knRpS9B(*2yk!>sW%lfq`J z$Ei$>yO{u2RQ=8LTekbCaSt=k0JFijnbLmPA_=sO0;reC8cE>-CbDI;X1S;GVJ7l@ zl>dQ%K2NT#0OwE@t-`~uXb_WyKE)LK0|AL0#%28&bciz7tQmf?p zSb8^=>GZNIUCaUtnThXY0{%K{>}4|abLRUm({K5!kIdgF9T`79TEASQk%6~WH`Xx= zY-LXI_bOjxCOXATaIWo5;UuOEoVeZfE@*jYAqaM6d7; zzQ;@XS$WDS-^!%!05g7|>K|q@^Cr*YDW=A!c`Y;P9L(UqxP=L{uJ8|Y*TwQbU!&92 z?D&38Zs1B@$=?lLub1=moQHA=b82s9p6k`LnQ{+ht^AOAv;Bq{ccSt?)%{WO z?a3{(n19bbbS%oZm<;Wsn956RW}+UQ=f6e69AyfVk`??Bzfb3+ER^Rw1$Qwi{sD7F zPBIf;rjVl=97rzKU*&{l+%K84)8JzqJ1Kc(2@~KpPPBe`TH{Xso?qY`1zgTPE?~2K zuHiD)_#(Ub7bdU{&srv4$~pWtQ}b6jpUpbAv4;t`k2wp6S(lA+TqBP|v+ZZVG>+k? znaq5Vzu+#`xWXfF0qr8-;FlS?LSKXth z@u~~=ak9c@f+zS#9%489xRl>c*GLVADRc&2&&S!rrEIu)@DMIzBEO&I_LVDl3~z4h-#_eVQ~&NUcQ>B3 Sal`8V_Vzbg`=9IV8~J}t$43GH diff --git a/tabcmd/locales/ko/tabcmd_messages_ko.properties b/tabcmd/locales/ko/tabcmd_messages_ko.properties index 23608130..3a764340 100644 --- a/tabcmd/locales/ko/tabcmd_messages_ko.properties +++ b/tabcmd/locales/ko/tabcmd_messages_ko.properties @@ -77,6 +77,7 @@ publish.errors.unexpected_server_response=서버의 예기치 않은 응답: {0} publish.errors.server_resource_not_found=지정한 리소스가 존재하지 않거나 사용자에게 리소스를 볼 수 있는 권한이 없습니다. 프로젝트 이름 및 사용 권한을 확인하십시오. publish.options.append=기존 데이터 원본에 추출 파일 추가 publish.options.db-password=모든 데이터 원본에 대한 데이터베이스 비밀번호 +publish.options.db-server=포함된 연결 자격 증명과 연결할 데이터베이스의 서버 주소입니다. 워크북을 게시할 때 --db-username 또는 --oauth-username을 사용하는 경우 필수입니다. publish.options.db-username=모든 데이터 원본에 대한 데이터베이스 사용자 이름 publish.options.encrypt_extracts=서버에서 추출 암호화 publish.options.name=서버에 있는 통합 문서/데이터 원본 이름입니다. 생략된 경우 통합 문서/데이터 원본의 이름은 twb(x), tds(x) 또는 tde 확장명을 제외한 파일 이름으로 지정됩니다. .tde 파일을 게시하면 데이터 원본이 만들어집니다. diff --git a/tabcmd/locales/pt/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/pt/LC_MESSAGES/tabcmd.mo index fa709d8ac9072a1809f9e6a84bcb9f9820df907a..2b6036af3e07644934115957dbfd020780d42deb 100644 GIT binary patch delta 4191 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 VwNYbUu6iz*p8l6FxO;A0!hdAHMn?bu diff --git a/tabcmd/locales/pt/tabcmd_messages_pt.properties b/tabcmd/locales/pt/tabcmd_messages_pt.properties index 32427c37..c9dbfddd 100644 --- a/tabcmd/locales/pt/tabcmd_messages_pt.properties +++ b/tabcmd/locales/pt/tabcmd_messages_pt.properties @@ -77,6 +77,7 @@ publish.errors.unexpected_server_response=Resposta inesperada do servidor: {0} publish.errors.server_resource_not_found=O recurso especificado não existe ou você não tem permissão para vê-lo. Verifique o nome e as permissões do projeto publish.options.append=Anexe o arquivo de extração à fonte de dados existente publish.options.db-password=Senha do banco de dados para todas as fontes de dados +publish.options.db-server=Endereço do servidor do banco de dado associado as credenciais. Obrigatório ao usar --db-username ou --oauth-username. publish.options.db-username=Nome de usuário do banco de dados para todas as fontes de dados publish.options.encrypt_extracts=Criptografar extrações no servidor publish.options.name=O nome da pasta de trabalho/fonte de dados no servidor. Se omitido, a pasta de trabalho/fonte de dados será nomeado de acordo com nome de arquivo, sem a extensão twb(x), tds(x) ou tde. Publicar um arquivo .tde criará uma fonte de dados diff --git a/tabcmd/locales/sv/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/sv/LC_MESSAGES/tabcmd.mo index e329a6a88f01698761fa98826e41d1f6b652ae22..d854d2f15868faf65e9aad94521d9805e325b2f5 100644 GIT binary patch delta 4228 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 PH+%L+O?~@2&Tjr6SoT0M diff --git a/tabcmd/locales/sv/tabcmd_messages_sv.properties b/tabcmd/locales/sv/tabcmd_messages_sv.properties index 448381a7..ccf9ea64 100644 --- a/tabcmd/locales/sv/tabcmd_messages_sv.properties +++ b/tabcmd/locales/sv/tabcmd_messages_sv.properties @@ -77,6 +77,7 @@ publish.errors.unexpected_server_response=Oväntat svar från servern: {0} publish.errors.server_resource_not_found=Den resurs du angav finns inte, eller så har du inte behörighet att se den. Kontrollera ditt projektnamn och dina behörigheter publish.options.append=Lägg till extraktfil till befintlig datakälla publish.options.db-password=Databaslösenord för alla datakällor +publish.options.db-server=Serveradress för databasen som ska associeras med de inbäddade anslutningsuppgifterna. Krävs när --db-username eller --oauth-username används för att publicera en arbetsbok. publish.options.db-username=Databasanvändarnamn för alla datakällor publish.options.encrypt_extracts=Kryptera extrakt på servern publish.options.name=Arbetsbokens/datakällans namn på servern. Om det inte anges namnges arbetsboken/datakällan efter filnamnet, utan filnamnstillägget twb(x), tds(x) eller tde. Om du publicerar en .tde-fil skapas en datakälla diff --git a/tabcmd/locales/zh/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/zh/LC_MESSAGES/tabcmd.mo index 3c25206721c3c5bbd400a93c13294fed41fbd0e6..c9d0e60fcf79751f9f4c64884b70b4c0801b22b8 100644 GIT binary patch delta 4200 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 diff --git a/tabcmd/locales/zh/tabcmd_messages_zh.properties b/tabcmd/locales/zh/tabcmd_messages_zh.properties index 58beb33a..8a1d0825 100644 --- a/tabcmd/locales/zh/tabcmd_messages_zh.properties +++ b/tabcmd/locales/zh/tabcmd_messages_zh.properties @@ -77,6 +77,7 @@ publish.errors.unexpected_server_response=来自服务器的意外响应: {0} publish.errors.server_resource_not_found=指定的资源不存在,或者您没有查看该资源的权限。请检查您的项目名称和权限 publish.options.append=将数据提取文件追加到现有数据源 publish.options.db-password=用于所有数据源的数据库密码 +publish.options.db-server=要与嵌入式连接凭据关联的数据库的服务器地址。使用 --db-username 或 --oauth-username 发布工作簿时必需。 publish.options.db-username=用于所有数据源的数据库用户名 publish.options.encrypt_extracts=在服务器上对数据提取进行加密 publish.options.name=服务器上的工作簿/数据源名称。如果省略,将使用文件名作为工作簿/数据源名称(无 twb(x)、tds(x) 或 tde 扩展名)。发布 .tde 文件将会创建数据源 diff --git a/tests/commands/test_publish_command.py b/tests/commands/test_publish_command.py index 368fd997..df4b7115 100644 --- a/tests/commands/test_publish_command.py +++ b/tests/commands/test_publish_command.py @@ -55,6 +55,7 @@ def test_publish_with_creds(self, mock_path, mock_glob, mock_session): mock_args.db_username = "username" mock_args.db_password = "oauth_u" + mock_args.db_server = "db.example.com" mock_args.save_db_password = True mock_args.oauth_username = None mock_args.embed = False @@ -66,6 +67,44 @@ def test_publish_with_creds(self, mock_path, mock_glob, mock_session): PublishCommand.run_command(mock_args) mock_session.internal_server.workbooks.publish.assert_called() + call_kwargs = mock_session.internal_server.workbooks.publish.call_args.kwargs + connections = call_kwargs["connections"] + self.assertEqual(len(connections), 1) + self.assertEqual(connections[0].server_address, "db.example.com") + + def test_publish_with_creds_no_db_server(self, mock_path, mock_glob, mock_session): + set_up_mock_server(mock_session) + mock_path = set_up_mock_path(mock_path) + + mock_args.overwrite = False + mock_args.append = True + mock_args.replace = False + + mock_args.filename = "existing_file.twbx" + mock_args.project_name = "project-name" + mock_args.parent_project_path = "projects" + mock_args.name = "" + mock_args.tabbed = True + + mock_args.db_username = "username" + mock_args.db_password = "oauth_u" + mock_args.db_server = None + mock_args.save_db_password = True + mock_args.oauth_username = None + mock_args.embed = False + + mock_args.thumbnail_username = None + mock_args.thumbnail_group = None + mock_args.skip_connection_check = False + + PublishCommand.run_command(mock_args) + mock_session.internal_server.workbooks.publish.assert_called() + + call_kwargs = mock_session.internal_server.workbooks.publish.call_args.kwargs + connections = call_kwargs["connections"] + self.assertEqual(len(connections), 1) + self.assertIsNone(connections[0].server_address) + def test_get_files_to_publish_twbx(self, mock_path, mock_glob, mock_session): set_up_mock_server(mock_session) mock_path = set_up_mock_path(mock_path) From d59ee65767d3ea009aec87afe83ae3c81c10fb96 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Tue, 8 Sep 2026 16:54:07 -0700 Subject: [PATCH 2/7] Require --db-server when publishing with embedded credentials PR #458 fixed the two publish crashes when using --db-username / --db-password / --save-db-password, but callers who omit --db-server still hit a raw ValueError from tableauserverclient's _add_connections_element. Fail fast in run_command with a clear, localizable message before we build the ConnectionItem so users get an actionable error instead of an internal stack trace. Update the existing no-db-server test to assert the exit path and add the parallel coverage for the --oauth-username branch. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../publish_command.py | 4 ++ .../locales/en/tabcmd_messages_en.properties | 1 + tests/commands/test_publish_command.py | 42 +++++++++++++++---- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/tabcmd/commands/datasources_and_workbooks/publish_command.py b/tabcmd/commands/datasources_and_workbooks/publish_command.py index dfd26b28..40d90160 100644 --- a/tabcmd/commands/datasources_and_workbooks/publish_command.py +++ b/tabcmd/commands/datasources_and_workbooks/publish_command.py @@ -46,6 +46,10 @@ def define_args(publish_parser): def run_command(cls, args): logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) + + if (args.db_username or args.oauth_username) and not args.db_server: + Errors.exit_with_error(logger, _("publish.errors.db_server_required")) + session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/locales/en/tabcmd_messages_en.properties b/tabcmd/locales/en/tabcmd_messages_en.properties index e5274230..2624e726 100644 --- a/tabcmd/locales/en/tabcmd_messages_en.properties +++ b/tabcmd/locales/en/tabcmd_messages_en.properties @@ -75,6 +75,7 @@ logout.short_description=Sign out from the server publish.description=Publish a workbook, data source, or extract to the server publish.errors.unexpected_server_response=Unexpected response from the server: {0} publish.errors.server_resource_not_found=The resource you specified does not exist, or you do not have permission to see it. Check your project name and permissions +publish.errors.db_server_required=--db-server is required when publishing a workbook with --db-username or --oauth-username. Use --db-server to specify the database server address the embedded credentials should be associated with. publish.options.append=Append extract file to existing data source publish.options.db-password=Database password for all data sources publish.options.db-server=Server address of the database to associate with the embedded connection credentials. Required when using --db-username or --oauth-username to publish a workbook. diff --git a/tests/commands/test_publish_command.py b/tests/commands/test_publish_command.py index df4b7115..57e2ef9a 100644 --- a/tests/commands/test_publish_command.py +++ b/tests/commands/test_publish_command.py @@ -72,7 +72,7 @@ def test_publish_with_creds(self, mock_path, mock_glob, mock_session): self.assertEqual(len(connections), 1) self.assertEqual(connections[0].server_address, "db.example.com") - def test_publish_with_creds_no_db_server(self, mock_path, mock_glob, mock_session): + def test_publish_with_db_username_missing_db_server_exits(self, mock_path, mock_glob, mock_session): set_up_mock_server(mock_session) mock_path = set_up_mock_path(mock_path) @@ -87,7 +87,7 @@ def test_publish_with_creds_no_db_server(self, mock_path, mock_glob, mock_sessio mock_args.tabbed = True mock_args.db_username = "username" - mock_args.db_password = "oauth_u" + mock_args.db_password = "password" mock_args.db_server = None mock_args.save_db_password = True mock_args.oauth_username = None @@ -97,13 +97,39 @@ def test_publish_with_creds_no_db_server(self, mock_path, mock_glob, mock_sessio mock_args.thumbnail_group = None mock_args.skip_connection_check = False - PublishCommand.run_command(mock_args) - mock_session.internal_server.workbooks.publish.assert_called() + with self.assertRaises(SystemExit): + PublishCommand.run_command(mock_args) + mock_session.internal_server.workbooks.publish.assert_not_called() - call_kwargs = mock_session.internal_server.workbooks.publish.call_args.kwargs - connections = call_kwargs["connections"] - self.assertEqual(len(connections), 1) - self.assertIsNone(connections[0].server_address) + def test_publish_with_oauth_username_missing_db_server_exits(self, mock_path, mock_glob, mock_session): + set_up_mock_server(mock_session) + mock_path = set_up_mock_path(mock_path) + + mock_args.overwrite = False + mock_args.append = True + mock_args.replace = False + + mock_args.filename = "existing_file.twbx" + mock_args.project_name = "project-name" + mock_args.parent_project_path = "projects" + mock_args.name = "" + mock_args.tabbed = True + + mock_args.db_username = None + mock_args.db_password = None + mock_args.db_server = None + mock_args.save_db_password = False + mock_args.oauth_username = "oauth_user" + mock_args.save_oauth = True + mock_args.embed = False + + mock_args.thumbnail_username = None + mock_args.thumbnail_group = None + mock_args.skip_connection_check = False + + with self.assertRaises(SystemExit): + PublishCommand.run_command(mock_args) + mock_session.internal_server.workbooks.publish.assert_not_called() def test_get_files_to_publish_twbx(self, mock_path, mock_glob, mock_session): set_up_mock_server(mock_session) From 95813292e5ccd677101a99ab28827439d85800f4 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Tue, 8 Sep 2026 17:04:32 -0700 Subject: [PATCH 3/7] Add e2e coverage for --db-server requirement Extend the online publish suite with: - A positive assertion for the happy path: test_wb_publish_embedded now passes --db-server matching the connection host baked into EmbeddedCredentials.twb (see-internal-slack). Without a matching server_address, tableauserverclient silently drops the embedded credentials at publish time, so covering the match is what actually proves the fix. - A negative assertion: test_wb_publish_embedded_missing_db_server_fails runs publish with --db-username but no --db-server and expects a non-zero exit. The check now fires in run_command before any network work, so hardcoded placeholder creds are sufficient. Threads a db_server keyword through the _publish_creds_args helper and records the workbook's connection host as a class constant. Co-Authored-By: Claude Opus 4.7 (1M context) --- tests/e2e/online_tests.py | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/tests/e2e/online_tests.py b/tests/e2e/online_tests.py index 741af663..b4e7a6fb 100644 --- a/tests/e2e/online_tests.py +++ b/tests/e2e/online_tests.py @@ -75,6 +75,9 @@ def get_publishable_name(file_value: str) -> str: TDS_FILE_LIVE = "live_mysql.tds" TWB_FILE_WITH_EMBEDDED_CONNECTION = "EmbeddedCredentials.twb" + # server_address baked into the workbook's element; the server-side + # embed only sticks when --db-server matches this value. + TWB_FILE_EMBEDDED_CONNECTION_SERVER = "see-internal-slack" USERS_DETAILS_FILE = "detailed_users.csv" USERNAMES_FILE = "usernames.csv" @@ -142,7 +145,9 @@ def _publish_args(file, name, optional_args=None): return arguments @staticmethod - def _publish_creds_args(arguments, db_user=None, db_pass=None, db_save=None, oauth_user=None, oauth_save=None): + def _publish_creds_args( + arguments, db_user=None, db_pass=None, db_save=None, oauth_user=None, oauth_save=None, db_server=None + ): if db_user: arguments.append("--db-username") arguments.append(db_user) @@ -156,6 +161,9 @@ def _publish_creds_args(arguments, db_user=None, db_pass=None, db_save=None, oau arguments.append(oauth_user) if oauth_save: arguments.append("--save-oauth") + if db_server: + arguments.append("--db-server") + arguments.append(db_server) return arguments @staticmethod @@ -471,11 +479,32 @@ def test_wb_publish_embedded(self): file = os.path.join("tests", "assets", TestAssets.TWB_FILE_WITH_EMBEDDED_CONNECTION) name_on_server = TestAssets.get_publishable_name(TestAssets.TWB_FILE_WITH_EMBEDDED_CONNECTION) arguments = TabcmdCall._publish_args(file, name_on_server) - arguments = TabcmdCall._publish_creds_args(arguments, database_user, database_password, True) + arguments = TabcmdCall._publish_creds_args( + arguments, + database_user, + database_password, + True, + db_server=TestAssets.TWB_FILE_EMBEDDED_CONNECTION_SERVER, + ) arguments.append("--tabbed") arguments.append("--skip-connection-check") _test_command(arguments) + @pytest.mark.order(11) + def test_wb_publish_embedded_missing_db_server_fails(self): + # publish with --db-username but no --db-server must exit non-zero with + # a friendly message rather than a raw tableauserverclient traceback. + # Hardcoded placeholder creds are fine here: the check fires in + # run_command before we touch the database. + file = os.path.join("tests", "assets", TestAssets.TWB_FILE_WITH_EMBEDDED_CONNECTION) + name_on_server = TestAssets.get_publishable_name(TestAssets.TWB_FILE_WITH_EMBEDDED_CONNECTION) + "-no-server" + arguments = TabcmdCall._publish_args(file, name_on_server) + arguments = TabcmdCall._publish_creds_args(arguments, "placeholder_user", "placeholder_pass", True) + arguments.append("--tabbed") + arguments.append("--skip-connection-check") + with pytest.raises(subprocess.CalledProcessError): + _test_command(arguments) + @pytest.mark.order(12) def test_publish_ds(self): file = os.path.join("tests", "assets", TestAssets.TDSX_FILE_WITH_EXTRACT) From cfe52c10524f43b2c9530fe2da99d14eef5ca522 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Tue, 8 Sep 2026 22:24:12 -0700 Subject: [PATCH 4/7] Address code-review findings on the --db-server guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scope the guard to workbook publishes only. Datasource publishes go through _add_credentials_element on the TSC side, which never requires server_address, so the previous unconditional check was a regression for `tabcmd publish live_mysql.tds --db-username ...` and similar. The guard now checks the target filename extension early (before auth, for fast failure) and repeats per-file inside the workbook branch of the loop for the folder-publish case. Rename the `credentials` parameter of `publish_workbook_file` to `connection` — it holds a single ConnectionItem, not a list. Tests: - Add test_publish_with_oauth_creds covering the oauth branch's server_address assignment (previously untested). - Add test_publish_datasource_with_db_username_no_db_server verifying datasource publishes are not blocked when --db-server is omitted. - Strengthen the e2e negative test to run tabcmd via subprocess.run, capture stdout/stderr, and assert on the localized guard message (or the raw key when .mo has not been regenerated). Any earlier unrelated failure — bad auth, missing asset, session expiry — now fails the test rather than passing it. - Trim the misleading class-constant comment on TWB_FILE_EMBEDDED_ CONNECTION_SERVER; reword the "before we touch the database" comment to reference the guard's contract instead of a specific location. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../publish_command.py | 18 +++-- tests/commands/test_publish_command.py | 72 +++++++++++++++++++ tests/e2e/online_tests.py | 25 ++++--- 3 files changed, 103 insertions(+), 12 deletions(-) diff --git a/tabcmd/commands/datasources_and_workbooks/publish_command.py b/tabcmd/commands/datasources_and_workbooks/publish_command.py index 40d90160..46945659 100644 --- a/tabcmd/commands/datasources_and_workbooks/publish_command.py +++ b/tabcmd/commands/datasources_and_workbooks/publish_command.py @@ -47,8 +47,14 @@ def run_command(cls, args): logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) + # Fail fast before auth when the direct target is clearly a workbook file: + # TSC's workbook publish path requires ConnectionItem.server_address whenever + # embedded credentials are supplied. Datasource publishes never need it, so + # this check is deliberately scoped to workbook extensions. if (args.db_username or args.oauth_username) and not args.db_server: - Errors.exit_with_error(logger, _("publish.errors.db_server_required")) + filename = (args.filename or "").lower() + if filename.endswith(".twb") or filename.endswith(".twbx"): + Errors.exit_with_error(logger, _("publish.errors.db_server_required")) session = Session() server = session.create_session(args, logger) @@ -92,6 +98,10 @@ def run_command(cls, args): source = PublishCommand.get_filename_extension_if_tableau_type(logger, str_filename) logger.info(_("publish.status").format(str_filename)) if source in ["twbx", "twb"]: + # TSC's workbook publish path requires ConnectionItem.server_address whenever + # embedded connection credentials are supplied; datasource publish does not. + if (args.db_username or args.oauth_username) and not args.db_server: + Errors.exit_with_error(logger, _("publish.errors.db_server_required")) try: published_item = PublishCommand.publish_workbook_file( args=args, @@ -100,7 +110,7 @@ def run_command(cls, args): project_id=project_id, str_filename=str_filename, publish_mode=publish_mode, - credentials=workbook_connections, + connection=workbook_connections, ) except Exception as e: Errors.exit_with_error(logger, exception=e) @@ -176,7 +186,7 @@ def get_publish_mode(args, logger): return publish_mode @staticmethod - def publish_workbook_file(args, logger, server, project_id, str_filename, publish_mode, credentials): + def publish_workbook_file(args, logger, server, project_id, str_filename, publish_mode, connection): if args.thumbnail_group: raise AttributeError("Generating thumbnails for a group is not yet implemented.") if args.thumbnail_username and args.thumbnail_group: @@ -189,7 +199,7 @@ def publish_workbook_file(args, logger, server, project_id, str_filename, publis publish_mode, # args.thumbnail_username, not yet implemented in tsc # args.thumbnail_group, - connections=[credentials] if credentials else None, + connections=[connection] if connection else None, as_job=False, skip_connection_check=args.skip_connection_check, ) diff --git a/tests/commands/test_publish_command.py b/tests/commands/test_publish_command.py index 57e2ef9a..5c54bc6e 100644 --- a/tests/commands/test_publish_command.py +++ b/tests/commands/test_publish_command.py @@ -131,6 +131,78 @@ def test_publish_with_oauth_username_missing_db_server_exits(self, mock_path, mo PublishCommand.run_command(mock_args) mock_session.internal_server.workbooks.publish.assert_not_called() + def test_publish_with_oauth_creds(self, mock_path, mock_glob, mock_session): + set_up_mock_server(mock_session) + mock_path = set_up_mock_path(mock_path) + + mock_args.overwrite = False + mock_args.append = True + mock_args.replace = False + + mock_args.filename = "existing_file.twbx" + mock_args.project_name = "project-name" + mock_args.parent_project_path = "projects" + mock_args.name = "" + mock_args.tabbed = True + + mock_args.db_username = None + mock_args.db_password = None + mock_args.save_db_password = False + mock_args.oauth_username = "oauth_user" + mock_args.save_oauth = True + mock_args.db_server = "db.example.com" + mock_args.embed = False + + mock_args.thumbnail_username = None + mock_args.thumbnail_group = None + mock_args.skip_connection_check = False + + PublishCommand.run_command(mock_args) + mock_session.internal_server.workbooks.publish.assert_called() + + call_kwargs = mock_session.internal_server.workbooks.publish.call_args.kwargs + connections = call_kwargs["connections"] + self.assertEqual(len(connections), 1) + self.assertEqual(connections[0].server_address, "db.example.com") + + def test_publish_datasource_with_db_username_no_db_server(self, mock_path, mock_glob, mock_session): + # Datasource publishes send credentials via _add_credentials_element on the TSC side, + # which does not require server_address. --db-server must not be required here. + set_up_mock_server(mock_session) + # set_up_mock_server only wires .workbooks on the mocked server (TSC.Server class-spec + # doesn't expose instance attributes from __init__); wire .datasources by hand. + mock_session.internal_server.datasources = mock.MagicMock() + mock_path = set_up_mock_path(mock_path) + # set_up_mock_path hardcodes splitext -> [_, 'twbx']; override so extension routing + # actually sees a datasource here. + mock_path.splitext = lambda x: ["file", "tdsx"] + + mock_args.overwrite = False + mock_args.append = True + mock_args.replace = False + + mock_args.filename = "existing_file.tdsx" + mock_args.project_name = "project-name" + mock_args.parent_project_path = "projects" + mock_args.name = "" + mock_args.tabbed = True + + mock_args.db_username = "username" + mock_args.db_password = "password" + mock_args.db_server = None + mock_args.save_db_password = True + mock_args.oauth_username = None + mock_args.embed = False + mock_args.use_tableau_bridge = False + + mock_args.thumbnail_username = None + mock_args.thumbnail_group = None + mock_args.skip_connection_check = False + + PublishCommand.run_command(mock_args) + mock_session.internal_server.datasources.publish.assert_called() + mock_session.internal_server.workbooks.publish.assert_not_called() + def test_get_files_to_publish_twbx(self, mock_path, mock_glob, mock_session): set_up_mock_server(mock_session) mock_path = set_up_mock_path(mock_path) diff --git a/tests/e2e/online_tests.py b/tests/e2e/online_tests.py index b4e7a6fb..b8ea4bfa 100644 --- a/tests/e2e/online_tests.py +++ b/tests/e2e/online_tests.py @@ -75,8 +75,7 @@ def get_publishable_name(file_value: str) -> str: TDS_FILE_LIVE = "live_mysql.tds" TWB_FILE_WITH_EMBEDDED_CONNECTION = "EmbeddedCredentials.twb" - # server_address baked into the workbook's element; the server-side - # embed only sticks when --db-server matches this value. + # server_address baked into the workbook's element. TWB_FILE_EMBEDDED_CONNECTION_SERVER = "see-internal-slack" USERS_DETAILS_FILE = "detailed_users.csv" @@ -492,18 +491,28 @@ def test_wb_publish_embedded(self): @pytest.mark.order(11) def test_wb_publish_embedded_missing_db_server_fails(self): - # publish with --db-username but no --db-server must exit non-zero with - # a friendly message rather than a raw tableauserverclient traceback. - # Hardcoded placeholder creds are fine here: the check fires in - # run_command before we touch the database. + # Publish with --db-username but no --db-server must exit non-zero with our + # friendly message rather than a raw tableauserverclient traceback. The guard + # short-circuits before any real work, so throwaway credentials are safe here. file = os.path.join("tests", "assets", TestAssets.TWB_FILE_WITH_EMBEDDED_CONNECTION) name_on_server = TestAssets.get_publishable_name(TestAssets.TWB_FILE_WITH_EMBEDDED_CONNECTION) + "-no-server" arguments = TabcmdCall._publish_args(file, name_on_server) arguments = TabcmdCall._publish_creds_args(arguments, "placeholder_user", "placeholder_pass", True) arguments.append("--tabbed") arguments.append("--skip-connection-check") - with pytest.raises(subprocess.CalledProcessError): - _test_command(arguments) + + login_args = setup_e2e.get_login_args() + if login_args is None: + pytest.skip("No credentials available (credentials.py not found)") + calling_args = ["python", "-m", "tabcmd"] + arguments + login_args + [debug_log] + ["--no-certcheck"] + result = subprocess.run(calling_args, capture_output=True, text=True) + + assert result.returncode != 0, "expected non-zero exit for missing --db-server" + # Localized string OR the raw key (if .mo has not been regenerated yet) both signal our guard. + combined = (result.stdout or "") + (result.stderr or "") + assert "publish.errors.db_server_required" in combined or "--db-server is required" in combined, ( + "expected guard message in output; got:\n" + combined + ) @pytest.mark.order(12) def test_publish_ds(self): From c7b06db458843bf55d0ca61615f732849472971f Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Thu, 10 Sep 2026 13:06:55 -0700 Subject: [PATCH 5/7] Revert "Address code-review findings on the --db-server guard" This reverts commit cfe52c10524f43b2c9530fe2da99d14eef5ca522. --- .../publish_command.py | 18 ++--- tests/commands/test_publish_command.py | 72 ------------------- tests/e2e/online_tests.py | 25 +++---- 3 files changed, 12 insertions(+), 103 deletions(-) diff --git a/tabcmd/commands/datasources_and_workbooks/publish_command.py b/tabcmd/commands/datasources_and_workbooks/publish_command.py index 46945659..40d90160 100644 --- a/tabcmd/commands/datasources_and_workbooks/publish_command.py +++ b/tabcmd/commands/datasources_and_workbooks/publish_command.py @@ -47,14 +47,8 @@ def run_command(cls, args): logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) - # Fail fast before auth when the direct target is clearly a workbook file: - # TSC's workbook publish path requires ConnectionItem.server_address whenever - # embedded credentials are supplied. Datasource publishes never need it, so - # this check is deliberately scoped to workbook extensions. if (args.db_username or args.oauth_username) and not args.db_server: - filename = (args.filename or "").lower() - if filename.endswith(".twb") or filename.endswith(".twbx"): - Errors.exit_with_error(logger, _("publish.errors.db_server_required")) + Errors.exit_with_error(logger, _("publish.errors.db_server_required")) session = Session() server = session.create_session(args, logger) @@ -98,10 +92,6 @@ def run_command(cls, args): source = PublishCommand.get_filename_extension_if_tableau_type(logger, str_filename) logger.info(_("publish.status").format(str_filename)) if source in ["twbx", "twb"]: - # TSC's workbook publish path requires ConnectionItem.server_address whenever - # embedded connection credentials are supplied; datasource publish does not. - if (args.db_username or args.oauth_username) and not args.db_server: - Errors.exit_with_error(logger, _("publish.errors.db_server_required")) try: published_item = PublishCommand.publish_workbook_file( args=args, @@ -110,7 +100,7 @@ def run_command(cls, args): project_id=project_id, str_filename=str_filename, publish_mode=publish_mode, - connection=workbook_connections, + credentials=workbook_connections, ) except Exception as e: Errors.exit_with_error(logger, exception=e) @@ -186,7 +176,7 @@ def get_publish_mode(args, logger): return publish_mode @staticmethod - def publish_workbook_file(args, logger, server, project_id, str_filename, publish_mode, connection): + def publish_workbook_file(args, logger, server, project_id, str_filename, publish_mode, credentials): if args.thumbnail_group: raise AttributeError("Generating thumbnails for a group is not yet implemented.") if args.thumbnail_username and args.thumbnail_group: @@ -199,7 +189,7 @@ def publish_workbook_file(args, logger, server, project_id, str_filename, publis publish_mode, # args.thumbnail_username, not yet implemented in tsc # args.thumbnail_group, - connections=[connection] if connection else None, + connections=[credentials] if credentials else None, as_job=False, skip_connection_check=args.skip_connection_check, ) diff --git a/tests/commands/test_publish_command.py b/tests/commands/test_publish_command.py index 5c54bc6e..57e2ef9a 100644 --- a/tests/commands/test_publish_command.py +++ b/tests/commands/test_publish_command.py @@ -131,78 +131,6 @@ def test_publish_with_oauth_username_missing_db_server_exits(self, mock_path, mo PublishCommand.run_command(mock_args) mock_session.internal_server.workbooks.publish.assert_not_called() - def test_publish_with_oauth_creds(self, mock_path, mock_glob, mock_session): - set_up_mock_server(mock_session) - mock_path = set_up_mock_path(mock_path) - - mock_args.overwrite = False - mock_args.append = True - mock_args.replace = False - - mock_args.filename = "existing_file.twbx" - mock_args.project_name = "project-name" - mock_args.parent_project_path = "projects" - mock_args.name = "" - mock_args.tabbed = True - - mock_args.db_username = None - mock_args.db_password = None - mock_args.save_db_password = False - mock_args.oauth_username = "oauth_user" - mock_args.save_oauth = True - mock_args.db_server = "db.example.com" - mock_args.embed = False - - mock_args.thumbnail_username = None - mock_args.thumbnail_group = None - mock_args.skip_connection_check = False - - PublishCommand.run_command(mock_args) - mock_session.internal_server.workbooks.publish.assert_called() - - call_kwargs = mock_session.internal_server.workbooks.publish.call_args.kwargs - connections = call_kwargs["connections"] - self.assertEqual(len(connections), 1) - self.assertEqual(connections[0].server_address, "db.example.com") - - def test_publish_datasource_with_db_username_no_db_server(self, mock_path, mock_glob, mock_session): - # Datasource publishes send credentials via _add_credentials_element on the TSC side, - # which does not require server_address. --db-server must not be required here. - set_up_mock_server(mock_session) - # set_up_mock_server only wires .workbooks on the mocked server (TSC.Server class-spec - # doesn't expose instance attributes from __init__); wire .datasources by hand. - mock_session.internal_server.datasources = mock.MagicMock() - mock_path = set_up_mock_path(mock_path) - # set_up_mock_path hardcodes splitext -> [_, 'twbx']; override so extension routing - # actually sees a datasource here. - mock_path.splitext = lambda x: ["file", "tdsx"] - - mock_args.overwrite = False - mock_args.append = True - mock_args.replace = False - - mock_args.filename = "existing_file.tdsx" - mock_args.project_name = "project-name" - mock_args.parent_project_path = "projects" - mock_args.name = "" - mock_args.tabbed = True - - mock_args.db_username = "username" - mock_args.db_password = "password" - mock_args.db_server = None - mock_args.save_db_password = True - mock_args.oauth_username = None - mock_args.embed = False - mock_args.use_tableau_bridge = False - - mock_args.thumbnail_username = None - mock_args.thumbnail_group = None - mock_args.skip_connection_check = False - - PublishCommand.run_command(mock_args) - mock_session.internal_server.datasources.publish.assert_called() - mock_session.internal_server.workbooks.publish.assert_not_called() - def test_get_files_to_publish_twbx(self, mock_path, mock_glob, mock_session): set_up_mock_server(mock_session) mock_path = set_up_mock_path(mock_path) diff --git a/tests/e2e/online_tests.py b/tests/e2e/online_tests.py index b8ea4bfa..b4e7a6fb 100644 --- a/tests/e2e/online_tests.py +++ b/tests/e2e/online_tests.py @@ -75,7 +75,8 @@ def get_publishable_name(file_value: str) -> str: TDS_FILE_LIVE = "live_mysql.tds" TWB_FILE_WITH_EMBEDDED_CONNECTION = "EmbeddedCredentials.twb" - # server_address baked into the workbook's element. + # server_address baked into the workbook's element; the server-side + # embed only sticks when --db-server matches this value. TWB_FILE_EMBEDDED_CONNECTION_SERVER = "see-internal-slack" USERS_DETAILS_FILE = "detailed_users.csv" @@ -491,28 +492,18 @@ def test_wb_publish_embedded(self): @pytest.mark.order(11) def test_wb_publish_embedded_missing_db_server_fails(self): - # Publish with --db-username but no --db-server must exit non-zero with our - # friendly message rather than a raw tableauserverclient traceback. The guard - # short-circuits before any real work, so throwaway credentials are safe here. + # publish with --db-username but no --db-server must exit non-zero with + # a friendly message rather than a raw tableauserverclient traceback. + # Hardcoded placeholder creds are fine here: the check fires in + # run_command before we touch the database. file = os.path.join("tests", "assets", TestAssets.TWB_FILE_WITH_EMBEDDED_CONNECTION) name_on_server = TestAssets.get_publishable_name(TestAssets.TWB_FILE_WITH_EMBEDDED_CONNECTION) + "-no-server" arguments = TabcmdCall._publish_args(file, name_on_server) arguments = TabcmdCall._publish_creds_args(arguments, "placeholder_user", "placeholder_pass", True) arguments.append("--tabbed") arguments.append("--skip-connection-check") - - login_args = setup_e2e.get_login_args() - if login_args is None: - pytest.skip("No credentials available (credentials.py not found)") - calling_args = ["python", "-m", "tabcmd"] + arguments + login_args + [debug_log] + ["--no-certcheck"] - result = subprocess.run(calling_args, capture_output=True, text=True) - - assert result.returncode != 0, "expected non-zero exit for missing --db-server" - # Localized string OR the raw key (if .mo has not been regenerated yet) both signal our guard. - combined = (result.stdout or "") + (result.stderr or "") - assert "publish.errors.db_server_required" in combined or "--db-server is required" in combined, ( - "expected guard message in output; got:\n" + combined - ) + with pytest.raises(subprocess.CalledProcessError): + _test_command(arguments) @pytest.mark.order(12) def test_publish_ds(self): From 6cd6e060186507659ecacb7731b3830b623274ff Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Thu, 10 Sep 2026 13:06:56 -0700 Subject: [PATCH 6/7] Revert "Add e2e coverage for --db-server requirement" This reverts commit 95813292e5ccd677101a99ab28827439d85800f4. --- tests/e2e/online_tests.py | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/tests/e2e/online_tests.py b/tests/e2e/online_tests.py index b4e7a6fb..741af663 100644 --- a/tests/e2e/online_tests.py +++ b/tests/e2e/online_tests.py @@ -75,9 +75,6 @@ def get_publishable_name(file_value: str) -> str: TDS_FILE_LIVE = "live_mysql.tds" TWB_FILE_WITH_EMBEDDED_CONNECTION = "EmbeddedCredentials.twb" - # server_address baked into the workbook's element; the server-side - # embed only sticks when --db-server matches this value. - TWB_FILE_EMBEDDED_CONNECTION_SERVER = "see-internal-slack" USERS_DETAILS_FILE = "detailed_users.csv" USERNAMES_FILE = "usernames.csv" @@ -145,9 +142,7 @@ def _publish_args(file, name, optional_args=None): return arguments @staticmethod - def _publish_creds_args( - arguments, db_user=None, db_pass=None, db_save=None, oauth_user=None, oauth_save=None, db_server=None - ): + def _publish_creds_args(arguments, db_user=None, db_pass=None, db_save=None, oauth_user=None, oauth_save=None): if db_user: arguments.append("--db-username") arguments.append(db_user) @@ -161,9 +156,6 @@ def _publish_creds_args( arguments.append(oauth_user) if oauth_save: arguments.append("--save-oauth") - if db_server: - arguments.append("--db-server") - arguments.append(db_server) return arguments @staticmethod @@ -479,32 +471,11 @@ def test_wb_publish_embedded(self): file = os.path.join("tests", "assets", TestAssets.TWB_FILE_WITH_EMBEDDED_CONNECTION) name_on_server = TestAssets.get_publishable_name(TestAssets.TWB_FILE_WITH_EMBEDDED_CONNECTION) arguments = TabcmdCall._publish_args(file, name_on_server) - arguments = TabcmdCall._publish_creds_args( - arguments, - database_user, - database_password, - True, - db_server=TestAssets.TWB_FILE_EMBEDDED_CONNECTION_SERVER, - ) + arguments = TabcmdCall._publish_creds_args(arguments, database_user, database_password, True) arguments.append("--tabbed") arguments.append("--skip-connection-check") _test_command(arguments) - @pytest.mark.order(11) - def test_wb_publish_embedded_missing_db_server_fails(self): - # publish with --db-username but no --db-server must exit non-zero with - # a friendly message rather than a raw tableauserverclient traceback. - # Hardcoded placeholder creds are fine here: the check fires in - # run_command before we touch the database. - file = os.path.join("tests", "assets", TestAssets.TWB_FILE_WITH_EMBEDDED_CONNECTION) - name_on_server = TestAssets.get_publishable_name(TestAssets.TWB_FILE_WITH_EMBEDDED_CONNECTION) + "-no-server" - arguments = TabcmdCall._publish_args(file, name_on_server) - arguments = TabcmdCall._publish_creds_args(arguments, "placeholder_user", "placeholder_pass", True) - arguments.append("--tabbed") - arguments.append("--skip-connection-check") - with pytest.raises(subprocess.CalledProcessError): - _test_command(arguments) - @pytest.mark.order(12) def test_publish_ds(self): file = os.path.join("tests", "assets", TestAssets.TDSX_FILE_WITH_EXTRACT) From ee70e71f79907a3216bccbccd467efeba20ef9c9 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Thu, 10 Sep 2026 13:06:57 -0700 Subject: [PATCH 7/7] Revert "Require --db-server when publishing with embedded credentials" This reverts commit d59ee65767d3ea009aec87afe83ae3c81c10fb96. --- .../publish_command.py | 4 -- .../locales/en/tabcmd_messages_en.properties | 1 - tests/commands/test_publish_command.py | 42 ++++--------------- 3 files changed, 8 insertions(+), 39 deletions(-) diff --git a/tabcmd/commands/datasources_and_workbooks/publish_command.py b/tabcmd/commands/datasources_and_workbooks/publish_command.py index 40d90160..dfd26b28 100644 --- a/tabcmd/commands/datasources_and_workbooks/publish_command.py +++ b/tabcmd/commands/datasources_and_workbooks/publish_command.py @@ -46,10 +46,6 @@ def define_args(publish_parser): def run_command(cls, args): logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) - - if (args.db_username or args.oauth_username) and not args.db_server: - Errors.exit_with_error(logger, _("publish.errors.db_server_required")) - session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/locales/en/tabcmd_messages_en.properties b/tabcmd/locales/en/tabcmd_messages_en.properties index 2624e726..e5274230 100644 --- a/tabcmd/locales/en/tabcmd_messages_en.properties +++ b/tabcmd/locales/en/tabcmd_messages_en.properties @@ -75,7 +75,6 @@ logout.short_description=Sign out from the server publish.description=Publish a workbook, data source, or extract to the server publish.errors.unexpected_server_response=Unexpected response from the server: {0} publish.errors.server_resource_not_found=The resource you specified does not exist, or you do not have permission to see it. Check your project name and permissions -publish.errors.db_server_required=--db-server is required when publishing a workbook with --db-username or --oauth-username. Use --db-server to specify the database server address the embedded credentials should be associated with. publish.options.append=Append extract file to existing data source publish.options.db-password=Database password for all data sources publish.options.db-server=Server address of the database to associate with the embedded connection credentials. Required when using --db-username or --oauth-username to publish a workbook. diff --git a/tests/commands/test_publish_command.py b/tests/commands/test_publish_command.py index 57e2ef9a..df4b7115 100644 --- a/tests/commands/test_publish_command.py +++ b/tests/commands/test_publish_command.py @@ -72,7 +72,7 @@ def test_publish_with_creds(self, mock_path, mock_glob, mock_session): self.assertEqual(len(connections), 1) self.assertEqual(connections[0].server_address, "db.example.com") - def test_publish_with_db_username_missing_db_server_exits(self, mock_path, mock_glob, mock_session): + def test_publish_with_creds_no_db_server(self, mock_path, mock_glob, mock_session): set_up_mock_server(mock_session) mock_path = set_up_mock_path(mock_path) @@ -87,7 +87,7 @@ def test_publish_with_db_username_missing_db_server_exits(self, mock_path, mock_ mock_args.tabbed = True mock_args.db_username = "username" - mock_args.db_password = "password" + mock_args.db_password = "oauth_u" mock_args.db_server = None mock_args.save_db_password = True mock_args.oauth_username = None @@ -97,39 +97,13 @@ def test_publish_with_db_username_missing_db_server_exits(self, mock_path, mock_ mock_args.thumbnail_group = None mock_args.skip_connection_check = False - with self.assertRaises(SystemExit): - PublishCommand.run_command(mock_args) - mock_session.internal_server.workbooks.publish.assert_not_called() - - def test_publish_with_oauth_username_missing_db_server_exits(self, mock_path, mock_glob, mock_session): - set_up_mock_server(mock_session) - mock_path = set_up_mock_path(mock_path) - - mock_args.overwrite = False - mock_args.append = True - mock_args.replace = False - - mock_args.filename = "existing_file.twbx" - mock_args.project_name = "project-name" - mock_args.parent_project_path = "projects" - mock_args.name = "" - mock_args.tabbed = True - - mock_args.db_username = None - mock_args.db_password = None - mock_args.db_server = None - mock_args.save_db_password = False - mock_args.oauth_username = "oauth_user" - mock_args.save_oauth = True - mock_args.embed = False - - mock_args.thumbnail_username = None - mock_args.thumbnail_group = None - mock_args.skip_connection_check = False + PublishCommand.run_command(mock_args) + mock_session.internal_server.workbooks.publish.assert_called() - with self.assertRaises(SystemExit): - PublishCommand.run_command(mock_args) - mock_session.internal_server.workbooks.publish.assert_not_called() + call_kwargs = mock_session.internal_server.workbooks.publish.call_args.kwargs + connections = call_kwargs["connections"] + self.assertEqual(len(connections), 1) + self.assertIsNone(connections[0].server_address) def test_get_files_to_publish_twbx(self, mock_path, mock_glob, mock_session): set_up_mock_server(mock_session)