google-cloud-bigquery 1.24.0 → 1.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -215,6 +215,17 @@ module Google
215
215
  @gapi.statistics.parent_job_id
216
216
  end
217
217
 
218
+ ##
219
+ # An array containing the job resource usage breakdown by reservation, if present. Reservation usage statistics
220
+ # are only reported for jobs that are executed within reservations. On-demand jobs do not report this data.
221
+ #
222
+ # @return [Array<Google::Cloud::Bigquery::Job::ReservationUsage>, nil] The reservation usage, if present.
223
+ #
224
+ def reservation_usage
225
+ return nil unless @gapi.statistics.reservation_usage
226
+ Array(@gapi.statistics.reservation_usage).map { |g| ReservationUsage.from_gapi g }
227
+ end
228
+
218
229
  ##
219
230
  # The statistics including stack frames for a child job of a script.
220
231
  #
@@ -489,6 +500,30 @@ module Google
489
500
  end
490
501
  end
491
502
 
503
+ ##
504
+ # Represents Job resource usage breakdown by reservation.
505
+ #
506
+ # @attr_reader [String] name The reservation name or "unreserved" for on-demand resources usage.
507
+ # @attr_reader [Fixnum] slot_ms The slot-milliseconds the job spent in the given reservation.
508
+ #
509
+ class ReservationUsage
510
+ attr_reader :name
511
+ attr_reader :slot_ms
512
+
513
+ ##
514
+ # @private Creates a new ReservationUsage instance.
515
+ def initialize name, slot_ms
516
+ @name = name
517
+ @slot_ms = slot_ms
518
+ end
519
+
520
+ ##
521
+ # @private New ReservationUsage from a statistics.reservation_usage value.
522
+ def self.from_gapi gapi
523
+ new gapi.name, gapi.slot_ms
524
+ end
525
+ end
526
+
492
527
  ##
493
528
  # Represents statistics for a child job of a script.
494
529
  #
@@ -537,7 +572,8 @@ module Google
537
572
  # end
538
573
  #
539
574
  class ScriptStatistics
540
- attr_reader :evaluation_kind, :stack_frames
575
+ attr_reader :evaluation_kind
576
+ attr_reader :stack_frames
541
577
 
542
578
  ##
543
579
  # @private Creates a new ScriptStatistics instance.
@@ -547,7 +583,7 @@ module Google
547
583
  end
548
584
 
549
585
  ##
550
- # @private New ScriptStatistics from a statistics.script_statistics object.
586
+ # @private New ScriptStatistics from a statistics.script_statistics value.
551
587
  def self.from_gapi gapi
552
588
  frames = Array(gapi.stack_frames).map { |g| ScriptStackFrame.from_gapi g }
553
589
  new gapi.evaluation_kind, frames
@@ -602,7 +638,11 @@ module Google
602
638
  # end
603
639
  #
604
640
  class ScriptStackFrame
605
- attr_reader :start_line, :start_column, :end_line, :end_column, :text
641
+ attr_reader :start_line
642
+ attr_reader :start_column
643
+ attr_reader :end_line
644
+ attr_reader :end_column
645
+ attr_reader :text
606
646
 
607
647
  ##
608
648
  # @private Creates a new ScriptStackFrame instance.
@@ -72,8 +72,8 @@ module Google
72
72
  return nil unless next?
73
73
  ensure_service!
74
74
  next_kwargs = @kwargs.merge token: token
75
- next_gapi = @service.list_jobs next_kwargs
76
- self.class.from_gapi next_gapi, @service, next_kwargs
75
+ next_gapi = @service.list_jobs(**next_kwargs)
76
+ self.class.from_gapi next_gapi, @service, **next_kwargs
77
77
  end
78
78
 
79
79
  ##
@@ -121,12 +121,12 @@ module Google
121
121
  # puts job.state
122
122
  # end
123
123
  #
124
- def all request_limit: nil
124
+ def all request_limit: nil, &block
125
125
  request_limit = request_limit.to_i if request_limit
126
126
  return enum_for :all, request_limit: request_limit unless block_given?
127
127
  results = self
128
128
  loop do
129
- results.each { |r| yield r }
129
+ results.each(&block)
130
130
  if request_limit
131
131
  request_limit -= 1
132
132
  break if request_limit.negative?
@@ -37,8 +37,8 @@ module Google
37
37
  # bigquery = Google::Cloud::Bigquery.new
38
38
  # dataset = bigquery.dataset "my_dataset"
39
39
  #
40
- # gs_url = "gs://my-bucket/file-name.csv"
41
- # load_job = dataset.load_job "my_new_table", gs_url do |schema|
40
+ # gcs_uri = "gs://my-bucket/file-name.csv"
41
+ # load_job = dataset.load_job "my_new_table", gcs_uri do |schema|
42
42
  # schema.string "first_name", mode: :required
43
43
  # schema.record "cities_lived", mode: :repeated do |nested_schema|
44
44
  # nested_schema.string "place", mode: :required
@@ -112,8 +112,7 @@ module Google
112
112
  # `false` otherwise.
113
113
  #
