diff --git a/lib/gcloud/bigquery.rb b/lib/gcloud/bigquery.rb index 56a23934ac7f..43fc6b01ede8 100644 --- a/lib/gcloud/bigquery.rb +++ b/lib/gcloud/bigquery.rb @@ -209,33 +209,13 @@ def self.bigquery project = nil, keyfile = nil, options = {} # bigquery = gcloud.bigquery # dataset = bigquery.dataset "my_dataset" # - # schema = { - # "fields" => [ - # { - # "name" => "first_name", - # "type" => "STRING", - # "mode" => "REQUIRED" - # }, - # { - # "name" => "cities_lived", - # "type" => "RECORD", - # "mode" => "REPEATED", - # "fields" => [ - # { - # "name" => "place", - # "type" => "STRING", - # "mode" => "REQUIRED" - # }, - # { - # "name" => "number_of_years", - # "type" => "INTEGER", - # "mode" => "REQUIRED" - # } - # ] - # } - # ] - # } - # table = dataset.create_table "people", schema: schema + # table = dataset.create_table "people" do |schema| + # schema.string "first_name", mode: :required + # schema.record "cities_lived", mode: :repeated do |nested_schema| + # nested_schema.string "place", mode: :required + # nested_schema.integer "number_of_years", mode: :required + # end + # end # # Because of the repeated field in this schema, we cannot use the CSV format # to load data into the table. @@ -306,26 +286,11 @@ def self.bigquery project = nil, keyfile = nil, options = {} # gcloud = Gcloud.new # bigquery = gcloud.bigquery # dataset = bigquery.dataset "my_dataset" - # schema = { - # "fields" => [ - # { - # "name" => "name", - # "type" => "STRING", - # "mode" => "REQUIRED" - # }, - # { - # "name" => "sex", - # "type" => "STRING", - # "mode" => "REQUIRED" - # }, - # { - # "name" => "number", - # "type" => "INTEGER", - # "mode" => "REQUIRED" - # } - # ] - # } - # table = dataset.create_table "baby_names", schema: schema + # table = dataset.create_table "baby_names" do |schema| + # schema.string "name", mode: :required + # schema.string "sex", mode: :required + # schema.integer "number", mode: :required + # end # # file = File.open "names/yob2014.txt" # load_job = table.load file, format: "csv" diff --git a/lib/gcloud/bigquery/dataset.rb b/lib/gcloud/bigquery/dataset.rb index 36e4e483b385..6842533720a6 100644 --- a/lib/gcloud/bigquery/dataset.rb +++ b/lib/gcloud/bigquery/dataset.rb @@ -16,6 +16,7 @@ require "json" require "gcloud/bigquery/errors" require "gcloud/bigquery/table" +require "gcloud/bigquery/table/schema" require "gcloud/bigquery/dataset/list" require "gcloud/bigquery/dataset/access" @@ -329,10 +330,11 @@ def delete options = {} # options[:description]:: # A user-friendly description of the table. (+String+) # options[:schema]:: - # A schema specifying fields and data types for the table. See the + # A hash specifying fields and data types for the table. A block may be + # passed instead (see examples.) For the format of this hash, see the # {Tables resource # }[https://cloud.google.com/bigquery/docs/reference/v2/tables#resource] - # for more information. (+Hash+) + # . (+Hash+) # # === Returns # @@ -347,7 +349,35 @@ def delete options = {} # dataset = bigquery.dataset "my_dataset" # table = dataset.create_table "my_table" # - # A name and description can be provided: + # You can also pass name and description options. + # + # require "gcloud" + # + # gcloud = Gcloud.new + # bigquery = gcloud.bigquery + # dataset = bigquery.dataset "my_dataset" + # table = dataset.create_table "my_table" + # name: "My Table", + # description: "A description of my table." + # + # You can define the table's schema using a block. + # + # require "gcloud" + # + # gcloud = Gcloud.new + # bigquery = gcloud.bigquery + # dataset = bigquery.dataset "my_dataset" + # table = dataset.create_table "my_table" do |schema| + # schema.string "first_name", mode: :required + # schema.record "cities_lived", mode: :repeated do |nested_schema| + # nested_schema.string "place", mode: :required + # nested_schema.integer "number_of_years", mode: :required + # end + # end + # + # Or, if you are adapting existing code that was written for the {Rest API + # }[https://cloud.google.com/bigquery/docs/reference/v2/tables#resource], + # you can pass the table's schema as a hash. # # require "gcloud" # @@ -381,20 +411,21 @@ def delete options = {} # } # ] # } - # table = dataset.create_table "my_table", - # name: "My Table", - # schema: schema + # table = dataset.create_table "my_table", schema: schema # # :category: Table # def create_table table_id, options = {} ensure_connection! - resp = connection.insert_table dataset_id, table_id, options - if resp.success? - Table.from_gapi resp.data, connection - else - fail ApiError.from_response(resp) + if block_given? + if options[:schema] + fail ArgumentError, "only schema block or schema option is allowed" + end + schema_builder = Table::Schema.new nil + yield schema_builder + options[:schema] = schema_builder.schema if schema_builder.changed? end + insert_table table_id, options end ## @@ -710,6 +741,15 @@ def self.from_gapi gapi, conn #:nodoc: protected + def insert_table table_id, options + resp = connection.insert_table dataset_id, table_id, options + if resp.success? + Table.from_gapi resp.data, connection + else + fail ApiError.from_response(resp) + end + end + ## # Raise an error unless an active connection is available. def ensure_connection! diff --git a/lib/gcloud/bigquery/table.rb b/lib/gcloud/bigquery/table.rb index 2fb492d7f670..d0b42db32d33 100644 --- a/lib/gcloud/bigquery/table.rb +++ b/lib/gcloud/bigquery/table.rb @@ -16,6 +16,7 @@ require "gcloud/bigquery/view" require "gcloud/bigquery/data" require "gcloud/bigquery/table/list" +require "gcloud/bigquery/table/schema" require "gcloud/bigquery/errors" require "gcloud/bigquery/insert_response" require "gcloud/upload" @@ -36,35 +37,14 @@ module Bigquery # gcloud = Gcloud.new # bigquery = gcloud.bigquery # dataset = bigquery.dataset "my_dataset" - # table = dataset.create_table "my_table" # - # schema = { - # "fields" => [ - # { - # "name" => "first_name", - # "type" => "STRING", - # "mode" => "REQUIRED" - # }, - # { - # "name" => "cities_lived", - # "type" => "RECORD", - # "mode" => "REPEATED", - # "fields" => [ - # { - # "name" => "place", - # "type" => "STRING", - # "mode" => "REQUIRED" - # }, - # { - # "name" => "number_of_years", - # "type" => "INTEGER", - # "mode" => "REQUIRED" - # } - # ] - # } - # ] - # } - # table.schema = schema + # table = dataset.create_table "my_table" do |schema| + # schema.string "first_name", mode: :required + # schema.record "cities_lived", mode: :repeated do |nested_schema| + # nested_schema.string "place", mode: :required + # nested_schema.integer "number_of_years", mode: :required + # end + # end # # row = { # "first_name" => "Alice", @@ -311,20 +291,64 @@ def location end ## - # The schema of the table. + # Returns the table's schema as hash containing the keys and values + # returned by the Google Cloud BigQuery {Rest API + # }[https://cloud.google.com/bigquery/docs/reference/v2/tables#resource]. + # This method can also be used to replace or update the schema by passing + # a block. See Table::Schema for available methods. To replace the current + # schema by passing a hash instead, use #schema=. + # + # === Parameters + # + # +options+:: + # An optional Hash for controlling additional behavior. (+Hash+) + # options[:replace]:: + # Whether to replace the existing schema with the new schema. If + # +false+, new fields will be added to the existing schema. The default + # value is +true+. (+Boolean+) + # + # === Examples + # + # require "gcloud" + # + # gcloud = Gcloud.new + # bigquery = gcloud.bigquery + # dataset = bigquery.dataset "my_dataset" + # table = dataset.create_table "my_table" + # + # table.schema do |schema| + # schema.string "first_name", mode: :required + # schema.record "cities_lived", mode: :repeated do |nested_schema| + # nested_schema.string "place", mode: :required + # nested_schema.integer "number_of_years", mode: :required + # end + # end # # :category: Attributes # - def schema + def schema options = {} ensure_full_data! - s = @gapi["schema"] - s = s.to_hash if s.respond_to? :to_hash - s = {} if s.nil? - s + g = @gapi + g = g.to_hash if g.respond_to? :to_hash + s = g["schema"] ||= {} + return s unless block_given? + old_schema = options[:replace] == false ? s : nil + schema_builder = Schema.new old_schema + yield schema_builder + self.schema = schema_builder.schema if schema_builder.changed? end ## # Updates the schema of the table. + # To update the schema using a block instead, use #schema. + # + # === Parameters + # + # +schema+:: + # A hash containing keys and values as specified by the Google Cloud + # BigQuery {Rest API + # }[https://cloud.google.com/bigquery/docs/reference/v2/tables#resource] + # . (+Hash+) # # === Example # diff --git a/lib/gcloud/bigquery/table/schema.rb b/lib/gcloud/bigquery/table/schema.rb new file mode 100644 index 000000000000..3d3c2e70ace5 --- /dev/null +++ b/lib/gcloud/bigquery/table/schema.rb @@ -0,0 +1,252 @@ +#-- +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module Gcloud + module Bigquery + class Table + ## + # = Table Schema + # + # A builder for BigQuery table schemas, passed to block arguments to + # Dataset#create_table and Table#schema. Supports nested and + # repeated fields via a nested block. For more information about BigQuery + # schema definitions, see {Preparing Data for BigQuery + # }[https://cloud.google.com/bigquery/preparing-data-for-bigquery]. + # + # require "gcloud" + # + # gcloud = Gcloud.new + # bigquery = gcloud.bigquery + # dataset = bigquery.dataset "my_dataset" + # table = dataset.create_table "my_table" + # + # table.schema do |schema| + # schema.string "first_name", mode: :required + # schema.record "cities_lived", mode: :repeated do |cities_lived| + # cities_lived.string "place", mode: :required + # cities_lived.integer "number_of_years", mode: :required + # end + # end + # + class Schema + MODES = %w( NULLABLE REQUIRED REPEATED ) #:nodoc: + TYPES = %w( STRING INTEGER FLOAT BOOLEAN TIMESTAMP RECORD ) #:nodoc: + + attr_reader :fields #:nodoc: + + ## + # Initializes a new schema object with an existing schema. + def initialize schema = nil, nested = false #:nodoc: + fields = (schema && schema["fields"]) || [] + @original_fields = fields.dup + @fields = fields.dup + @nested = nested + end + + def changed? #:nodoc: + @original_fields != @fields + end + + ## + # Returns the schema as hash containing the keys and values specified by + # the Google Cloud BigQuery {Rest API + # }[https://cloud.google.com/bigquery/docs/reference/v2/tables#resource] + # . + def schema #:nodoc: + { + "fields" => @fields + } + end + + ## + # Adds a string field to the schema. + # + # === Parameters + # + # +name+:: + # The field name. The name must contain only letters (a-z, A-Z), + # numbers (0-9), or underscores (_), and must start with a letter or + # underscore. The maximum length is 128 characters. (+String+) + # +options+:: + # An optional Hash for controlling additional behavior. (+Hash+) + # options[:description]:: + # A description of the field. (+String+) + # options[:mode]:: + # The field's mode. The possible values are +:nullable+, +:required+, + # and +:repeated+. The default value is +:nullable+. (+Symbol+) + def string name, options = {} + add_field name, :string, nil, options + end + + ## + # Adds an integer field to the schema. + # + # === Parameters + # + # +name+:: + # The field name. The name must contain only letters (a-z, A-Z), + # numbers (0-9), or underscores (_), and must start with a letter or + # underscore. The maximum length is 128 characters. (+String+) + # +options+:: + # An optional Hash for controlling additional behavior. (+Hash+) + # options[:description]:: + # A description of the field. (+String+) + # options[:mode]:: + # The field's mode. The possible values are +:nullable+, +:required+, + # and +:repeated+. The default value is +:nullable+. (+Symbol+) + def integer name, options = {} + add_field name, :integer, nil, options + end + + ## + # Adds a floating-point number field to the schema. + # + # === Parameters + # + # +name+:: + # The field name. The name must contain only letters (a-z, A-Z), + # numbers (0-9), or underscores (_), and must start with a letter or + # underscore. The maximum length is 128 characters. (+String+) + # +options+:: + # An optional Hash for controlling additional behavior. (+Hash+) + # options[:description]:: + # A description of the field. (+String+) + # options[:mode]:: + # The field's mode. The possible values are +:nullable+, +:required+, + # and +:repeated+. The default value is +:nullable+. (+Symbol+) + def float name, options = {} + add_field name, :float, nil, options + end + + ## + # Adds a boolean field to the schema. + # + # === Parameters + # + # +name+:: + # The field name. The name must contain only letters (a-z, A-Z), + # numbers (0-9), or underscores (_), and must start with a letter or + # underscore. The maximum length is 128 characters. (+String+) + # +options+:: + # An optional Hash for controlling additional behavior. (+Hash+) + # options[:description]:: + # A description of the field. (+String+) + # options[:mode]:: + # The field's mode. The possible values are +:nullable+, +:required+, + # and +:repeated+. The default value is +:nullable+. (+Symbol+) + def boolean name, options = {} + add_field name, :boolean, nil, options + end + + ## + # Adds a timestamp field to the schema. + # + # === Parameters + # + # +name+:: + # The field name. The name must contain only letters (a-z, A-Z), + # numbers (0-9), or underscores (_), and must start with a letter or + # underscore. The maximum length is 128 characters. (+String+) + # +options+:: + # An optional Hash for controlling additional behavior. (+Hash+) + # options[:description]:: + # A description of the field. (+String+) + # options[:mode]:: + # The field's mode. The possible values are +:nullable+, +:required+, + # and +:repeated+. The default value is +:nullable+. (+Symbol+) + def timestamp name, options = {} + add_field name, :timestamp, nil, options + end + + ## + # Adds a record field to the schema. A block must be passed describing + # the nested fields of the record. For more information about nested + # and repeated records, see {Preparing Data for BigQuery + # }[https://cloud.google.com/bigquery/preparing-data-for-bigquery]. + # + # === Parameters + # + # +name+:: + # The field name. The name must contain only letters (a-z, A-Z), + # numbers (0-9), or underscores (_), and must start with a letter or + # underscore. The maximum length is 128 characters. (+String+) + # +options+:: + # An optional Hash for controlling additional behavior. (+Hash+) + # options[:description]:: + # A description of the field. (+String+) + # options[:mode]:: + # The field's mode. The possible values are +:nullable+, +:required+, + # and +:repeated+. The default value is +:nullable+. (+Symbol+) + # + # === Example + # + # require "gcloud" + # + # gcloud = Gcloud.new + # bigquery = gcloud.bigquery + # dataset = bigquery.dataset "my_dataset" + # table = dataset.create_table "my_table" + # + # table.schema do |schema| + # schema.string "first_name", mode: :required + # schema.record "cities_lived", mode: :repeated do |cities_lived| + # cities_lived.string "place", mode: :required + # cities_lived.integer "number_of_years", mode: :required + # end + # end + # + def record name, options = {} + fail ArgumentError, "nested RECORD type is not permitted" if @nested + fail ArgumentError, "a block is required" unless block_given? + nested_schema = self.class.new nil, true + yield nested_schema + add_field name, :record, nested_schema.fields, options + end + + protected + + def upcase_type type + upcase_type = type.to_s.upcase + unless TYPES.include? upcase_type + fail ArgumentError, + "Type '#{upcase_type}' not found in #{TYPES.inspect}" + end + upcase_type + end + + def upcase_mode mode + upcase_mode = mode.to_s.upcase + unless MODES.include? upcase_mode + fail ArgumentError "Unable to determine mode for '#{mode}'" + end + upcase_mode + end + + def add_field name, type, nested_fields, options + # Remove any existing field of this name + @fields.reject! { |h| h["name"] == name } + field = { + "name" => name, + "type" => upcase_type(type) + } + field["mode"] = upcase_mode(options[:mode]) if options[:mode] + field["description"] =options[:description] if options[:description] + field["fields"] = nested_fields if nested_fields + @fields << field + end + end + end + end +end diff --git a/test/gcloud/bigquery/dataset_test.rb b/test/gcloud/bigquery/dataset_test.rb index e73b0504fff6..04c6870f19a0 100644 --- a/test/gcloud/bigquery/dataset_test.rb +++ b/test/gcloud/bigquery/dataset_test.rb @@ -27,22 +27,20 @@ { "name" => "name", "type" => "STRING", - "mode" => "NULLABLE" + "mode" => "REQUIRED" }, { "name" => "age", - "type" => "INTEGER", - "mode" => "NULLABLE" + "type" => "INTEGER" }, { "name" => "score", "type" => "FLOAT", - "mode" => "NULLABLE" + "description" => "A score from 0.0 to 10.0" }, { "name" => "active", - "type" => "BOOLEAN", - "mode" => "NULLABLE" + "type" => "BOOLEAN" } ] } @@ -106,7 +104,7 @@ table.wont_be :view? end - it "creates a table with a name, description, and schema" do + it "creates a table with a name, description, and schema option" do id = "my_table" name = "My Table" description = "This is my table" @@ -114,6 +112,7 @@ mock_connection.post "/bigquery/v2/projects/#{project}/datasets/#{dataset.dataset_id}/tables" do |env| JSON.parse(env.body)["friendlyName"].must_equal name JSON.parse(env.body)["description"].must_equal description + JSON.parse(env.body)["schema"].must_equal table_schema [200, {"Content-Type" => "application/json"}, create_table_json(id, name, description)] end @@ -131,6 +130,28 @@ table.wont_be :view? end + it "creates a table with a schema block" do + id = "my_table" + + mock_connection.post "/bigquery/v2/projects/#{project}/datasets/#{dataset.dataset_id}/tables" do |env| + JSON.parse(env.body)["schema"].must_equal table_schema + [200, {"Content-Type" => "application/json"}, + create_table_json(id, name, description)] + end + + table = dataset.create_table id do |schema| + schema.string "name", mode: :required + schema.integer "age" + schema.float "score", description: "A score from 0.0 to 10.0" + schema.boolean "active" + end + table.must_be_kind_of Gcloud::Bigquery::Table + table.table_id.must_equal id + table.schema.must_equal table_schema + table.must_be :table? + table.wont_be :view? + end + it "can create a empty view" do query = "SELECT * FROM [table]" diff --git a/test/gcloud/bigquery/table_schema_test.rb b/test/gcloud/bigquery/table_schema_test.rb new file mode 100644 index 000000000000..91a62fe4fcf5 --- /dev/null +++ b/test/gcloud/bigquery/table_schema_test.rb @@ -0,0 +1,191 @@ +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require "helper" +require "json" +require "uri" + +describe Gcloud::Bigquery::Table, :mock_bigquery do + # Create a table object with the project's mocked connection object + let(:dataset) { "my_dataset" } + + let(:table_id) { "my_table" } + let(:table_name) { "My Table" } + let(:description) { "This is my table" } + let(:etag) { "etag123456789" } + let(:location_code) { "US" } + let(:url) { "http://googleapi/bigquery/v2/projects/#{project}/datasets/#{dataset}/tables/#{table_id}" } + let(:table_hash) { random_table_hash dataset, table_id, table_name, description } + let(:table) { Gcloud::Bigquery::Table.from_gapi table_hash, bigquery.connection } + + let(:schema) { table.schema.dup } + + it "gets the schema, fields, and headers" do + table.schema.must_be_kind_of Hash + table.schema.keys.must_include "fields" + table.fields.must_equal table.schema["fields"] + table.headers.must_equal ["name", "age", "score", "active"] + end + + it "sets its schema if assigned a hash" do + new_table_data = new_table_hash + new_table_data["schema"]["fields"].first["name"] = "moniker" + new_schema = new_table_data["schema"] + mock_connection.patch "/bigquery/v2/projects/#{project}/datasets/#{dataset}/tables/#{table_id}" do |env| + json = JSON.parse env.body + json["schema"].must_equal new_schema + [200, { "Content-Type" => "application/json" }, + new_table_data.to_json] + end + + table.schema = new_schema + + table.schema.must_equal new_schema + table.schema["fields"].first["name"].must_equal "moniker" + end + + it "sets a flat schema via a block" do + new_table_data = new_table_hash + new_table_data["schema"]["fields"] = [ + field_string_required, + field_integer, + field_float, + field_boolean, + field_timestamp + ] + mock_connection.patch "/bigquery/v2/projects/#{project}/datasets/#{dataset}/tables/#{table_id}" do |env| + json = JSON.parse env.body + json["schema"].must_equal new_table_data["schema"] + [200, { "Content-Type" => "application/json" }, new_table_data.to_json] + end + + table.schema do |schema| + schema.string "first_name", mode: :required + schema.integer "rank", description: "An integer value from 1 to 100" + schema.float "accuracy" + schema.boolean "approved" + schema.timestamp "start_date" + end + + table.schema.must_equal new_table_data["schema"] + end + + it "adds to its existing schema with replace option false" do + new_table_data = new_table_hash + new_table_data["schema"]["fields"] << field_timestamp + mock_connection.patch "/bigquery/v2/projects/#{project}/datasets/#{dataset}/tables/#{table_id}" do |env| + json = JSON.parse env.body + json["schema"].must_equal new_table_data["schema"] + [200, { "Content-Type" => "application/json" }, new_table_data.to_json] + end + + table.schema replace: false do |schema| + schema.timestamp "start_date" + end + + table.schema.must_equal new_table_data["schema"] + end + + it "sets a nested repeated schema field via a nested block" do + new_table_data = new_table_hash + new_table_data["schema"]["fields"] = [ + field_string_required, + field_record_repeated + ] + mock_connection.patch "/bigquery/v2/projects/#{project}/datasets/#{dataset}/tables/#{table_id}" do |env| + json = JSON.parse env.body + json["schema"].must_equal new_table_data["schema"] + [200, { "Content-Type" => "application/json" }, new_table_data.to_json] + end + + table.schema do |schema| + schema.string "first_name", mode: :required + schema.record "cities_lived", mode: :repeated do |nested| + nested.integer "rank", description: "An integer value from 1 to 100" + nested.timestamp "start_date" + end + end + + table.schema.must_equal new_table_data["schema"] + end + + it "raises when nesting fields more than one level deep" do + original_schema = table.schema.dup + + assert_raises ArgumentError do + table.schema do |schema| + schema.string "first_name", mode: :required + schema.record "countries_lived", mode: :repeated do |nested| + nested.record "cities_lived", mode: :repeated do |nested_2| + nested_2.integer "rank", description: "An integer value from 1 to 100" + end + end + end + end + + table.schema.must_equal original_schema + end + + protected + + def new_table_hash + random_table_hash dataset, table_id, table_name, description + end + + def field_string_required + { + "name" => "first_name", + "type" => "STRING", + "mode" => "REQUIRED" + } + end + + def field_integer + { + "name" => "rank", + "type" => "INTEGER", + "description" => "An integer value from 1 to 100" + } + end + + def field_float + { + "name" => "accuracy", + "type" => "FLOAT" + } + end + + def field_boolean + { + "name" => "approved", + "type" => "BOOLEAN" + } + end + + def field_timestamp + { + "name" => "start_date", + "type" => "TIMESTAMP" + } + end + + def field_record_repeated + { + "name" => "cities_lived", + "type" => "RECORD", + "mode" => "REPEATED", + "fields" => [ field_integer, field_timestamp ] + } + end +end diff --git a/test/gcloud/bigquery/table_update_test.rb b/test/gcloud/bigquery/table_update_test.rb index c02a9b543ccf..40f1fe3227f7 100644 --- a/test/gcloud/bigquery/table_update_test.rb +++ b/test/gcloud/bigquery/table_update_test.rb @@ -66,34 +66,4 @@ table.description.must_equal new_description table.schema.must_equal schema end - - it "updates its schema" do - new_schema = schema.dup - new_schema["fields"].first["name"].must_equal "name" - new_schema["fields"].first["name"] = "moniker" - - mock_connection.patch "/bigquery/v2/projects/#{project}/datasets/#{dataset_id}/tables/#{table_id}" do |env| - json = JSON.parse env.body - json["schema"].must_equal new_schema - [200, {"Content-Type"=>"application/json"}, - new_table_schema_json] - end - - table.name.must_equal table_name - table.description.must_equal description - table.schema.must_equal schema - - table.schema = new_schema - - table.name.must_equal table_name - table.description.must_equal description - table.schema.must_equal new_schema - table.schema["fields"].first["name"].must_equal "moniker" - end - - def new_table_schema_json - hash = random_table_hash dataset_id, table_id, table_name, description - hash["schema"]["fields"].first["name"] = "moniker" - hash.to_json - end end diff --git a/test/helper.rb b/test/helper.rb index 03eb7b2c62f2..ebfc819e77be 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -337,22 +337,20 @@ def random_table_hash dataset, id = nil, name = nil, description = nil, project_ { "name" => "name", "type" => "STRING", - "mode" => "NULLABLE" + "mode" => "REQUIRED" }, { "name" => "age", - "type" => "INTEGER", - "mode" => "NULLABLE" + "type" => "INTEGER" }, { "name" => "score", "type" => "FLOAT", - "mode" => "NULLABLE" + "description" => "A score from 0.0 to 10.0" }, { "name" => "active", - "type" => "BOOLEAN", - "mode" => "NULLABLE" + "type" => "BOOLEAN" } ] },