25
25
import time
26
26
import unittest
27
27
import uuid
28
+ import random
29
+ import string
28
30
from typing import Optional
29
31
30
32
from google .api_core .exceptions import PreconditionFailed
45
47
from google .cloud import storage
46
48
from google .cloud .datacatalog_v1 import types as datacatalog_types
47
49
from google .cloud .datacatalog_v1 import PolicyTagManagerClient
50
+ from google .cloud .resourcemanager_v3 import types as resourcemanager_types
51
+ from google .cloud .resourcemanager_v3 import TagKeysClient , TagValuesClient
48
52
import psutil
49
53
import pytest
50
54
from test_utils .retry import RetryErrors
@@ -156,9 +160,12 @@ def setUpModule():
156
160
class TestBigQuery (unittest .TestCase ):
157
161
def setUp (self ):
158
162
self .to_delete = []
163
+ self .to_delete_tag_keys_values = []
159
164
160
165
def tearDown (self ):
161
166
policy_tag_client = PolicyTagManagerClient ()
167
+ tag_keys_client = TagKeysClient ()
168
+ tag_values_client = TagValuesClient ()
162
169
163
170
def _still_in_use (bad_request ):
164
171
return any (
@@ -181,6 +188,18 @@ def _still_in_use(bad_request):
181
188
else :
182
189
doomed .delete ()
183
190
191
+ # The TagKey cannot be deleted if it has any child TagValues.
192
+ for key_values in self .to_delete_tag_keys_values :
193
+ tag_key = key_values .pop ()
194
+
195
+ # Delete tag values first
196
+ [
197
+ tag_values_client .delete_tag_value (name = tag_value .name ).result ()
198
+ for tag_value in key_values
199
+ ]
200
+
201
+ tag_keys_client .delete_tag_key (name = tag_key .name ).result ()
202
+
184
203
def test_get_service_account_email (self ):
185
204
client = Config .CLIENT
186
205
@@ -278,33 +297,100 @@ def test_create_dataset_with_default_rounding_mode(self):
278
297
self .assertTrue (_dataset_exists (dataset ))
279
298
self .assertEqual (dataset .default_rounding_mode , "ROUND_HALF_EVEN" )
280
299
300
+ def _create_resource_tag_key_and_values (self , key , values ):
301
+ tag_key_client = TagKeysClient ()
302
+ tag_value_client = TagValuesClient ()
303
+
304
+ tag_key_parent = f"projects/{ Config .CLIENT .project } "
305
+ new_tag_key = resourcemanager_types .TagKey (
306
+ short_name = key , parent = tag_key_parent
307
+ )
308
+ tag_key = tag_key_client .create_tag_key (tag_key = new_tag_key ).result ()
309
+ self .to_delete_tag_keys_values .insert (0 , [tag_key ])
310
+
311
+ for value in values :
312
+ new_tag_value = resourcemanager_types .TagValue (
313
+ short_name = value , parent = tag_key .name
314
+ )
315
+ tag_value = tag_value_client .create_tag_value (
316
+ tag_value = new_tag_value
317
+ ).result ()
318
+ self .to_delete_tag_keys_values [0 ].insert (0 , tag_value )
319
+
281
320
def test_update_dataset (self ):
282
321
dataset = self .temp_dataset (_make_dataset_id ("update_dataset" ))
283
322
self .assertTrue (_dataset_exists (dataset ))
284
323
self .assertIsNone (dataset .friendly_name )
285
324
self .assertIsNone (dataset .description )
286
325
self .assertEqual (dataset .labels , {})
326
+ self .assertEqual (dataset .resource_tags , {})
287
327
self .assertIs (dataset .is_case_insensitive , False )
288
328
329
+ # This creates unique tag keys for each of test runnings for different Python versions
330
+ tag_postfix = "" .join (random .choices (string .ascii_letters + string .digits , k = 4 ))
331
+ tag_1 = f"env_{ tag_postfix } "
332
+ tag_2 = f"component_{ tag_postfix } "
333
+ tag_3 = f"project_{ tag_postfix } "
334
+
335
+ # Tags need to be created before they can be used in a dataset.
336
+ self ._create_resource_tag_key_and_values (tag_1 , ["prod" , "dev" ])
337
+ self ._create_resource_tag_key_and_values (tag_2 , ["batch" ])
338
+ self ._create_resource_tag_key_and_values (tag_3 , ["atlas" ])
339
+
289
340
dataset .friendly_name = "Friendly"
290
341
dataset .description = "Description"
291
342
dataset .labels = {"priority" : "high" , "color" : "blue" }
343
+ dataset .resource_tags = {
344
+ f"{ Config .CLIENT .project } /{ tag_1 } " : "prod" ,
345
+ f"{ Config .CLIENT .project } /{ tag_2 } " : "batch" ,
346
+ }
292
347
dataset .is_case_insensitive = True
293
348
ds2 = Config .CLIENT .update_dataset (
294
- dataset , ("friendly_name" , "description" , "labels" , "is_case_insensitive" )
349
+ dataset ,
350
+ (
351
+ "friendly_name" ,
352
+ "description" ,
353
+ "labels" ,
354
+ "resource_tags" ,
355
+ "is_case_insensitive" ,
356
+ ),
295
357
)
296
358
self .assertEqual (ds2 .friendly_name , "Friendly" )
297
359
self .assertEqual (ds2 .description , "Description" )
298
360
self .assertEqual (ds2 .labels , {"priority" : "high" , "color" : "blue" })
361
+ self .assertEqual (
362
+ ds2 .resource_tags ,
363
+ {
364
+ f"{ Config .CLIENT .project } /{ tag_1 } " : "prod" ,
365
+ f"{ Config .CLIENT .project } /{ tag_2 } " : "batch" ,
366
+ },
367
+ )
299
368
self .assertIs (ds2 .is_case_insensitive , True )
300
369
301
370
ds2 .labels = {
302
371
"color" : "green" , # change
303
372
"shape" : "circle" , # add
304
373
"priority" : None , # delete
305
374
}
306
- ds3 = Config .CLIENT .update_dataset (ds2 , ["labels" ])
375
+ ds2 .resource_tags = {
376
+ f"{ Config .CLIENT .project } /{ tag_1 } " : "dev" , # change
377
+ f"{ Config .CLIENT .project } /{ tag_3 } " : "atlas" , # add
378
+ f"{ Config .CLIENT .project } /{ tag_2 } " : None , # delete
379
+ }
380
+ ds3 = Config .CLIENT .update_dataset (ds2 , ["labels" , "resource_tags" ])
307
381
self .assertEqual (ds3 .labels , {"color" : "green" , "shape" : "circle" })
382
+ self .assertEqual (
383
+ ds3 .resource_tags ,
384
+ {
385
+ f"{ Config .CLIENT .project } /{ tag_1 } " : "dev" ,
386
+ f"{ Config .CLIENT .project } /{ tag_3 } " : "atlas" ,
387
+ },
388
+ )
389
+
390
+ # Remove all tags
391
+ ds3 .resource_tags = None
392
+ ds4 = Config .CLIENT .update_dataset (ds3 , ["resource_tags" ])
393
+ self .assertEqual (ds4 .resource_tags , {})
308
394
309
395
# If we try to update using d2 again, it will fail because the
310
396
# previous update changed the ETag.
0 commit comments