114
114
  def iso8859_1?
115
- val = @gapi.configuration.load.encoding
116
- val == "ISO-8859-1"
115
+ @gapi.configuration.load.encoding == "ISO-8859-1"
117
116
  end
118
117
 
119
118
  ##
@@ -195,8 +194,7 @@ module Google
195
194
  # `NEWLINE_DELIMITED_JSON`, `false` otherwise.
196
195
  #
197
196
  def json?
198
- val = @gapi.configuration.load.source_format
199
- val == "NEWLINE_DELIMITED_JSON"
197
+ @gapi.configuration.load.source_format == "NEWLINE_DELIMITED_JSON"
200
198
  end
201
199
 
202
200
  ##
@@ -218,8 +216,27 @@ module Google
218
216
  # `false` otherwise.
219
217
  #
220
218
  def backup?
221
- val = @gapi.configuration.load.source_format
222
- val == "DATASTORE_BACKUP"
219
+ @gapi.configuration.load.source_format == "DATASTORE_BACKUP"
220
+ end
221
+
222
+ ##
223
+ # Checks if the source format is ORC.
224
+ #
225
+ # @return [Boolean] `true` when the source format is `ORC`,
226
+ # `false` otherwise.
227
+ #
228
+ def orc?
229
+ @gapi.configuration.load.source_format == "ORC"
230
+ end
231
+
232
+ ##
233
+ # Checks if the source format is Parquet.
234
+ #
235
+ # @return [Boolean] `true` when the source format is `PARQUET`,
236
+ # `false` otherwise.
237
+ #
238
+ def parquet?
239
+ @gapi.configuration.load.source_format == "PARQUET"
223
240
  end
224
241
 
225
242
  ##
@@ -347,6 +364,58 @@ module Google
347
364
  nil
348
365
  end
349
366
 
367
+ ###
368
+ # Checks if hive partitioning options are set.
369
+ #
370
+ # @see https://cloud.google.com/bigquery/docs/hive-partitioned-loads-gcs Loading externally partitioned data
371
+ #
372
+ # @return [Boolean] `true` when hive partitioning options are set, or `false` otherwise.
373
+ #
374
+ # @!group Attributes
375
+ #
376
+ def hive_partitioning?
377
+ !@gapi.configuration.load.hive_partitioning_options.nil?
378
+ end
379
+
380
+ ###
381
+ # The mode of hive partitioning to use when reading data. The following modes are supported:
382
+ #
383
+ # 1. `AUTO`: automatically infer partition key name(s) and type(s).
384
+ # 2. `STRINGS`: automatically infer partition key name(s). All types are interpreted as strings.
385
+ # 3. `CUSTOM`: partition key schema is encoded in the source URI prefix.
386
+ #
387
+ # @see https://cloud.google.com/bigquery/docs/hive-partitioned-loads-gcs Loading externally partitioned data
388
+ #
389
+ # @return [String, nil] The mode of hive partitioning, or `nil` if not set.
390
+ #
391
+ # @!group Attributes
392
+ #
393
+ def hive_partitioning_mode
394
+ @gapi.configuration.load.hive_partitioning_options.mode if hive_partitioning?
395
+ end
396
+
397
+ ###
398
+ # The common prefix for all source uris when hive partition detection is requested. The prefix must end
399
+ # immediately before the partition key encoding begins. For example, consider files following this data layout:
400
+ #
401
+ # ```
402
+ # gs://bucket/path_to_table/dt=2019-01-01/country=BR/id=7/file.avro
403
+ # gs://bucket/path_to_table/dt=2018-12-31/country=CA/id=3/file.avro
404
+ # ```
405
+ #
406
+ # When hive partitioning is requested with either `AUTO` or `STRINGS` mode, the common prefix can be either of
407
+ # `gs://bucket/path_to_table` or `gs://bucket/path_to_table/` (trailing slash does not matter).
408
+ #
409
+ # @see https://cloud.google.com/bigquery/docs/hive-partitioned-loads-gcs Loading externally partitioned data
410
+ #
411
+ # @return [String, nil] The common prefix for all source uris, or `nil` if not set.
412
+ #
413
+ # @!group Attributes
414
+ #
415
+ def hive_partitioning_source_uri_prefix
416
+ @gapi.configuration.load.hive_partitioning_options.source_uri_prefix if hive_partitioning?
417
+ end
418
+
350
419
  ###
351
420
  # Checks if the destination table will be range partitioned. See [Creating and using integer range partitioned
352
421
  # tables](https://cloud.google.com/bigquery/docs/creating-integer-range-partitions).
@@ -537,6 +606,7 @@ module Google
537
606
  ##
538
607
  # @private Create an Updater object.
539
608
  def initialize gapi
609
+ super()
540
610
  @updates = []
541
611
  @gapi = gapi
542
612
  @schema = nil
@@ -1326,6 +1396,89 @@ module Google
1326
1396
  @gapi.configuration.update! labels: val
1327
1397
  end
1328
1398
 
