Import refactored library. See MIGRATING.MD for details

This commit is contained in:
Steven Bazyl committed 2015-06-23 15:15:14 -07:00
1 parent 9b7809174d
commit f584a2cec8
373 files changed
+255715 -474

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
---
"/test:v1/TestAnotherThing": another_thing
+8
View File
@@ -0,0 +1,8 @@
{ "access_token":"access_token_123456789",
"authorization_uri":"https://accounts.google.com/o/oauth2/auth",
"client_id":"123456789p.apps.googleusercontent.com",
"client_secret":"very_secret",
"expires_in":3600,
"refresh_token":"refresh_token_12345679",
"token_credential_uri":"https://accounts.google.com/o/oauth2/token",
"issued_at":1386053761}
+1
View File
@@ -0,0 +1 @@
{"installed":{"auth_uri":"https://accounts.google.com/o/oauth2/auth","client_secret":"i8YaXdGgiQ4_KrTVNGsB7QP1","token_uri":"https://accounts.google.com/o/oauth2/token","client_email":"","client_x509_cert_url":"","client_id":"898243283568.apps.googleusercontent.com","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs"}}
+1
View File
@@ -0,0 +1 @@
test
+1
View File
@@ -0,0 +1 @@
Hello world
+440
View File
@@ -0,0 +1,440 @@
{
"kind": "discovery#describeItem",
"name": "test",
"version": "v1",
"id": "test:v1",
"description": "Discovery doc API used for testing the code generator",
"basePath": "/test/",
"rootUrl": "https://www.googleapis.com/",
"servicePath": "test/v1/",
"rpcPath": "/rpc",
"auth": {
"oauth2": {
"scopes": {
"https://www.googleapis.com/auth/test": {
"description": "View and manage things"
},
"https://www.googleapis.com/auth/test.readonly": {
"description": "View things"
}
}
}
},
"parameters": {
"alt": {
"type": "string",
"description": "Data format for the response.",
"default": "json",
"enum": [
"json"
],
"enumDescriptions": [
"Responses with Content-Type of application/json"
],
"location": "query"
},
"fields": {
"type": "string",
"description": "Selector specifying which fields to include in a partial response.",
"location": "query"
},
"key": {
"type": "string",
"description": "API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.",
"location": "query"
},
"oauth_token": {
"type": "string",
"description": "OAuth 2.0 token for the current user.",
"location": "query"
},
"prettyPrint": {
"type": "boolean",
"description": "Returns response with indentations and line breaks.",
"default": "true",
"location": "query"
},
"quotaUser": {
"type": "string",
"description": "Available to use for quota purposes for server-side applications. Can be any arbitrary string assigned to a user, but should not exceed 40 characters. Overrides userIp if both are provided.",
"location": "query"
},
"userIp": {
"type": "string",
"description": "IP address of the site where the request originates. Use this if you want to enforce per-user limits.",
"location": "query"
}
},
"schemas": {
"Thing": {
"id": "Thing",
"type": "object",
"properties": {
"id": {
"type": "string"
},
"etag": {
"type": "string"
},
"kind": {
"type": "string",
"default": "test#thing"
},
"name": {
"type": "string"
},
"enabled": {
"type": "boolean"
},
"hat": {
"$ref": "Hat"
},
"properties": {
"$ref": "HashLikeThing"
},
"photo": {
"type": "object",
"properties": {
"filename": {
"type": "string"
},
"hash": {
"type": "string"
},
"hashAlgorithm": {
"type": "string"
},
"size": {
"type": "integer"
},
"type": {
"type": "string"
}
}
}
}
},
"Hat": {
"type": "object",
"variant": {
"discriminant": "type",
"map": [
{
"type_value": "topHat",
"$ref": "TopHat"
},
{
"type_value": "baseballHat",
"$ref": "BaseballHat"
}
]
}
},
"TopHat": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"height": {
"type": "number"
}
}
},
"BaseballHat": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"color": {
"type": "string"
}
}
},
"HashLikeThing": {
"id": "HashLikeThing",
"type": "object",
"additionalProperties": {
"type": "string",
"description": "A mapping from export format to URL"
}
},
"TestThing": {
"id": "TestThing",
"type": "object",
"properties" :{}
},
"TestAnotherThing": {
"id": "TestAnotherThing",
"type": "object",
"properties" :{}
},
"ThingList": {
"id": "ThingList",
"type": "object",
"properties": {
"items": {
"type": "array",
"description": "The actual list of files.",
"items": {
"$ref": "Thing"
}
},
"kind": {
"type": "string",
"default": "test#thinglist"
}
}
},
"QueryResults": {
"id": "QueryResults",
"type": "object",
"properties": {
"rows": {
"$ref": "Rows"
}
}
},
"Rows": {
"id": "QueryResults",
"type": "array",
"items": {
"type": "object",
"properties": {
"value": {
"type": "string"
}
}
}
}
},
"methods": {
"query": {
"path": "query",
"id": "test.query",
"httpMethod": "GET",
"response": {
"$ref": "QueryResults"
//"$ref": "Rows" TODO: Support naked collections as a return value
},
"parameters": {
"s": {
"type": "string",
"location": "query",
"required": false,
"repeated": false
},
"i": {
"type": "integer",
"location": "query",
"required": false,
"repeated": false,
"minimum": "0",
"maximum": "4294967295",
"default": "20"
},
"n": {
"type": "number",
"location": "query",
"required": false,
"repeated": false
},
"b": {
"type": "boolean",
"location": "query",
"required": false,
"repeated": false
},
"a": {
"type": "any",
"location": "query",
"required": false,
"repeated": false
},
"e": {
"type": "string",
"location": "query",
"required": false,
"repeated": false,
"enum": [
"foo",
"bar"
]
},
"er": {
"type": "string",
"location": "query",
"required": false,
"repeated": true,
"enum": [
"one",
"two",
"three"
]
},
"sr": {
"type": "string",
"location": "query",
"required": false,
"repeated": true,
"pattern": "[a-z]+"
},
"do": {
"type": "string",
"location": "query",
"required": false
}
}
}
},
"resources": {
"things": {
"resources": {
"subthings": {
"methods": {
"list": {
"path": "things",
"id": "test.things.subthings.list",
"httpMethod": "GET",
"parameters": {
"max-results": {
"type": "number",
"location": "query",
"required": false
}
},
"response": {
"$ref": "ThingList"
}
}
}
}
},
"methods": {
"list": {
"path": "things",
"id": "test.things.list",
"httpMethod": "GET",
"parameters": {
"max-results": {
"type": "number",
"location": "query",
"required": false
}
},
"response": {
"$ref": "ThingList"
}
},
"delete": {
"path": "things/{id}",
"id": "test.things.delete",
"httpMethod": "DELETE",
"description": "Delete things",
"parameters": {
"id": {
"location": "path",
"required": true,
"description": "ID of the thing to delete",
"type": "string"
}
},
"parameterOrder": [
"id"
]
},
"get": {
"path": "things/{id}",
"id": "test.things.get",
"httpMethod": "GET",
"description": "Get things",
"supportsMediaDownload": true,
"parameters": {
"id": {
"location": "path",
"required": true,
"description": "ID of the thing to get",
"type": "string"
}
},
"supportsMediaDownload": true,
"parameterOrder": [
"id"
],
"response": {
"$ref": "Thing"
}
},
"create": {
"path": "things",
"id": "test.things.create",
"httpMethod": "POST",
"description": "Create things",
"request": {
"$ref": "Thing"
},
"response": {
"$ref": "Thing"
},
"supportsMediaUpload": true,
"mediaUpload": {
"accept": [
"*/*"
],
"maxSize": "1KB",
"protocols": {
"simple": {
"multipart": true,
"path": "upload/things/{id}"
},
"resumable": {
"multipart": true,
"path": "/resumable/upload/things/{id}"
}
}
}
},
"update": {
"path": "things/{id}",
"id": "test.things.update",
"httpMethod": "PUT",
"description": "Update things",
"parameters": {
"id": {
"location": "path",
"description": "ID of the thing to update",
"type": "string"
}
},
"parameterOrder": [
"id"
],
"request": {
"$ref": "Thing"
},
"response": {
"$ref": "Thing"
},
"supportsMediaUpload": true,
"mediaUpload": {
"accept": [
"*/*"
],
"maxSize": "1KB",
"protocols": {
"simple": {
"multipart": true,
"path": "upload/things/{id}"
},
"resumable": {
"multipart": true,
"path": "/resumable/upload/things/{id}"
}
}
}
}
}
}
}
}
+120
View File
@@ -0,0 +1,120 @@
require 'spec_helper'
require 'google/api_client/auth/storage'
describe Google::APIClient::Storage do
let(:root_path) { File.expand_path(File.join(__FILE__, '..', '..', '..')) }
let(:json_file) { File.expand_path(File.join(root_path, 'fixtures', 'files', 'auth_stored_credentials.json')) }
let(:store) { double }
let(:client_stub) { double }
subject { Google::APIClient::Storage.new(store) }
describe 'authorize' do
it 'should authorize' do
expect(subject).to respond_to(:authorization)
expect(subject.store).to be == store
end
end
describe 'authorize' do
describe 'with credentials' do
it 'should initialize a new OAuth Client' do
expect(subject).to receive(:load_credentials).and_return({:first => 'a dummy'})
expect(client_stub).to receive(:issued_at=)
expect(client_stub).to receive(:expired?).and_return(false)
expect(Signet::OAuth2::Client).to receive(:new).and_return(client_stub)
expect(subject).not_to receive(:refresh_authorization)
subject.authorize
end
it 'should refresh authorization' do
expect(subject).to receive(:load_credentials).and_return({:first => 'a dummy'})
expect(client_stub).to receive(:issued_at=)
expect(client_stub).to receive(:expired?).and_return(true)
expect(Signet::OAuth2::Client).to receive(:new).and_return(client_stub)
expect(subject).to receive(:refresh_authorization)
auth = subject.authorize
expect(auth).to be == subject.authorization
expect(auth).not_to be_nil
end
end
describe 'without credentials' do
it 'should return nil' do
expect(subject.authorization).to be_nil
expect(subject).to receive(:load_credentials).and_return({})
expect(subject.authorize).to be_nil
expect(subject.authorization).to be_nil
end
end
end
describe 'write_credentials' do
it 'should call store to write credentials' do
authorization_stub = double
expect(authorization_stub).to receive(:refresh_token).and_return(true)
expect(subject).to receive(:credentials_hash)
expect(subject.store).to receive(:write_credentials)
subject.write_credentials(authorization_stub)
expect(subject.authorization).to be == authorization_stub
end
it 'should not call store to write credentials' do
expect(subject).not_to receive(:credentials_hash)
expect(subject.store).not_to receive(:write_credentials)
expect {
subject.write_credentials()
}.not_to raise_error
end
it 'should not call store to write credentials' do
expect(subject).not_to receive(:credentials_hash)
expect(subject.store).not_to receive(:write_credentials)
expect {
subject.write_credentials('something')
}.not_to raise_error
end
end
describe 'refresh_authorization' do
it 'should call refresh and write credentials' do
expect(subject).to receive(:write_credentials)
authorization_stub = double
expect(subject).to receive(:authorization).and_return(authorization_stub)
expect(authorization_stub).to receive(:refresh!).and_return(true)
subject.refresh_authorization
end
end
describe 'load_credentials' do
it 'should call store to load credentials' do
expect(subject.store).to receive(:load_credentials)
subject.send(:load_credentials)
end
end
describe 'credentials_hash' do
it 'should return an hash' do
authorization_stub = double
expect(authorization_stub).to receive(:access_token)
expect(authorization_stub).to receive(:client_id)
expect(authorization_stub).to receive(:client_secret)
expect(authorization_stub).to receive(:expires_in)
expect(authorization_stub).to receive(:refresh_token)
expect(authorization_stub).to receive(:issued_at).and_return('100')
allow(subject).to receive(:authorization).and_return(authorization_stub)
credentials = subject.send(:credentials_hash)
expect(credentials).to include(:access_token)
expect(credentials).to include(:authorization_uri)
expect(credentials).to include(:client_id)
expect(credentials).to include(:client_secret)
expect(credentials).to include(:expires_in)
expect(credentials).to include(:refresh_token)
expect(credentials).to include(:token_credential_uri)
expect(credentials).to include(:issued_at)
end
end
end
@@ -0,0 +1,39 @@
require 'spec_helper'
require 'google/api_client/auth/storages/file_store'
describe Google::APIClient::FileStore do
let(:root_path) { File.expand_path(File.join(__FILE__, '..','..','..', '..','..')) }
let(:json_file) { File.expand_path(File.join(root_path, 'fixtures', 'files', 'auth_stored_credentials.json')) }
let(:credentials_hash) {{
"access_token"=>"my_access_token",
"authorization_uri"=>"https://accounts.google.com/o/oauth2/auth",
"client_id"=>"123456_test_client_id@.apps.googleusercontent.com",
"client_secret"=>"123456_client_secret",
"expires_in"=>3600,
"refresh_token"=>"my_refresh_token",
"token_credential_uri"=>"https://accounts.google.com/o/oauth2/token",
"issued_at"=>1384440275
}}
subject{Google::APIClient::FileStore.new('a file path')}
it 'should have a path' do
expect(subject.path).to be == 'a file path'
subject.path = 'an other file path'
expect(subject.path).to be == 'an other file path'
end
it 'should load credentials' do
subject.path = json_file
credentials = subject.load_credentials
expect(credentials).to include('access_token', 'authorization_uri', 'refresh_token')
end
it 'should write credentials' do
io_stub = StringIO.new
expect(subject).to receive(:open).and_return(io_stub)
subject.write_credentials(credentials_hash)
end
end
@@ -0,0 +1,68 @@
require 'spec_helper'
require 'google/api_client/auth/storages/redis_store'
describe Google::APIClient::RedisStore do
let(:root_path) { File.expand_path(File.join(__FILE__, '..', '..', '..', '..', '..')) }
let(:json_file) { File.expand_path(File.join(root_path, 'fixtures', 'files', 'auth_stored_credentials.json')) }
let(:redis) {double}
let(:credentials_hash) { {
"access_token" => "my_access_token",
"authorization_uri" => "https://accounts.google.com/o/oauth2/auth",
"client_id" => "123456_test_client_id@.apps.googleusercontent.com",
"client_secret" => "123456_client_secret",
"expires_in" => 3600,
"refresh_token" => "my_refresh_token",
"token_credential_uri" => "https://accounts.google.com/o/oauth2/token",
"issued_at" => 1384440275
} }
subject { Google::APIClient::RedisStore.new('a redis instance') }
it 'should have a redis instance' do
expect(subject.redis).to be == 'a redis instance'
subject.redis = 'an other redis instance'
expect(subject.redis).to be == 'an other redis instance'
end
describe 'load_credentials' do
it 'should load credentials' do
subject.redis= redis
expect(redis).to receive(:get).and_return(credentials_hash.to_json)
expect(subject.load_credentials).to be == credentials_hash
end
it 'should return nil' do
subject.redis= redis
expect(redis).to receive(:get).and_return(nil)
expect(subject.load_credentials).to be_nil
end
end
describe 'redis_credentials_key' do
context 'without given key' do
it 'should return default key' do
expect(subject.redis_credentials_key).to be == "google_api_credentials"
end
end
context 'with given key' do
let(:redis_store) { Google::APIClient::RedisStore.new('a redis instance', 'another_google_api_credentials') }
it 'should use given key' do
expect(redis_store.redis_credentials_key).to be == "another_google_api_credentials"
end
end
end
describe 'write credentials' do
it 'should write credentials' do
subject.redis= redis
expect(redis).to receive(:set).and_return('ok')
expect(subject.write_credentials(credentials_hash)).to be_truthy
end
end
end
@@ -0,0 +1,53 @@
# encoding:utf-8
# Copyright 2013 Google Inc.
#
# 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 'spec_helper'
require 'google/api_client/client_secrets'
FIXTURES_PATH = File.expand_path('../../../fixtures', __FILE__)
RSpec.describe Google::APIClient::ClientSecrets do
context 'with JSON file' do
let(:file) { File.join(FIXTURES_PATH, 'files', 'client_secrets.json') }
subject(:secrets) { Google::APIClient::ClientSecrets.load(file)}
it 'should load the correct client ID' do
expect(secrets.client_id).to be == '898243283568.apps.googleusercontent.com'
end
it 'should load the correct client secret' do
expect(secrets.client_secret).to be == 'i8YaXdGgiQ4_KrTVNGsB7QP1'
end
context 'serialzed to hash' do
subject(:hash) { secrets.to_hash }
it 'should contain the flow as the first key' do
expect(hash).to have_key "installed"
end
it 'should contain the client ID' do
expect(hash["installed"]["client_id"]).to be == '898243283568.apps.googleusercontent.com'
end
it 'should contain the client secret' do
expect(hash["installed"]["client_secret"]).to be == 'i8YaXdGgiQ4_KrTVNGsB7QP1'
end
end
end
end
+170
View File
@@ -0,0 +1,170 @@
# Copyright 2015 Google Inc.
#
# 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 'spec_helper'
require 'google/apis/core/api_command'
require 'google/apis/core/json_representation'
require 'hurley/test'
RSpec.describe Google::Apis::Core::HttpCommand do
include TestHelpers
include_context 'HTTP client'
let(:model_class) do
Class.new do
attr_accessor :value
end
end
let(:representer_class) do
Class.new(Google::Apis::Core::JsonRepresentation) do
property :value
end
end
context('with a request body') do
let(:command) do
request = model_class.new
request.value = 'hello'
command = Google::Apis::Core::ApiCommand.new(:post, 'https://www.googleapis.com/zoo/animals')
command.request_representation = representer_class
command.request_object = request
command
end
before(:example) do
stub_request(:post, 'https://www.googleapis.com/zoo/animals')
.to_return(headers: { 'Content-Type' => 'application/json' }, body: %({}))
end
it 'should serialize the request object' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals').with do |req|
be_json_eql(%({"value":"hello"})).matches?(req.body)
end).to have_been_made
end
end
context('with a JSON response') do
let(:command) do
command = Google::Apis::Core::ApiCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
command.response_representation = representer_class
command.response_class = model_class
command
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(headers: { 'Content-Type' => 'application/json' }, body: %({"value" : "hello"}))
end
it 'should return a model instance' do
result = command.execute(client)
expect(result).to be_kind_of(model_class)
end
it 'should return a populated object' do
result = command.execute(client)
expect(result.value).to eql 'hello'
end
end
context('with an invalid content-type response') do
let(:command) do
command = Google::Apis::Core::ApiCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
command.response_representation = representer_class
command.response_class = model_class
command
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(headers: { 'Content-Type' => 'text/plain' }, body: %(Ignore me))
end
it 'should return nil' do
result = command.execute(client)
expect(result).to be_nil
end
end
context('with a field parameter') do
let(:command) do
command = Google::Apis::Core::ApiCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
command.query['fields'] = ':items(:id, :long_name)'
command
end
before(:example) do
stub_request(:get, /.*/)
.to_return(headers: { 'Content-Type' => 'application/json' }, body: %({}))
end
it 'should normalize fields params' do
command.execute(client)
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals')
.with(query: { 'fields' => 'items(id, longName)' })) .to have_been_made
end
end
context('with a rate limit response') do
let(:command) do
Google::Apis::Core::ApiCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
json = <<EOF
{
"error": {
"errors": [
{
"domain": "global",
"reason": "rateLimitExceeded",
"message": "Rate limit exceeded"
}
],
"code": 403,
"message": "Rate limit exceeded"
}
}
EOF
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [403, 'Rate Limit Exceeded'], headers: { 'Content-Type' => 'application/json' }, body: json)
.to_return(headers: { 'Content-Type' => 'application/json' }, body: %({}))
end
it 'should retry' do
command.execute(client)
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals')).to have_been_made.times(2)
end
end
context('with an empty error body') do
let(:command) do
Google::Apis::Core::ApiCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
json = %({})
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [403, 'Rate Limit Exceeded'], headers: { 'Content-Type' => 'application/json' }, body: json)
end
it 'should raise client error' do
expect { command.execute(client) }.to raise_error(Google::Apis::ClientError)
end
end
end
+128
View File
@@ -0,0 +1,128 @@
# Copyright 2015 Google Inc.
#
# 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 'spec_helper'
require 'google/apis/core/batch'
require 'google/apis/core/json_representation'
require 'hurley/test'
RSpec.describe Google::Apis::Core::BatchCommand do
include TestHelpers
include_context 'HTTP client'
let(:command) do
command = Google::Apis::Core::BatchCommand.new(:post, 'https://www.googleapis.com/batch')
end
let(:get_command) { Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals/1') }
let(:post_with_string_command) do
command = Google::Apis::Core::HttpCommand.new(:post, 'https://www.googleapis.com/zoo/animals/2')
command.body = 'Hello world'
command.header[:content_type] = 'text/plain'
command
end
let(:post_with_io_command) do
command = Google::Apis::Core::HttpCommand.new(:post, 'https://www.googleapis.com/zoo/animals/3')
command.body = StringIO.new('Goodbye!')
command.header[:content_type] = 'text/plain'
command
end
before(:example) do
response = <<EOF
--batch123
Content-Type: application/http
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Hello
--batch123
Content-Type: application/http
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
world
--batch123
Content-Type: application/http
HTTP/1.1 500 Server Error
Content-Type: text/plain; charset=UTF-8
Error!
--batch123--
EOF
stub_request(:post, 'https://www.googleapis.com/batch')
.to_return(headers: { 'Content-Type' => 'multipart/mixed; boundary=batch123' }, body: response)
end
it 'should send content' do
b = ->(_res, _err) {}
command.add(get_command, &b)
command.add(post_with_string_command, &b)
command.add(post_with_io_command, &b)
command.execute(client)
expected_body = <<EOF.gsub(/\n/, "\r\n")
--RubyApiBatchRequest
Content-Length: 58
Content-Type: application/http
Content-Transfer-Encoding: binary
GET /zoo/animals/1? HTTP/1.1
Host: www.googleapis.com
--RubyApiBatchRequest
Content-Length: 96
Content-Type: application/http
Content-Transfer-Encoding: binary
POST /zoo/animals/2? HTTP/1.1
Content-Type: text/plain
Host: www.googleapis.com
Hello world
--RubyApiBatchRequest
Content-Length: 93
Content-Type: application/http
Content-Transfer-Encoding: binary
POST /zoo/animals/3? HTTP/1.1
Content-Type: text/plain
Host: www.googleapis.com
Goodbye!
--RubyApiBatchRequest--
EOF
expect(a_request(:post, 'https://www.googleapis.com/batch').with(body: expected_body)).to have_been_made
end
it 'should send decode responses' do
expect do |b|
command.add(get_command, &b)
command.add(post_with_string_command, &b)
command.add(post_with_io_command, &b)
command.execute(client)
end.to yield_successive_args(['Hello', nil], ['world', nil], [nil, an_instance_of(Google::Apis::ServerError)])
end
it 'should raise error if batch is empty' do
expect { command.execute(client) }.to raise_error(Google::Apis::BatchError)
end
end
+90
View File
@@ -0,0 +1,90 @@
# Copyright 2015 Google Inc.
#
# 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 'spec_helper'
require 'google/apis/core/download'
require 'google/apis/core/json_representation'
require 'hurley/test'
require 'tempfile'
require 'tmpdir'
RSpec.describe Google::Apis::Core::DownloadCommand do
include TestHelpers
include_context 'HTTP client'
let(:command) do
command = Google::Apis::Core::DownloadCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
command.download_dest = dest
command
end
shared_examples 'should download' do
context 'with successful response' do
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(headers: { 'Content-Type' => 'application/json' }, body: %(Hello world))
end
it 'should not include a range header' do
command.execute(client)
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals')
.with { |req| !req.headers.key?('Range') }
).to have_been_made
end
it 'should receive content' do
expect(received).to eql 'Hello world'
end
end
context 'with disconnects' do
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(body: ['Hello ', Timeout::Error])
.to_return(body: 'world')
end
it 'should receive entire content' do
expect(received).to eql('Hello world')
end
end
end
context 'with default destination' do
let(:dest) { nil }
let(:received) { command.execute(client).string }
include_examples 'should download'
end
context 'with IO destination' do
let(:dest) { Tempfile.new('test') }
let(:received) do
command.execute(client)
dest.rewind
dest.read
end
include_examples 'should download'
end
context 'with filename destination' do
let(:dest) { File.join(Dir.mktmpdir, 'test.txt') }
let(:received) do
command.execute(client)
File.read(dest)
end
include_examples 'should download'
end
end
+60
View File
@@ -0,0 +1,60 @@
# Copyright 2015 Google Inc.
#
# 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 'spec_helper'
require 'google/apis/core/hashable'
RSpec.describe Google::Apis::Core::Hashable do
let(:child_class) do
Class.new do
include Google::Apis::Core::Hashable
attr_accessor :value
end
end
let(:model_class) do
Class.new do
include Google::Apis::Core::Hashable
attr_accessor :value
attr_accessor :value2
attr_accessor :children
end
end
let(:model) do
obj = model_class.new
obj.value = 'hello'
obj.value2 = {
a: 'a'
}
child = child_class.new
child.value = 'goodbye'
obj.children = [child]
obj
end
let(:hash) { model.to_h }
it 'should serialize attributes' do
expect(hash).to include(value: 'hello')
end
it 'should serialize collections' do
expect(hash).to include(children: [{ value: 'goodbye' }])
end
it 'should serialize hashes' do
expect(hash[:value2]).to include(a: 'a')
end
end
+238
View File
@@ -0,0 +1,238 @@
# Copyright 2015 Google Inc.
#
# 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 'spec_helper'
require 'google/apis/core/http_command'
require 'hurley/test'
RSpec.describe Google::Apis::Core::HttpCommand do
include TestHelpers
include_context 'HTTP client'
context('with credentials') do
let(:command) do
command = Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
command.options.authorization = authorization
command
end
context('that are refreshable') do
let(:authorization) do
calls = 0
auth = object_double(Signet::OAuth2::Client.new)
allow(auth).to receive(:apply!) do |header|
header['Authorization'] = sprintf('Bearer a_token_value_%d', calls)
calls += 1
end
auth
end
it 'should send credentials' do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_return(body: %(Hello world))
result = command.execute(client)
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['Authorization'] == 'Bearer a_token_value_0' }).to have_been_made
end
context('with authorizaton error') do
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [401, 'Unauthorized'])
.to_return(body: %(Hello world))
end
it 'should refresh if auth error received' do
result = command.execute(client)
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['Authorization'] == 'Bearer a_token_value_1' }).to have_been_made
end
it 'should ignore retry count' do
command.options.retries = 0
result = command.execute(client)
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['Authorization'] == 'Bearer a_token_value_1' }).to have_been_made
end
end
end
context('that are bare tokens`') do
let(:authorization) { 'a_token_value' }
it 'should send credentials' do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_return(body: %(Hello world))
result = command.execute(client)
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals')
.with { |req| expect(req.headers['Authorization']).to eql 'Bearer a_token_value' }).to have_been_made
end
it 'should send not refresh' do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_return(status: [401, 'Unauthorized'])
expect { command.execute(client) }.to raise_error(Google::Apis::AuthorizationError)
end
end
end
context('with a successful response') do
let(:command) do
Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_return(body: %(Hello world))
end
it 'should return the response body if block not present' do
result = command.execute(client)
expect(result).to eql 'Hello world'
end
it 'should call block if present' do
expect { |b| command.execute(client, &b) }.to yield_with_args('Hello world', nil)
end
end
context('with server errors') do
let(:command) do
Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [500, 'Server error']).times(2)
.to_return(body: %(Hello world))
end
it 'should return the response body' do
result = command.execute(client)
expect(result).to eql 'Hello world'
end
it 'should raise error if retries exceeded' do
command.options.retries = 1
expect { command.execute(client) }.to raise_error(Google::Apis::ServerError)
end
context('with callbacks') do
it 'should return the response body after retries' do
expect { |b| command.execute(client, &b) }.to yield_successive_args(
[nil, an_instance_of(Google::Apis::ServerError)],
[nil, an_instance_of(Google::Apis::ServerError)],
['Hello world', nil])
end
end
end
context('with options') do
let(:command) do
command = Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
command.options.header = { 'X-Foo' => 'bar' }
command
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(body: %(Hello world))
end
it 'should send user headers' do
result = command.execute(client)
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['X-Foo'] == 'bar' }).to have_been_made
end
end
context('with redirects') do
let(:command) do
Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [302, 'Redirect'], headers: { 'Location' => 'https://zoo.googleapis.com/animals' })
stub_request(:get, 'https://zoo.googleapis.com/animals')
.to_return(body: %(Hello world))
end
it 'should return the response body' do
result = command.execute(client)
expect(result).to eql 'Hello world'
end
end
context('with too many redirects') do
let(:command) do
Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [302, 'Redirect'], headers: { 'Location' => 'https://www.googleapis.com/zoo/animals' }).times(6)
end
it 'should raise error if retries exceeded' do
expect { command.execute(client) }.to raise_error(Google::Apis::RedirectError)
end
end
context('with no server response') do
let(:command) do
Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_timeout
end
it 'should raise transmission error' do
expect { command.execute(client) }.to raise_error(Google::Apis::TransmissionError)
end
end
context('with invalid status code') do
let(:command) do
Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [0, 'Wat!?'])
end
it 'should raise transmission error' do
expect { command.execute(client) }.to raise_error(Google::Apis::TransmissionError)
end
end
context('with client errors') do
let(:command) do
Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [400, 'Invalid request'])
end
it 'should raise error without retry' do
command.options.retries = 1
expect { command.execute(client) }.to raise_error(Google::Apis::ClientError)
end
it 'should call block if present' do
expect { |b| command.execute(client, &b) }.to yield_with_args(nil, an_instance_of(Google::Apis::ClientError))
end
end
end
@@ -0,0 +1,192 @@
# Copyright 2015 Google Inc.
#
# 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 'spec_helper'
require 'google/apis/core/json_representation'
RSpec.describe Google::Apis::Core::JsonRepresentation do
let(:child_class) do
Class.new do
attr_accessor :value
end
end
let(:model_class) do
Class.new do
attr_accessor :unset_value
attr_accessor :nil_value
attr_accessor :numeric_value
attr_accessor :string_value
attr_accessor :boolean_value
attr_accessor :date_value
attr_accessor :bytes_value
attr_accessor :items
attr_accessor :child
attr_accessor :children
end
end
let(:representer_class) do
klass = child_class
Class.new(Google::Apis::Core::JsonRepresentation) do
property :unset_value, as: 'unsetValue'
property :nil_value, as: 'nilValue'
property :numeric_value, as: 'numericValue'
property :string_value, as: 'stringValue'
property :boolean_value, as: 'booleanValue'
property :date_value, as: 'dateValue', type: DateTime
property :bytes_value, as: 'bytesValue', base64: true
property :items
property :child, class: klass do
property :value
end
collection :children, class: klass do
property :value
end
end
end
shared_examples 'it serializes' do
it 'does not serialize unset values' do
expect(json).not_to have_json_path('unsetValue')
end
it 'serializes explicit nil values' do
expect(json).to be_json_eql(%(null)).at_path('nilValue')
end
it 'serializes numeric values' do
expect(json).to be_json_eql(%(123)).at_path('numericValue')
end
it 'serializes string values' do
expect(json).to be_json_eql(%("test")).at_path('stringValue')
end
it 'serializes boolean values' do
expect(json).to be_json_eql(%(true)).at_path('booleanValue')
end
it 'serializes date values' do
expect(json).to be_json_eql(%("2015-05-01T12:00:00+00:00")).at_path('dateValue')
end
it 'serializes byte values to base64' do
expect(json).to be_json_eql(%("SGVsbG8gd29ybGQ=")).at_path('bytesValue')
end
it 'serializes basic collections' do
expect(json).to be_json_eql(%([1,2,3])).at_path('items')
end
it 'serializes nested objects' do
expect(json).to be_json_eql(%({"value" : "child"})).at_path('child')
end
it 'serializes object collections' do
expect(json).to be_json_eql(%([{"value" : "child"}])).at_path('children')
end
end
context 'with model object' do
let(:json) { representer_class.new(model).to_json(skip_undefined: true) }
let(:model) do
model = model_class.new
model.nil_value = nil
model.numeric_value = 123
model.string_value = 'test'
model.date_value = DateTime.new(2015, 5, 1, 12)
model.boolean_value = true
model.bytes_value = 'Hello world'
model.items = [1, 2, 3]
model.child = child_class.new
model.child.value = 'child'
model.children = [model.child]
model
end
include_examples 'it serializes'
end
context 'with hash' do
let(:json) { representer_class.new(model).to_json(skip_undefined: true) }
let(:model) do
{
nil_value: nil,
string_value: 'test',
numeric_value: 123,
date_value: DateTime.new(2015, 5, 1, 12),
boolean_value: true,
bytes_value: 'Hello world',
items: [1, 2, 3],
child: {
value: 'child'
},
children: [{ value: 'child' }]
}
end
include_examples 'it serializes'
end
context 'when de-serializing' do
let(:model) { representer_class.new(model_class.new).from_json(json) }
let(:json) do
json = <<EOF
{ "stringValue": "test",
"nilValue": null,
"booleanValue": true,
"numericValue": 123,
"dateValue": "2015-05-01T12:00:00+00:00",
"bytesValue": "SGVsbG8gd29ybGQ=",
"items": [1,2,3],
"child": {"value" : "hello"},
"children": [{"value" : "hello"}]
}
EOF
end
it 'deserializes string values' do
expect(model.string_value).to eql 'test'
end
it 'deserializes null values' do
expect(model.nil_value).to be_nil
end
it 'deserializes numeric values' do
expect(model.numeric_value).to eql 123
end
it 'deserializes boolean values' do
expect(model.boolean_value).to be_truthy
end
it 'deserializes date values' do
expect(model.date_value).to eql DateTime.new(2015, 5, 1, 12)
end
it 'deserializes basic collections' do
expect(model.items).to contain_exactly(1, 2, 3)
end
it 'deserializes nested objects' do
expect(model.child.value).to eql 'hello'
end
it 'serializes object collections' do
expect(model.children[0].value).to eql 'hello'
end
end
end
+247
View File
@@ -0,0 +1,247 @@
# Copyright 2015 Google Inc.
#
# 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 'spec_helper'
require 'google/apis/options'
require 'google/apis/core/base_service'
require 'google/apis/core/json_representation'
require 'hurley/test'
RSpec.describe Google::Apis::Core::BaseService do
include TestHelpers
let(:service) { Google::Apis::Core::BaseService.new('https://www.googleapis.com/', '') }
before do
Google::Apis::ClientOptions.default.application_name = 'test'
Google::Apis::ClientOptions.default.application_version = '1.0'
end
it 'should inherit default options' do
expect(service.client_options.application_name).to eql 'test'
end
it 'should include application in user agent' do
agent = service.send(:user_agent)
expect(agent).to match /^test\/1.0/
end
it 'should inherit authorization' do
Google::Apis::RequestOptions.default.authorization = 'a token'
expect(service.authorization).to eql 'a token'
end
it 'should allow overiding authorization' do
Google::Apis::RequestOptions.default.authorization = 'a token'
service.authorization = 'another token'
expect(service.authorization).to eql 'another token'
end
shared_examples 'with options' do
it 'should inherit service options' do
service.request_options.retries = 2
expect(command.options.retries).to eql 2
end
it 'should include per-request options' do
expect(command.options.authorization).to eql 'foo'
end
it 'should allow per-request options to override service options' do
service.request_options.authorization = 'bar'
expect(command.options.authorization).to eql 'foo'
end
end
context 'when making raw http requests' do
context 'with :get methods' do
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_return(body: 'hello world')
end
it 'should return body for get' do
response = service.http(:get, 'https://www.googleapis.com/zoo/animals')
expect(response).to eq 'hello world'
end
it 'should return allow downloads' do
response = service.http(:get, 'https://www.googleapis.com/zoo/animals', download_dest: StringIO.new)
expect(response.string).to eq 'hello world'
end
end
context 'with :post methods' do
before(:example) do
stub_request(:post, 'https://www.googleapis.com/zoo/animals').to_return(body: '')
end
it 'should post body' do
service.http(:post, 'https://www.googleapis.com/zoo/animals', body: 'hello')
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with(body: 'hello')).to have_been_made
end
end
end
context 'when making simple commands' do
let(:command) { service.send(:make_simple_command, :get, 'zoo/animals', authorization: 'foo') }
it 'should return the correct command type' do
expect(command).to be_an_instance_of(Google::Apis::Core::ApiCommand)
end
it 'should build a correct URL' do
url = command.url.expand({}).to_s
expect(url).to eql 'https://www.googleapis.com/zoo/animals'
end
include_examples 'with options'
end
context 'when making download commands' do
let(:command) { service.send(:make_download_command, :get, 'zoo/animals', authorization: 'foo') }
it 'should return the correct command type' do
expect(command).to be_an_instance_of(Google::Apis::Core::DownloadCommand)
end
it 'should build a correct URL' do
url = command.url.expand({}).to_s
expect(url).to eql 'https://www.googleapis.com/zoo/animals'
end
it 'should include alt=media in params' do
expect(command.query).to include('alt' => 'media')
end
include_examples 'with options'
end
context 'when making upload commands' do
let(:command) { service.send(:make_upload_command, :post, 'zoo/animals', authorization: 'foo') }
it 'should return the correct command type' do
expect(command).to be_an_instance_of(Google::Apis::Core::ResumableUploadCommand)
end
it 'should build a correct URL' do
url = command.url.expand({}).to_s
expect(url).to eql 'https://www.googleapis.com/upload/zoo/animals'
end
include_examples 'with options'
end
context 'with batch' do
before(:example) do
response = <<EOF.gsub(/\n/, "\r\n")
--batch123
Content-Type: application/http
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Hello
--batch123--
EOF
stub_request(:post, 'https://www.googleapis.com/batch')
.to_return(headers: { 'Content-Type' => 'multipart/mixed; boundary=batch123' }, body: response)
end
it 'should add commands to a batch' do
expect do |b|
service.batch do |service|
command = service.send(:make_simple_command, :get, 'zoo/animals', {})
service.send(:execute_or_queue_command, command, &b)
end
end.to yield_with_args('Hello', nil)
end
it 'should disallow uploads in batch' do
expect do |b|
service.batch do |service|
command = service.send(:make_upload_command, :post, 'zoo/animals', {})
service.send(:execute_or_queue_command, command, &b)
end
end.to raise_error(Google::Apis::ClientError)
end
it 'should disallow downloads in batch' do
expect do |b|
service.batch do |service|
command = service.send(:make_download_command, :get, 'zoo/animals', {})
service.send(:execute_or_queue_command, command, &b)
end
end.to raise_error(Google::Apis::ClientError)
end
end
context 'with batch uploads' do
before(:example) do
response = <<EOF.gsub(/\n/, "\r\n")
--batch123
Content-Type: application/http
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Hello
--batch123--
EOF
stub_request(:put, 'https://www.googleapis.com/upload/')
.to_return(headers: { 'Content-Type' => 'multipart/mixed; boundary=batch123' }, body: response)
end
it 'should add upload to a batch' do
expect do |b|
service.batch_upload do |service|
command = service.send(:make_upload_command, :post, 'zoo/animals', {})
command.upload_source = StringIO.new('test')
command.upload_content_type = 'text/plain'
service.send(:execute_or_queue_command, command, &b)
end
end.to yield_with_args('Hello', nil)
end
it 'should use multipart upload' do
expect do |b|
service.batch_upload do |service|
command = service.send(:make_upload_command, :post, 'zoo/animals', {})
command.upload_source = StringIO.new('test')
command.upload_content_type = 'text/plain'
expect(command).to be_an_instance_of(Google::Apis::Core::MultipartUploadCommand)
service.send(:execute_or_queue_command, command, &b)
end
end.to yield_with_args('Hello', nil)
end
it 'should disallow downloads in batch' do
expect do |b|
service.batch_upload do |service|
command = service.send(:make_download_command, :get, 'zoo/animals', {})
service.send(:execute_or_queue_command, command, &b)
end
end.to raise_error(Google::Apis::ClientError)
end
it 'should disallow simple commands in batch' do
expect do |b|
service.batch_upload do |service|
command = service.send(:make_simple_command, :get, 'zoo/animals', {})
service.send(:execute_or_queue_command, command, &b)
end
end.to raise_error(Google::Apis::ClientError)
end
end
end
+238
View File
@@ -0,0 +1,238 @@
# Copyright 2015 Google Inc.
#
# 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 'spec_helper'
require 'google/apis/core/upload'
require 'google/apis/core/json_representation'
require 'hurley/test'
# TODO: JSON Response decoding
# TODO: Upload from IO
# TODO: Upload from file
RSpec.describe Google::Apis::Core::UploadIO do
let(:upload_io) { Google::Apis::Core::UploadIO.from_file(file) }
context 'with text file' do
let(:file) { File.join(FIXTURES_DIR, 'files', 'test.txt') }
it 'should infer content type from file' do
expect(upload_io.content_type).to eql('text/plain')
end
it 'should allow overriding the mime type' do
io = Google::Apis::Core::UploadIO.from_file(file, content_type: 'application/json')
expect(io.content_type).to eql('application/json')
end
end
context 'with unknown type' do
let(:file) { File.join(FIXTURES_DIR, 'files', 'test.blah') }
it 'should use the default mime type' do
expect(upload_io.content_type).to eql('application/octet-stream')
end
it 'should allow overriding the mime type' do
io = Google::Apis::Core::UploadIO.from_file(file, content_type: 'application/json')
expect(io.content_type).to eql('application/json')
end
end
end
RSpec.describe Google::Apis::Core::RawUploadCommand do
include TestHelpers
include_context 'HTTP client'
let(:command) do
command = Google::Apis::Core::RawUploadCommand.new(:post, 'https://www.googleapis.com/zoo/animals')
command.upload_source = file
command.upload_content_type = 'text/plain'
command
end
shared_examples 'should upload' do
before(:example) do
stub_request(:post, 'https://www.googleapis.com/zoo/animals').to_return(body: '')
end
it 'should send content' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with(body: "Hello world\n")).to have_been_made
end
it 'should send upload protocol' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['X-Goog-Upload-Protocol'] == 'raw' }).to have_been_made
end
end
context('with StringIO input') do
let(:file) { StringIO.new("Hello world\n") }
include_examples 'should upload'
end
context('with IO input') do
let(:file) { File.open(File.join(FIXTURES_DIR, 'files', 'test.txt'), 'r') }
include_examples 'should upload'
it 'should not close stream' do
expect(file.closed?).to be false
end
end
context('with file path input') do
let(:file) { File.join(FIXTURES_DIR, 'files', 'test.txt') }
include_examples 'should upload'
end
context('with invalid input') do
let(:file) { -> {} }
it 'should raise client error' do
expect { command.execute(client) }.to raise_error(Google::Apis::ClientError)
end
end
end
RSpec.describe Google::Apis::Core::MultipartUploadCommand do
include TestHelpers
include_context 'HTTP client'
let(:command) do
command = Google::Apis::Core::MultipartUploadCommand.new(:post, 'https://www.googleapis.com/zoo/animals')
command.upload_source = StringIO.new('Hello world')
command.upload_content_type = 'text/plain'
command.body = 'metadata'
command
end
before(:example) do
stub_request(:post, 'https://www.googleapis.com/zoo/animals').to_return(body: %(Hello world))
end
it 'should send content' do
expected_body = <<EOF.gsub(/\n/, "\r\n")
--RubyApiClientUpload
Content-Type: application/json
metadata
--RubyApiClientUpload
Content-Length: 11
Content-Type: text/plain
Content-Transfer-Encoding: binary
Hello world
--RubyApiClientUpload--
EOF
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with(body: expected_body)).to have_been_made
end
it 'should send upload protocol' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['X-Goog-Upload-Protocol'] == 'multipart' }).to have_been_made
end
end
RSpec.describe Google::Apis::Core::ResumableUploadCommand do
include TestHelpers
include_context 'HTTP client'
let(:command) do
command = Google::Apis::Core::ResumableUploadCommand.new(:post, 'https://www.googleapis.com/zoo/animals')
command.upload_source = StringIO.new('Hello world')
command.upload_content_type = 'text/plain'
command
end
context 'with uninterrupted upload' do
before(:example) do
stub_request(:post, 'https://www.googleapis.com/zoo/animals')
.to_return(headers: { 'X-Goog-Upload-Status' => 'active', 'X-Goog-Upload-URL' => 'https://www.googleapis.com/zoo/animals' })
.to_return(headers: { 'X-Goog-Upload-Status' => 'final' }, body: %(OK))
end
it 'should send upload protocol' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['X-Goog-Upload-Protocol'] == 'resumable' }).to have_been_made
end
it 'should send start command' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['X-Goog-Upload-Command'] == 'start' }).to have_been_made
end
it 'should send upload command' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['X-Goog-Upload-Command'].include?('upload') }).to have_been_made
end
it 'should send upload content' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with(body: 'Hello world')).to have_been_made
end
end
context 'with retriable error on start' do
before(:example) do
stub_request(:post, 'https://www.googleapis.com/zoo/animals')
.to_timeout
.to_return(headers: { 'X-Goog-Upload-Status' => 'active', 'X-Goog-Upload-URL' => 'https://www.googleapis.com/zoo/animals' })
.to_return(headers: { 'X-Goog-Upload-Status' => 'active', 'X-Goog-Upload-Size-Received' => '6' })
.to_return(headers: { 'X-Goog-Upload-Status' => 'final' }, body: %(OK))
end
it 'should retry start command and continue' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with(body: 'Hello world')).to have_been_made
end
end
context 'with interruption' do
before(:example) do
stub_request(:post, 'https://www.googleapis.com/zoo/animals')
.to_return(headers: { 'X-Goog-Upload-Status' => 'active', 'X-Goog-Upload-URL' => 'https://www.googleapis.com/zoo/animals' })
.to_return(status: [500, 'Server error'])
.to_return(headers: { 'X-Goog-Upload-Status' => 'active', 'X-Goog-Upload-Size-Received' => '6' })
.to_return(headers: { 'X-Goog-Upload-Status' => 'final' }, body: %(OK))
end
it 'should send remaining upload content after failure' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with(body: 'world')).to have_been_made
end
end
context 'with cancelled upload' do
before(:example) do
stub_request(:post, 'https://www.googleapis.com/zoo/animals')
.to_return(headers: { 'X-Goog-Upload-Status' => 'active', 'X-Goog-Upload-URL' => 'https://www.googleapis.com/zoo/animals' })
.to_return(status: [500, 'Server error'])
.to_return(headers: { 'X-Goog-Upload-Status' => 'cancelled' })
end
it 'should raise error' do
expect { command.execute(client) }.to raise_error Google::Apis::ClientError
end
end
end
+27
View File
@@ -0,0 +1,27 @@
# Copyright 2015 Google Inc.
#
# 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 'spec_helper'
RSpec.describe Google::Apis do
# Minimal test just to ensure no syntax errors in generated code
it 'should load all APIs' do
expect do
Dir.glob(File.join(ROOT_DIR, 'generated', 'google', 'apis', '*.rb')) do |file|
base = File.basename(file, '.rb')
require sprintf('google/apis/%s', base)
end
end.not_to raise_error
end
end
@@ -0,0 +1,272 @@
# Copyright 2015 Google Inc.
#
# 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 'spec_helper'
require 'google/apis/generator'
require 'tmpdir'
require 'fileutils'
# TODO - Naked arrays in method requests/responses
RSpec.describe Google::Apis::Generator do
include TestHelpers
context 'with test API' do
before(:context) do
generator = Google::Apis::Generator.new(api_names: File.join(FIXTURES_DIR, 'files', 'api_names.yaml'))
discovery = File.read(File.join(FIXTURES_DIR, 'files', 'test_api.json'))
generated_files = generator.render(discovery)
puts generator.dump_api_names
tempdir = Dir.mktmpdir
generated_files.each do |key, content|
path = File.join(tempdir, key)
FileUtils.mkdir_p(File.dirname(path))
File.open(path, 'w') do |f|
f.write(content)
end
end
$LOAD_PATH.unshift(tempdir)
require 'google/apis/test_v1'
end
let(:service) { Google::Apis::TestV1::TestService.new }
context 'with the generated service' do
it 'should set the base URL' do
expect(service.root_url.to_s).to eql('https://www.googleapis.com/')
end
it 'should define global methods from discovery' do
expect(service.method(:query)).to_not be_nil
end
it 'should define parameters on methods' do
parameters = service.method(:query).parameters.map { |(_k, v)| v }
expect(parameters).to include(:s, :i, :n, :b, :a, :e, :er, :sr)
end
it 'should modify parameter names that are ruby keywords' do
parameters = service.method(:query).parameters.map { |(_k, v)| v }
expect(parameters).to include(:do_)
end
it 'should define global parameters on methods' do
parameters = service.method(:query).parameters.map { |(_k, v)| v }
expect(parameters).to include(:fields, :quota_user, :user_ip)
end
it 'should include standard options & block' do
parameters = service.method(:query).parameters.map { |(_k, v)| v }
expect(parameters).to include(:options, :block)
end
it 'should define AUTH_TEST scope' do
expect(Google::Apis::TestV1::AUTH_TEST).to eql ('https://www.googleapis.com/auth/test')
end
it 'should define AUTH_TEST_READONLY scope' do
expect(Google::Apis::TestV1::AUTH_TEST_READONLY).to eql ('https://www.googleapis.com/auth/test.readonly')
end
context 'when simplifying class names' do
it 'should simplify the TestAnotherThing name' do
expect { Google::Apis::TestV1::AnotherThing.new }.not_to raise_error
end
it 'should not simplify the TestThing name' do
expect { Google::Apis::TestV1::TestThing.new }.not_to raise_error
end
end
context 'with the Thing resource' do
it 'should define the create method`' do
expect(service.method(:create_thing)).to_not be_nil
end
it 'should define the list method`' do
expect(service.method(:list_things)).to_not be_nil
end
it 'should define the get method`' do
expect(service.method(:get_thing)).to_not be_nil
end
it 'should include the download_dest parameter for get_thing' do
parameters = service.method(:get_thing).parameters.map { |(_k, v)| v }
expect(parameters).to include(:download_dest)
end
it 'should define the update method`' do
expect(service.method(:update_thing)).to_not be_nil
end
it 'should include the upload_source parameter for update_thing' do
parameters = service.method(:update_thing).parameters.map { |(_k, v)| v }
expect(parameters).to include(:upload_source)
end
it 'should define subresource methods' do
expect(service.method(:list_thing_subthings)).to_not be_nil
end
end
context 'with the get_thing method' do
before(:example) do
json = <<EOF
{
"id" : "123",
"name" : "A thing",
"enabled": true,
"properties": {
"prop_a" : "value_a",
"prop_b" : "value_b"
},
"photo": {
"filename": "image.jpg"
},
"hat": {
"type": "baseballHat",
"color": "red"
}
}
EOF
stub_request(:get, 'https://www.googleapis.com/test/v1/things/123')
.to_return(headers: { 'Content-Type' => 'application/json' }, body: json)
end
let(:thing) { service.get_thing('123') }
it 'should return a Thing' do
expect(thing).to be_instance_of(Google::Apis::TestV1::Thing)
end
it 'should set attributes' do
expect(thing.id).to eql '123'
end
it 'should alias boolean methods on Thing' do
expect(thing.enabled?).to be true
end
it 'should return a Hash for properties' do
expect(thing.properties).to be_instance_of(Hash)
end
it 'should set hash elements' do
expect(thing.properties).to include('prop_a' => 'value_a')
end
it 'should return the correct variant type for hat' do
expect(thing.hat).to be_instance_of(Google::Apis::TestV1::BaseballHat)
end
it 'should return the correct variant properties for hat' do
expect(thing.hat.color).to eql 'red'
end
it 'should return a photo' do
expect(thing.photo).to be_instance_of(Google::Apis::TestV1::Thing::Photo)
end
it 'should return photo properties' do
expect(thing.photo.filename).to eql 'image.jpg'
end
end
context 'with the create_thing method' do
before(:example) do
json = <<EOF
{
"id" : "123",
"name" : "A thing",
"properties": {
"prop_a" : "value_a"
},
"photo": {
"filename": "image.jpg"
},
"hat": {
"type": "topHat",
"height": 100
}
}
EOF
stub_request(:post, 'https://www.googleapis.com/test/v1/things')
.to_return(headers: { 'Content-Type' => 'application/json' }, body: json)
end
let(:thing) do
thing = Google::Apis::TestV1::Thing.new(name: 'A thing', properties: { 'prop_a' => 'value_a' })
thing.photo = Google::Apis::TestV1::Thing::Photo.new(filename: 'image.jpg')
thing.hat = Google::Apis::TestV1::TopHat.new(type: 'topHat', height: 100)
service.create_thing(thing)
end
it 'should serialize the thing' do
expected_body = <<EOF
{
"name" : "A thing",
"properties": {
"prop_a" : "value_a"
},
"photo": {
"filename": "image.jpg"
},
"hat": {
"type": "topHat",
"height": 100
}
}
EOF
expect(thing).to be_instance_of(Google::Apis::TestV1::Thing)
expect(a_request(:post, 'https://www.googleapis.com/test/v1/things')
.with { |req| expect(req.body).to be_json_eql(expected_body) }).to have_been_made
end
it 'should an updated id' do
expect(thing.id).to eql '123'
end
end
context 'with the query method' do
before(:example) do
body = %({"rows": [{"value": "hello"}, {"value": "world"}]})
stub_request(:get, 'https://www.googleapis.com/test/v1/query')
.to_return(headers: { 'Content-Type' => 'application/json' }, body: body)
end
it 'should return query results' do
results = service.query
expect(results).to be_instance_of(Google::Apis::TestV1::QueryResults)
end
it 'should return an array for items' do
results = service.query
expect(results.rows).to be_instance_of(Array)
end
it 'should return items of type Row' do
results = service.query
expect(results.rows.first).to be_instance_of(Google::Apis::TestV1::Row)
end
it 'should return values for rows' do
results = service.query
expect(results.rows[1].value).to eql('world')
end
end
end
end
end
+45
View File
@@ -0,0 +1,45 @@
# Copyright 2015 Google Inc.
#
# 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 'spec_helper'
require 'google/apis'
require 'google/apis/core/logging'
RSpec.describe Google::Apis do
it 'should have a default logger' do
expect(Google::Apis.logger).to be_an_instance_of(Logger)
end
context 'with service' do
let(:service) do
Class.new do
include Google::Apis::Core::Logging
end.new
end
it 'should have a logger' do
expect(service.logger).to be_an_instance_of(Logger)
end
it 'should use the default logger' do
expect(service.logger).to be Google::Apis.logger
end
it 'should allow custom loggers' do
Google::Apis.logger = Logger.new(STDERR)
expect(service.logger).to be Google::Apis.logger
end
end
end
+40
View File
@@ -0,0 +1,40 @@
# Copyright 2015 Google Inc.
#
# 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 'spec_helper'
require 'google/apis/options'
RSpec.describe Google::Apis::RequestOptions do
let(:options) { Google::Apis::RequestOptions.new }
it 'should not merge nil values' do
options.retries = 1
expect(options.merge(authorization: 'foo').retries).to eql 1
end
it 'should merge non-nil values' do
options.retries = 1
expect(options.merge(authorization: 'foo').authorization).to eql 'foo'
end
it 'should merge from options' do
opts = Google::Apis::RequestOptions.new
opts.authorization = 'foo'
expect(options.merge(opts).authorization).to eql 'foo'
end
it 'should allow nil in merge' do
expect(options.merge(nil)).to be_an_instance_of(Google::Apis::RequestOptions)
end
end
+117
View File
@@ -0,0 +1,117 @@
# Copyright 2015 Google Inc.
#
# 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.
SPEC_DIR = File.expand_path(File.dirname(__FILE__))
ROOT_DIR = File.expand_path(File.join(SPEC_DIR, '..'))
LIB_DIR = File.expand_path(File.join(ROOT_DIR, 'lib'))
GENERATED_DIR = File.expand_path(File.join(ROOT_DIR, 'generated'))
FIXTURES_DIR = File.expand_path(File.join(SPEC_DIR, 'fixtures'))
$LOAD_PATH.unshift(SPEC_DIR)
$LOAD_PATH.unshift(LIB_DIR)
$LOAD_PATH.unshift(GENERATED_DIR)
$LOAD_PATH.uniq!
if defined?(JRUBY_VERSION)
puts 'Skipping coverage on JRuby'
else
# set up coverage
require 'simplecov'
require 'coveralls'
SimpleCov.formatters = [
Coveralls::SimpleCov::Formatter,
SimpleCov::Formatter::HTMLFormatter
]
SimpleCov.start do
add_filter '/spec/'
add_filter '/generated/'
end
end
require 'rspec'
require 'webmock/rspec'
require 'json_spec'
require 'logging'
require 'rspec/logging_helper'
require 'google/apis'
require 'google/apis/core/base_service'
# Configure RSpec to capture log messages for each test. The output from the
# logs will be stored in the @log_output variable. It is a StringIO instance.
RSpec.configure do |config|
include RSpec::LoggingHelper
config.include JsonSpec::Helpers
config.include WebMock::API
config.capture_log_messages
Google::Apis.logger.level = Logger::DEBUG
end
[JsonSpec::Matchers::BeJsonEql,
JsonSpec::Matchers::IncludeJson,
JsonSpec::Matchers::HaveJsonType,
JsonSpec::Matchers::HaveJsonSize,
JsonSpec::Matchers::HaveJsonPath].each do |klass|
klass.send(:alias_method, :===, :matches?)
end
RSpec.shared_context 'HTTP client' do
let(:client) do
Google::Apis::Core::BaseService.new('', '').client
end
end
module TestHelpers
include WebMock::API
include WebMock::Matchers
end
# Enable retries for tests
Google::Apis::RequestOptions.default.retries = 5
# Temporarily patch WebMock to allow chunked responses for Net::HTTP
module Net
module WebMockHTTPResponse
def eval_chunk(chunk)
puts chunk.is_a? Exception
chunk if chunk.is_a?(String)
chunk.read if chunk.is_a?(IO)
chunk.call if chunk.is_a?(Proc)
fail chunk if chunk.is_a?(Class)
chunk
end
def read_body(dest = nil, &block)
if !(defined?(@__read_body_previously_called).nil?) && @__read_body_previously_called
return super
end
return @body if dest.nil? && block.nil?
fail ArgumentError.new('both arg and block given for HTTP method') if dest && block
return nil if @body.nil?
dest ||= ::Net::ReadAdapter.new(block)
body_parts = Array(@body)
body_parts.each do |chunk|
chunk = eval_chunk(chunk)
dest << chunk
end
@body = dest
ensure
# allow subsequent calls to #read_body to proceed as normal, without our hack...
@__read_body_previously_called = true
end
end
end