1399
+ ##
1400
+ # Sets the mode of hive partitioning to use when reading data. The following modes are supported:
1401
+ #
1402
+ # 1. `auto`: automatically infer partition key name(s) and type(s).
1403
+ # 2. `strings`: automatically infer partition key name(s). All types are interpreted as strings.
1404
+ # 3. `custom`: partition key schema is encoded in the source URI prefix.
1405
+ #
1406
+ # Not all storage formats support hive partitioning. Requesting hive partitioning on an unsupported format
1407
+ # will lead to an error. Currently supported types include: `avro`, `csv`, `json`, `orc` and `parquet`.
1408
+ #
1409
+ # See {#format=} and {#hive_partitioning_source_uri_prefix=}.
1410
+ #
1411
+ # @see https://cloud.google.com/bigquery/docs/hive-partitioned-loads-gcs Loading externally partitioned data
1412
+ #
1413
+ # @param [String, Symbol] mode The mode of hive partitioning to use when reading data.
1414
+ #
1415
+ # @example
1416
+ # require "google/cloud/bigquery"
1417
+ #
1418
+ # bigquery = Google::Cloud::Bigquery.new
1419
+ # dataset = bigquery.dataset "my_dataset"
1420
+ #
1421
+ # gcs_uri = "gs://cloud-samples-data/bigquery/hive-partitioning-samples/autolayout/*"
1422
+ # source_uri_prefix = "gs://cloud-samples-data/bigquery/hive-partitioning-samples/autolayout/"
1423
+ # load_job = dataset.load_job "my_new_table", gcs_uri do |job|
1424
+ # job.format = :parquet
1425
+ # job.hive_partitioning_mode = :auto
1426
+ # job.hive_partitioning_source_uri_prefix = source_uri_prefix
1427
+ # end
1428
+ #
1429
+ # load_job.wait_until_done!
1430
+ # load_job.done? #=> true
1431
+ #
1432
+ # @!group Attributes
1433
+ #
1434
+ def hive_partitioning_mode= mode
1435
+ @gapi.configuration.load.hive_partitioning_options ||= Google::Apis::BigqueryV2::HivePartitioningOptions.new
1436
+ @gapi.configuration.load.hive_partitioning_options.mode = mode.to_s.upcase
1437
+ end
1438
+
1439
+ ##
1440
+ # Sets the common prefix for all source uris when hive partition detection is requested. The prefix must end
1441
+ # immediately before the partition key encoding begins. For example, consider files following this data
1442
+ # layout:
1443
+ #
1444
+ # ```
1445
+ # gs://bucket/path_to_table/dt=2019-01-01/country=BR/id=7/file.avro
1446
+ # gs://bucket/path_to_table/dt=2018-12-31/country=CA/id=3/file.avro
1447
+ # ```
1448
+ #
1449
+ # When hive partitioning is requested with either `AUTO` or `STRINGS` mode, the common prefix can be either of
1450
+ # `gs://bucket/path_to_table` or `gs://bucket/path_to_table/` (trailing slash does not matter).
1451
+ #
1452
+ # See {#hive_partitioning_mode=}.
1453
+ #
1454
+ # @see https://cloud.google.com/bigquery/docs/hive-partitioned-loads-gcs Loading externally partitioned data
1455
+ #
1456
+ # @param [String] source_uri_prefix The common prefix for all source uris.
1457
+ #
1458
+ # @example
1459
+ # require "google/cloud/bigquery"
1460
+ #
1461
+ # bigquery = Google::Cloud::Bigquery.new
1462
+ # dataset = bigquery.dataset "my_dataset"
1463
+ #
1464
+ # gcs_uri = "gs://cloud-samples-data/bigquery/hive-partitioning-samples/autolayout/*"
1465
+ # source_uri_prefix = "gs://cloud-samples-data/bigquery/hive-partitioning-samples/autolayout/"
1466
+ # load_job = dataset.load_job "my_new_table", gcs_uri do |job|
1467
+ # job.format = :parquet
1468
+ # job.hive_partitioning_mode = :auto
1469
+ # job.hive_partitioning_source_uri_prefix = source_uri_prefix
1470
+ # end
1471
+ #
1472
+ # load_job.wait_until_done!
1473
+ # load_job.done? #=> true
1474
+ #
1475
+ # @!group Attributes
1476
+ #
1477
+ def hive_partitioning_source_uri_prefix= source_uri_prefix
1478
+ @gapi.configuration.load.hive_partitioning_options ||= Google::Apis::BigqueryV2::HivePartitioningOptions.new
1479
+ @gapi.configuration.load.hive_partitioning_options.source_uri_prefix = source_uri_prefix
1480
+ end
1481
+
1329
1482
  ##
1330
1483
  # Sets the field on which to range partition the table. See [Creating and using integer range partitioned
1331
1484
  # tables](https://cloud.google.com/bigquery/docs/creating-integer-range-partitions).
@@ -1345,8 +1498,8 @@ module Google
1345
1498
  # bigquery = Google::Cloud::Bigquery.new
1346
1499
  # dataset = bigquery.dataset "my_dataset"
1347
1500
  #
1348
- # gs_url = "gs://my-bucket/file-name.csv"
1349
- # load_job = dataset.load_job "my_new_table", gs_url do |job|
1501
+ # gcs_uri = "gs://my-bucket/file-name.csv"
1502
+ # load_job = dataset.load_job "my_new_table", gcs_uri do |job|
1350
1503
  # job.schema do |schema|
1351
1504
  # schema.integer "my_table_id", mode: :required
1352
1505
  # schema.string "my_table_data", mode: :required
@@ -1386,8 +1539,8 @@ module Google
1386
1539
  # bigquery = Google::Cloud::Bigquery.new
1387
1540
  # dataset = bigquery.dataset "my_dataset"
1388
1541
  #
1389
- # gs_url = "gs://my-bucket/file-name.csv"
1390
- # load_job = dataset.load_job "my_new_table", gs_url do |job|
1542
+ # gcs_uri = "gs://my-bucket/file-name.csv"
1543
+ # load_job = dataset.load_job "my_new_table", gcs_uri do |job|
1391
1544
  # job.schema do |schema|
1392
1545
  # schema.integer "my_table_id", mode: :required
1393
1546
  # schema.string "my_table_data", mode: :required
@@ -1427,8 +1580,8 @@ module Google
1427
1580
  # bigquery = Google::Cloud::Bigquery.new
1428
1581
  # dataset = bigquery.dataset "my_dataset"
1429
1582
  #
1430
- # gs_url = "gs://my-bucket/file-name.csv"
1431
- # load_job = dataset.load_job "my_new_table", gs_url do |job|
1583
+ # gcs_uri = "gs://my-bucket/file-name.csv"
1584
+ # load_job = dataset.load_job "my_new_table", gcs_uri do |job|
1432
1585
  # job.schema do |schema|
1433
1586
  # schema.integer "my_table_id", mode: :required
1434
1587
  # schema.string "my_table_data", mode: :required
@@ -1468,8 +1621,8 @@ module Google
1468
1621
  # bigquery = Google::Cloud::Bigquery.new
1469
1622
  # dataset = bigquery.dataset "my_dataset"
1470
1623
  #
1471
- # gs_url = "gs://my-bucket/file-name.csv"
1472
- # load_job = dataset.load_job "my_new_table", gs_url do |job|
1624
+ # gcs_uri = "gs://my-bucket/file-name.csv"
1625
+ # load_job = dataset.load_job "my_new_table", gcs_uri do |job|
1473
1626
  # job.schema do |schema|
1474
1627
  # schema.integer "my_table_id", mode: :required
1475
1628
  # schema.string "my_table_data", mode: :required
@@ -1510,8 +1663,8 @@ module Google
1510
1663
  # bigquery = Google::Cloud::Bigquery.new
1511
1664
  # dataset = bigquery.dataset "my_dataset"
1512
1665
  #
1513
- # gs_url = "gs://my-bucket/file-name.csv"
1514
- # load_job = dataset.load_job "my_new_table", gs_url do |job|
1666
+ # gcs_uri = "gs://my-bucket/file-name.csv"
1667
+ # load_job = dataset.load_job "my_new_table", gcs_uri do |job|
1515
1668
  # job.time_partitioning_type = "DAY"
1516
1669
  # end
1517
1670
  #
@@ -1549,8 +1702,8 @@ module Google
1549
1702
  # bigquery = Google::Cloud::Bigquery.new
1550
1703
  # dataset = bigquery.dataset "my_dataset"
1551
1704
  #
1552
- # gs_url = "gs://my-bucket/file-name.csv"
1553
- # load_job = dataset.load_job "my_new_table", gs_url do |job|
1705
+ # gcs_uri = "gs://my-bucket/file-name.csv"
1706
+ # load_job = dataset.load_job "my_new_table", gcs_uri do |job|
1554
1707
  # job.time_partitioning_type = "DAY"
1555
1708
  # job.time_partitioning_field = "dob"
1556
1709
  # job.schema do |schema|
@@ -1585,8 +1738,8 @@ module Google
1585
1738
  # bigquery = Google::Cloud::Bigquery.new
1586
1739
  # dataset = bigquery.dataset "my_dataset"
1587
1740
  #
1588
- # gs_url = "gs://my-bucket/file-name.csv"
1589
- # load_job = dataset.load_job "my_new_table", gs_url do |job|
1741
+ # gcs_uri = "gs://my-bucket/file-name.csv"
1742
+ # load_job = dataset.load_job "my_new_table", gcs_uri do |job|
1590
1743
  # job.time_partitioning_type = "DAY"
1591
1744
  # job.time_partitioning_expiration = 86_400
1592
1745
  # end
@@ -1645,8 +1798,8 @@ module Google
1645
1798
  # bigquery = Google::Cloud::Bigquery.new
1646
1799
  # dataset = bigquery.dataset "my_dataset"
1647
1800
  #
1648
- # gs_url = "gs://my-bucket/file-name.csv"
1649
- # load_job = dataset.load_job "my_new_table", gs_url do |job|
1801
+ # gcs_uri = "gs://my-bucket/file-name.csv"
1802
+ # load_job = dataset.load_job "my_new_table", gcs_uri do |job|
1650
1803
  # job.time_partitioning_type = "DAY"
1651
1804
  # job.time_partitioning_field = "dob"
1652
1805
  # job.schema do |schema|
@@ -124,12 +124,12 @@ module Google
124
124
  # puts model.model_id
125
125
  # end
126
126
  #
127
- def all request_limit: nil
127
+ def all request_limit: nil, &block
128
128
  request_limit = request_limit.to_i if request_limit
129
129
  return enum_for :all, request_limit: request_limit unless block_given?
130
130
  results = self
131
131
  loop do
132
- results.each { |r| yield r }
132
+ results.each(&block)
133
133
  if request_limit
134
134
  request_limit -= 1
135
135
  break if request_limit.negative?
@@ -0,0 +1,432 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ require "google/apis/bigquery_v2"
17
+
18
+ module Google
19
+ module Cloud
20
+ module Bigquery
21
+ ##
22
+ # # Policy
23
+ #
24
+ # Represents a Cloud IAM Policy for BigQuery resources.
25
+ #
26
+ # A Policy is a collection of bindings. A {Policy::Binding} binds one or more members to a single role. Member
27
+ # strings can describe user accounts, service accounts, Google groups, and domains. A role string represents a
28
+ # named list of permissions; each role can be an IAM predefined role or a user-created custom role.
29
+ #
30
+ # @see https://cloud.google.com/iam/docs/managing-policies Managing Policies
31
+ # @see https://cloud.google.com/bigquery/docs/table-access-controls-intro Controlling access to tables
32
+ #
33
+ # @attr [String] etag Used to check if the policy has changed since the last request. When you make a request with
34
+ # an `etag` value, Cloud IAM compares the `etag` value in the request with the existing `etag` value associated
35
+ # with the policy. It writes the policy only if the `etag` values match.
36
+ # @attr [Array<Binding>] bindings The bindings in the policy, which may be mutable or frozen depending on the
37
+ # context. See [Understanding Roles](https://cloud.google.com/iam/docs/understanding-roles) for a list of
38
+ # primitive and curated roles. See [BigQuery Table ACL
39
+ # permissions](https://cloud.google.com/bigquery/docs/table-access-controls-intro#permissions) for a list of
40
+ # values and patterns for members.
41
+ #
42
+ # @example
43
+ # require "google/cloud/bigquery"
44
+ #
45
+ # bigquery = Google::Cloud::Bigquery.new
46
+ # dataset = bigquery.dataset "my_dataset"
47
+ # table = dataset.table "my_table"
48
+ # policy = table.policy
49
+ #
50
+ # policy.frozen? #=> true
51
+ # binding_owner = policy.bindings.find { |b| b.role == "roles/owner" }
52
+ #
53
+ # binding_owner.role #=> "roles/owner"
54
+ # binding_owner.members #=> ["user:owner@example.com"]
55
+ # binding_owner.frozen? #=> true
56
+ # binding_owner.members.frozen? #=> true
57
+ #
58
+ # @example Update mutable bindings in the policy.
59
+ # require "google/cloud/bigquery"
60
+ #
61
+ # bigquery = Google::Cloud::Bigquery.new
62
+ # dataset = bigquery.dataset "my_dataset"
63
+ # table = dataset.table "my_table"
64
+ #
65
+ # table.update_policy do |p|
66
+ # p.grant role: "roles/viewer", members: "user:viewer@example.com"
67
+ # p.revoke role: "roles/editor", members: "user:editor@example.com"
68
+ # p.revoke role: "roles/owner"
69
+ # end
70
+ #
71
+ # @example Iterate over frozen bindings.
72
+ # require "google/cloud/bigquery"
73
+ #
74
+ # bigquery = Google::Cloud::Bigquery.new
75
+ # dataset = bigquery.dataset "my_dataset"
76
+ # table = dataset.table "my_table"
77
+ # policy = table.policy
78
+ #
79
+ # policy.frozen? #=> true
80
+ # policy.bindings.each do |b|
81
+ # puts b.role
82
+ # puts b.members
83
+ # end
84
+ #
85
+ # @example Update mutable bindings.
86
+ # require "google/cloud/bigquery"
87
+ #
88
+ # bigquery = Google::Cloud::Bigquery.new
89
+ # dataset = bigquery.dataset "my_dataset"
90
+ # table = dataset.table "my_table"
91
+ #
92
+ # table.update_policy do |p|
93
+ # p.bindings.each do |b|
94
+ # b.members.delete_if { |m| m.include? "@example.com" }
95
+ # end
96
+ # end
97
+ #
98
+ class Policy
99
+ attr_reader :etag
100
+ attr_reader :bindings
101
+
102
+ # @private
103
+ def initialize etag, bindings
104
+ @etag = etag.freeze
105
+ @bindings = bindings
106
+ end
107
+
108
+ ##
109
+ # Convenience method adding or updating a binding in the policy. See [Understanding
110
+ # Roles](https://cloud.google.com/iam/docs/understanding-roles) for a list of primitive and curated roles. See
111
+ # [BigQuery Table ACL
112
+ # permissions](https://cloud.google.com/bigquery/docs/table-access-controls-intro#permissions) for a list of
113
+ # values and patterns for members.
114
+ #
115
+ # @param [String] role The role that is bound to members in the binding. For example, `roles/viewer`,
116
+ # `roles/editor`, or `roles/owner`. Required.
117
+ # @param [String, Array<String>] members Specifies the identities requesting access for a Cloud Platform
118
+ # resource. `members` can have the following values. Required.
119
+ #
120
+ # * `allUsers`: A special identifier that represents anyone who is on the internet; with or without a Google
121
+ # account.
122
+ # * `allAuthenticatedUsers`: A special identifier that represents anyone who is authenticated with a Google
123
+ # account or a service account.
124
+ # * `user:<emailid>`: An email address that represents a specific Google account. For example,
125
+ # `alice@example.com`.
126
+ # * `serviceAccount:<emailid>`: An email address that represents a service account. For example,
127
+ # `my-other-app@appspot.gserviceaccount.com`.
128
+ # * `group:<emailid>`: An email address that represents a Google group. For example, `admins@example.com`.
129
+ # * `deleted:user:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing a user
130
+ # that has been recently deleted. For example, `alice@example.com?uid=123456789012345678901`. If the user
131
+ # is recovered, this value reverts to `user:<emailid>` and the recovered user retains the role in the
132
+ # binding.
133
+ # * `deleted: serviceAccount:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing
134
+ # a service account that has been recently deleted. For example,
135
+ # `my-other-app@appspot.gserviceaccount.com?uid=123456789012345678901`. If the service account is undeleted,
136
+ # this value reverts to `serviceAccount:<emailid>` and the undeleted service account retains the role in
137
+ # the binding.
138
+ # * `deleted:group:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing a Google
139
+ # group that has been recently deleted. For example, `admins@example.com?uid=123456789012345678901`. If the
140
+ # group is recovered, this value reverts to `group:<emailid>` and the recovered group retains the role in
141
+ # the binding.
142
+ # * `domain:<domain>`: The G Suite domain (primary) that represents all the users of that domain. For example,
143
+ # `google.com` or `example.com`.
144
+ #
145
+ # @return [nil]
146
+ #
147
+ # @example Grant a role to a member.
148
+ # require "google/cloud/bigquery"
149
+ #
150
+ # bigquery = Google::Cloud::Bigquery.new
151
+ # dataset = bigquery.dataset "my_dataset"
152
+ # table = dataset.table "my_table"
153
+ #
154
+ # table.update_policy do |p|
155
+ # p.grant role: "roles/viewer", members: "user:viewer@example.com"
156
+ # end
157
+ #
158
+ def grant role:, members:
159
+ existing_binding = bindings.find { |b| b.role == role }
160
+ if existing_binding
161
+ existing_binding.members.concat Array(members)
162
+ existing_binding.members.uniq!
163
+ else
164
+ bindings << Binding.new(role, members)
165
+ end
166
+ nil
167
+ end
168
+
169
+ ##
170
+ # Convenience method for removing a binding or bindings from the policy. See
171
+ # [Understanding Roles](https://cloud.google.com/iam/docs/understanding-roles) for a list of primitive and
172
+ # curated roles. See [BigQuery Table ACL
173
+ # permissions](https://cloud.google.com/bigquery/docs/table-access-controls-intro#permissions) for a list of
174
+ # values and patterns for members.
175
+ #
176
+ # @param [String] role A role that is bound to members in the policy. For example, `roles/viewer`,
177
+ # `roles/editor`, or `roles/owner`. Optional.
178
+ # @param [String, Array<String>] members Specifies the identities receiving access for a Cloud Platform
179
+ # resource. `members` can have the following values. Optional.
180
+ #
181
+ # * `allUsers`: A special identifier that represents anyone who is on the internet; with or without a Google
182
+ # account.
183
+ # * `allAuthenticatedUsers`: A special identifier that represents anyone who is authenticated with a Google
184
+ # account or a service account.
185
+ # * `user:<emailid>`: An email address that represents a specific Google account. For example,
186
+ # `alice@example.com`.
187
+ # * `serviceAccount:<emailid>`: An email address that represents a service account. For example,
188
+ # `my-other-app@appspot.gserviceaccount.com`.
189
+ # * `group:<emailid>`: An email address that represents a Google group. For example, `admins@example.com`.
190
+ # * `deleted:user:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing a user
191
+ # that has been recently deleted. For example, `alice@example.com?uid=123456789012345678901`. If the user
192
+ # is recovered, this value reverts to `user:<emailid>` and the recovered user retains the role in the
193
+ # binding.
194
+ # * `deleted: serviceAccount:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing
195
+ # a service account that has been recently deleted. For example,
196
+ # `my-other-app@appspot.gserviceaccount.com?uid=123456789012345678901`. If the service account is undeleted,
197
+ # this value reverts to `serviceAccount:<emailid>` and the undeleted service account retains the role in
198
+ # the binding.
199
+ # * `deleted:group:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing a Google
200
+ # group that has been recently deleted. For example, `admins@example.com?uid=123456789012345678901`. If the
201
+ # group is recovered, this value reverts to `group:<emailid>` and the recovered group retains the role in
202
+ # the binding.
203
+ # * `domain:<domain>`: The G Suite domain (primary) that represents all the users of that domain. For example,
204
+ # `google.com` or `example.com`.
205
+ #
206
+ # @return [nil]
207
+ #
208
+ # @example Revoke a role for a member or members.
209
+ # require "google/cloud/bigquery"
210
+ #
211
+ # bigquery = Google::Cloud::Bigquery.new
212
+ # dataset = bigquery.dataset "my_dataset"
213
+ # table = dataset.table "my_table"
214
+ #
215
+ # table.update_policy do |p|
216
+ # p.revoke role: "roles/viewer", members: "user:viewer@example.com"
217
+ # end
218
+ #
219
+ # @example Revoke a role for all members.
220
+ # require "google/cloud/bigquery"
221
+ #
222
+ # bigquery = Google::Cloud::Bigquery.new
223
+ # dataset = bigquery.dataset "my_dataset"
224
+ # table = dataset.table "my_table"
225
+ #
226
+ # table.update_policy do |p|
227
+ # p.revoke role: "roles/viewer"
228
+ # end
229
+ #
230
+ # @example Revoke all roles for a member or members.
231
+ # require "google/cloud/bigquery"
232
+ #
233
+ # bigquery = Google::Cloud::Bigquery.new
234
+ # dataset = bigquery.dataset "my_dataset"
235
+ # table = dataset.table "my_table"
236
+ #
237
+ # table.update_policy do |p|
238
+ # p.revoke members: ["user:viewer@example.com", "user:editor@example.com"]
239
+ # end
240
+ #
241
+ def revoke role: nil, members: nil
242
+ bindings_for_role = role ? bindings.select { |b| b.role == role } : bindings
243
+ bindings_for_role.each do |b|
244
+ if members
245
+ b.members -= Array(members)
246
+ bindings.delete b if b.members.empty?
247
+ else
248
+ bindings.delete b
249
+ end
250
+ end
251
+ nil
252
+ end
253
+
254
+ ##
255
+ # @private Convert the Policy to a Google::Apis::BigqueryV2::Policy.
256
+ def to_gapi
257
+ Google::Apis::BigqueryV2::Policy.new(
258
+ bindings: bindings_to_gapi,
259
+ etag: etag,
260
+ version: 1
261
+ )
262
+ end
263
+
264
+ ##
265
+ # @private Deep freeze the policy including its bindings.
266
+ def freeze
267
+ super
268
+ @bindings.each(&:freeze)
269
+ @bindings.freeze
270
+ self
271
+ end
272
+
273
+ ##
274
+ # @private New Policy from a Google::Apis::BigqueryV2::Policy object.
275
+ def self.from_gapi gapi
276
+ bindings = Array(gapi.bindings).map do |binding|
277
+ Binding.new binding.role, binding.members.to_a
278
+ end
279
+ new gapi.etag, bindings
280
+ end
281
+
282
+ ##
283
+ # # Policy::Binding
284
+ #
285
+ # Represents a Cloud IAM Binding for BigQuery resources within the context of a {Policy}.
286
+ #
287
+ # A binding binds one or more members to a single role. Member strings can describe user accounts, service
288
+ # accounts, Google groups, and domains. A role is a named list of permissions; each role can be an IAM
289
+ # predefined role or a user-created custom role.
290
+ #
291
+ # @see https://cloud.google.com/bigquery/docs/table-access-controls-intro Controlling access to tables
292
+ #
293
+ # @attr [String] role The role that is assigned to `members`. For example, `roles/viewer`, `roles/editor`, or
294
+ # `roles/owner`. Required.
295
+ # @attr [Array<String>] members Specifies the identities requesting access for a Cloud Platform resource.
296
+ # `members` can have the following values. Required.
297
+ #
298
+ # * `allUsers`: A special identifier that represents anyone who is on the internet; with or without a Google
299
+ # account.
300
+ # * `allAuthenticatedUsers`: A special identifier that represents anyone who is authenticated with a Google
301
+ # account or a service account.
302
+ # * `user:<emailid>`: An email address that represents a specific Google account. For example,
303
+ # `alice@example.com`.
304
+ # * `serviceAccount:<emailid>`: An email address that represents a service account. For example,
305
+ # `my-other-app@appspot.gserviceaccount.com`.
306
+ # * `group:<emailid>`: An email address that represents a Google group. For example, `admins@example.com`.
307
+ # * `deleted:user:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing a user
308
+ # that has been recently deleted. For example, `alice@example.com?uid=123456789012345678901`. If the user
309
+ # is recovered, this value reverts to `user:<emailid>` and the recovered user retains the role in the
310
+ # binding.
311
+ # * `deleted: serviceAccount:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing
312
+ # a service account that has been recently deleted. For example,
313
+ # `my-other-app@appspot.gserviceaccount.com?uid=123456789012345678901`. If the service account is undeleted,
314
+ # this value reverts to `serviceAccount:<emailid>` and the undeleted service account retains the role in
315
+ # the binding.
316
+ # * `deleted:group:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing a Google
317
+ # group that has been recently deleted. For example, `admins@example.com?uid=123456789012345678901`. If the
318
+ # group is recovered, this value reverts to `group:<emailid>` and the recovered group retains the role in
319
+ # the binding.
320
+ # * `domain:<domain>`: The G Suite domain (primary) that represents all the users of that domain. For example,
321
+ # `google.com` or `example.com`.
322
+ #
323
+ # @example
324
+ # require "google/cloud/bigquery"
325
+ #
326
+ # bigquery = Google::Cloud::Bigquery.new
327
+ # dataset = bigquery.dataset "my_dataset"
328
+ # table = dataset.table "my_table"
329
+ #
330
+ # policy = table.policy
331
+ # binding_owner = policy.bindings.find { |b| b.role == "roles/owner" }
332
+ #
333
+ # binding_owner.role #=> "roles/owner"
334
+ # binding_owner.members #=> ["user:owner@example.com"]
335
+ #
336
+ # binding_owner.frozen? #=> true
337
+ # binding_owner.members.frozen? #=> true
338
+ #
339
+ # @example Update mutable bindings.
340
+ # require "google/cloud/bigquery"
341
+ #
342
+ # bigquery = Google::Cloud::Bigquery.new
343
+ # dataset = bigquery.dataset "my_dataset"
344
+ # table = dataset.table "my_table"
345
+ #
346
+ # table.update_policy do |p|
347
+ # binding_owner = p.bindings.find { |b| b.role == "roles/owner" }
348
+ # binding_owner.members.delete_if { |m| m.include? "@example.com" }
349
+ # end
350
+ #
351
+ class Binding
352
+ attr_accessor :role
353
+ attr_reader :members
354
+
355
+ # @private
356
+ def initialize role, members
357
+ members = Array(members).uniq
358
+ raise ArgumentError, "members cannot be empty" if members.empty?
359
+ @role = role
360
+ @members = members
361
+ end
362
+
363
+ ##
364
+ # Sets the binding members.
365
+ #
366
+ # @param [Array<String>] new_members Specifies the identities requesting access for a Cloud Platform resource.
367
+ # `new_members` can have the following values. Required.
368
+ #
369
+ # * `allUsers`: A special identifier that represents anyone who is on the internet; with or without a Google
370
+ # account.
371
+ # * `allAuthenticatedUsers`: A special identifier that represents anyone who is authenticated with a Google
372
+ # account or a service account.
373
+ # * `user:<emailid>`: An email address that represents a specific Google account. For example,
374
+ # `alice@example.com`.
375
+ # * `serviceAccount:<emailid>`: An email address that represents a service account. For example,
376
+ # `my-other-app@appspot.gserviceaccount.com`.
377
+ # * `group:<emailid>`: An email address that represents a Google group. For example, `admins@example.com`.
378
+ # * `deleted:user:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing a user
379
+ # that has been recently deleted. For example, `alice@example.com?uid=123456789012345678901`. If the user
380
+ # is recovered, this value reverts to `user:<emailid>` and the recovered user retains the role in the
381
+ # binding.
382
+ # * `deleted: serviceAccount:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier)
383
+ # representing a service account that has been recently deleted. For example,
384
+ # `my-other-app@appspot.gserviceaccount.com?uid=123456789012345678901`. If the service account is
385
+ # undeleted, this value reverts to `serviceAccount:<emailid>` and the undeleted service account retains
386
+ # the role in the binding.
387
+ # * `deleted:group:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing a
388
+ # Google group that has been recently deleted. For example,
389
+ # `admins@example.com?uid=123456789012345678901`. If the group is recovered, this value reverts to
390
+ # `group:<emailid>` and the recovered group retains the role in the binding.
391
+ # * `domain:<domain>`: The G Suite domain (primary) that represents all the users of that domain. For
392
+ # example, `google.com` or `example.com`.
393
+ #
394
+ def members= new_members
395
+ @members = Array(new_members).uniq
396
+ end
397
+
398
+ ##
399
+ # @private Convert the Binding to a Google::Apis::BigqueryV2::Binding.
400
+ def to_gapi
401
+ Google::Apis::BigqueryV2::Binding.new role: role, members: members
402
+ end
403
+
404
+ ##
405
+ # @private Deep freeze the policy including its members.
406
+ def freeze
407
+ super
408
+ role.freeze
409
+ members.each(&:freeze)
410
+ members.freeze
411
+ self
412
+ end
413
+
414
+ ##
415
+ # @private New Binding from a Google::Apis::BigqueryV2::Binding object.
416
+ def self.from_gapi gapi
417
+ new gapi.etag, gapi.members.to_a
418
+ end
419
+ end
420
+
421
+ protected
422
+
423
+ def bindings_to_gapi
424
+ @bindings.compact.uniq.map do |b|
425
+ next if b.members.empty?
426
+ b.to_gapi
427
+ end
428
+ end
429
+ end
430
+ end
431
+ end
432
+ end