language: ruby
rvm:
- - 1.9.3
+ - 2.2
- 2.0.0
- - 2.1.0
+ - 2.1
+ - 1.9.3
- rbx-2
- - jruby-19mode
+ - jruby
script: "bundle exec rake spec:all"
before_install:
- sudo apt-get update
require 'google/api_client/service_account'
require 'google/api_client/batch'
require 'google/api_client/gzip'
+require 'google/api_client/charset'
require 'google/api_client/client_secrets'
require 'google/api_client/railtie' if defined?(Rails)
# @option options [String] :ca_file
# Optional set of root certificates to use when validating SSL connections.
# By default, a bundled set of trusted roots will be used.
+ # @options options[Hash] :force_encoding
+ # Experimental option. True if response body should be force encoded into the charset
+ # specified in the Content-Type header. Mostly intended for compressed content.
# @options options[Hash] :faraday_options
# Pass through of options to set on the Faraday connection
def initialize(options={})
@discovered_apis = {}
ca_file = options[:ca_file] || File.expand_path('../../cacerts.pem', __FILE__)
self.connection = Faraday.new do |faraday|
+ faraday.response :charset if options[:force_encoding]
faraday.response :gzip
faraday.options.params_encoder = Faraday::FlatParamsEncoder
faraday.ssl.ca_file = ca_file
# @param [String, Symbol] api The API name.
# @param [String] version The desired version of the API.
# @param [Addressable::URI] uri The URI of the discovery document.
+ # @return [Google::APIClient::API] The service object.
def register_discovery_uri(api, version, uri)
api = api.to_s
version = version || 'v1'
@discovery_uris["#{api}:#{version}"] = uri
+ discovered_api(api, version)
end
##
# @param [String] version The desired version of the API.
# @param [String, StringIO] discovery_document
# The contents of the discovery document.
+ # @return [Google::APIClient::API] The service object.
def register_discovery_document(api, version, discovery_document)
api = api.to_s
version = version || 'v1'
end
@discovery_documents["#{api}:#{version}"] =
MultiJson.load(discovery_document)
+ discovered_api(api, version)
end
##
# the HTTP response.
def process_http_response(response)
content_type = find_header('Content-Type', response.headers)
- boundary = /.*boundary=(.+)/.match(content_type)[1]
- parts = response.body.split(/--#{Regexp.escape(boundary)}/)
- parts = parts[1...-1]
- parts.each do |part|
- call_response = deserialize_call_response(part)
- _, call, callback = @calls.assoc(call_response.call_id)
- result = Google::APIClient::Result.new(call, call_response)
- callback.call(result) if callback
+ m = /.*boundary=(.+)/.match(content_type)
+ if m
+ boundary = m[1]
+ parts = response.body.split(/--#{Regexp.escape(boundary)}/)
+ parts = parts[1...-1]
+ parts.each do |part|
+ call_response = deserialize_call_response(part)
+ _, call, callback = @calls.assoc(call_response.call_id)
+ result = Google::APIClient::Result.new(call, call_response)
+ callback.call(result) if callback
+ end
end
Google::APIClient::Result.new(self, response)
end
--- /dev/null
+require 'faraday'
+require 'zlib'
+
+module Google
+ class APIClient
+ class Charset < Faraday::Response::Middleware
+ include Google::APIClient::Logging
+
+ def charset_for_content_type(type)
+ if type
+ m = type.match(/(?:charset|encoding)="?([a-z0-9-]+)"?/i)
+ if m
+ return Encoding.find(m[1])
+ end
+ end
+ nil
+ end
+
+ def adjust_encoding(env)
+ charset = charset_for_content_type(env[:response_headers]['content-type'])
+ if charset && env[:body].encoding != charset
+ env[:body].force_encoding(charset)
+ end
+ end
+
+ def on_complete(env)
+ adjust_encoding(env)
+ end
+ end
+ end
+end
+
+Faraday::Response.register_middleware :charset => Google::APIClient::Charset
\ No newline at end of file
@client_id = fdata[:client_id] || fdata["client_id"]
@client_secret = fdata[:client_secret] || fdata["client_secret"]
@redirect_uris = fdata[:redirect_uris] || fdata["redirect_uris"]
- @redirect_uris ||= [fdata[:redirect_uri]]
+ @redirect_uris ||= [fdata[:redirect_uri] || fdata["redirect_uri"]].compact
@javascript_origins = (
fdata[:javascript_origins] ||
fdata["javascript_origins"]
)
- @javascript_origins ||= [fdata[:javascript_origin]]
+ @javascript_origins ||= [fdata[:javascript_origin] || fdata["javascript_origin"]].compact
@authorization_uri = fdata[:auth_uri] || fdata["auth_uri"]
@authorization_uri ||= fdata[:authorization_uri]
@token_credential_uri = fdata[:token_uri] || fdata["token_uri"]
# @return [String]
# JSON
def to_json
- return MultiJson.dump({
+ return MultiJson.dump(to_hash)
+ end
+
+ def to_hash
+ {
self.flow => ({
'client_id' => self.client_id,
'client_secret' => self.client_secret,
end
accu
end
- })
+ }
end
def to_authorization
# Build a request for fetching the next page of data
#
# @return [Google::APIClient::Request]
- # API request for retrieving next page
+ # API request for retrieving next page, nil if no page token available
def next_page
+ return nil unless self.next_page_token
merged_parameters = Hash[self.reference.parameters].merge({
self.page_token_param => self.next_page_token
})
# Build a request for fetching the previous page of data
#
# @return [Google::APIClient::Request]
- # API request for retrieving previous page
+ # API request for retrieving previous page, nil if no page token available
def prev_page
+ return nil unless self.prev_page_token
merged_parameters = Hash[self.reference.parameters].merge({
self.page_token_param => self.prev_page_token
})
result = @client.execute(params)
return Google::APIClient::Service::Result.new(request, result)
elsif request.instance_of? Google::APIClient::Service::BatchRequest
- @client.execute(request.base_batch)
+ @client.execute(request.base_batch, {:connection => @connection})
end
end
end
--- /dev/null
+{"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"}}
\ No newline at end of file
--- /dev/null
+{
+ "kind": "discovery#describeItem",
+ "name": "zoo",
+ "version": "v1",
+ "description": "Zoo API used for testing",
+ "basePath": "/zoo/",
+ "rootUrl": "https://www.googleapis.com/",
+ "servicePath": "zoo/v1/",
+ "rpcPath": "/rpc",
+ "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"
+ }
+ },
+ "features": [
+ "dataWrapper"
+ ],
+ "schemas": {
+ "Animal": {
+ "id": "Animal",
+ "type": "object",
+ "properties": {
+ "etag": {
+ "type": "string"
+ },
+ "kind": {
+ "type": "string",
+ "default": "zoo#animal"
+ },
+ "name": {
+ "type": "string"
+ },
+ "photo": {
+ "type": "object",
+ "properties": {
+ "filename": {
+ "type": "string"
+ },
+ "hash": {
+ "type": "string"
+ },
+ "hashAlgorithm": {
+ "type": "string"
+ },
+ "size": {
+ "type": "integer"
+ },
+ "type": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "Animal2": {
+ "id": "Animal2",
+ "type": "object",
+ "properties": {
+ "kind": {
+ "type": "string",
+ "default": "zoo#animal"
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+ },
+ "AnimalFeed": {
+ "id": "AnimalFeed",
+ "type": "object",
+ "properties": {
+ "etag": {
+ "type": "string"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "Animal"
+ }
+ },
+ "kind": {
+ "type": "string",
+ "default": "zoo#animalFeed"
+ }
+ }
+ },
+ "AnimalMap": {
+ "id": "AnimalMap",
+ "type": "object",
+ "properties": {
+ "etag": {
+ "type": "string"
+ },
+ "animals": {
+ "type": "object",
+ "description": "Map of animal id to animal data",
+ "additionalProperties": {
+ "$ref": "Animal"
+ }
+ },
+ "kind": {
+ "type": "string",
+ "default": "zoo#animalMap"
+ }
+ }
+ },
+ "LoadFeed": {
+ "id": "LoadFeed",
+ "type": "object",
+ "properties": {
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "doubleVal": {
+ "type": "number"
+ },
+ "nullVal": {
+ "type": "null"
+ },
+ "booleanVal": {
+ "type": "boolean",
+ "description": "True or False."
+ },
+ "anyVal": {
+ "type": "any",
+ "description": "Anything will do."
+ },
+ "enumVal": {
+ "type": "string"
+ },
+ "kind": {
+ "type": "string",
+ "default": "zoo#loadValue"
+ },
+ "longVal": {
+ "type": "integer"
+ },
+ "stringVal": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "kind": {
+ "type": "string",
+ "default": "zoo#loadFeed"
+ }
+ }
+ }
+ },
+ "methods": {
+ "query": {
+ "path": "query",
+ "id": "bigquery.query",
+ "httpMethod": "GET",
+ "parameters": {
+ "q": {
+ "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
+ },
+ "o": {
+ "type": "object",
+ "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"
+ ]
+ },
+ "rr": {
+ "type": "string",
+ "location": "query",
+ "required": false,
+ "repeated": true,
+ "pattern": "[a-z]+"
+ }
+ }
+ }
+ },
+ "resources": {
+ "my": {
+ "resources": {
+ "favorites": {
+ "methods": {
+ "list": {
+ "path": "favorites/@me/mine",
+ "id": "zoo.animals.mine",
+ "httpMethod": "GET",
+ "parameters": {
+ "max-results": {
+ "location": "query",
+ "required": false
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "global": {
+ "resources": {
+ "print": {
+ "methods": {
+ "assert": {
+ "path": "global/print/assert",
+ "id": "zoo.animals.mine",
+ "httpMethod": "GET",
+ "parameters": {
+ "max-results": {
+ "location": "query",
+ "required": false
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "animals": {
+ "methods": {
+ "crossbreed": {
+ "path": "animals/crossbreed",
+ "id": "zoo.animals.crossbreed",
+ "httpMethod": "POST",
+ "description": "Cross-breed animals",
+ "response": {
+ "$ref": "Animal2"
+ },
+ "mediaUpload": {
+ "accept": [
+ "image/png"
+ ],
+ "protocols": {
+ "simple": {
+ "multipart": true,
+ "path": "upload/activities/{userId}/@self"
+ },
+ "resumable": {
+ "multipart": true,
+ "path": "upload/activities/{userId}/@self"
+ }
+ }
+ }
+ },
+ "delete": {
+ "path": "animals/{name}",
+ "id": "zoo.animals.delete",
+ "httpMethod": "DELETE",
+ "description": "Delete animals",
+ "parameters": {
+ "name": {
+ "location": "path",
+ "required": true,
+ "description": "Name of the animal to delete",
+ "type": "string"
+ }
+ },
+ "parameterOrder": [
+ "name"
+ ]
+ },
+ "get": {
+ "path": "animals/{name}",
+ "id": "zoo.animals.get",
+ "httpMethod": "GET",
+ "description": "Get animals",
+ "supportsMediaDownload": true,
+ "parameters": {
+ "name": {
+ "location": "path",
+ "required": true,
+ "description": "Name of the animal to load",
+ "type": "string"
+ },
+ "projection": {
+ "location": "query",
+ "type": "string",
+ "enum": [
+ "full"
+ ],
+ "enumDescriptions": [
+ "Include everything"
+ ]
+ }
+ },
+ "parameterOrder": [
+ "name"
+ ],
+ "response": {
+ "$ref": "Animal"
+ }
+ },
+ "getmedia": {
+ "path": "animals/{name}",
+ "id": "zoo.animals.get",
+ "httpMethod": "GET",
+ "description": "Get animals",
+ "parameters": {
+ "name": {
+ "location": "path",
+ "required": true,
+ "description": "Name of the animal to load",
+ "type": "string"
+ },
+ "projection": {
+ "location": "query",
+ "type": "string",
+ "enum": [
+ "full"
+ ],
+ "enumDescriptions": [
+ "Include everything"
+ ]
+ }
+ },
+ "parameterOrder": [
+ "name"
+ ]
+ },
+ "insert": {
+ "path": "animals",
+ "id": "zoo.animals.insert",
+ "httpMethod": "POST",
+ "description": "Insert animals",
+ "request": {
+ "$ref": "Animal"
+ },
+ "response": {
+ "$ref": "Animal"
+ },
+ "mediaUpload": {
+ "accept": [
+ "image/png"
+ ],
+ "maxSize": "1KB",
+ "protocols": {
+ "simple": {
+ "multipart": true,
+ "path": "upload/activities/{userId}/@self"
+ },
+ "resumable": {
+ "multipart": true,
+ "path": "upload/activities/{userId}/@self"
+ }
+ }
+ }
+ },
+ "list": {
+ "path": "animals",
+ "id": "zoo.animals.list",
+ "httpMethod": "GET",
+ "description": "List animals",
+ "parameters": {
+ "max-results": {
+ "location": "query",
+ "description": "Maximum number of results to return",
+ "type": "integer",
+ "minimum": "0"
+ },
+ "name": {
+ "location": "query",
+ "description": "Restrict result to animals with this name",
+ "type": "string"
+ },
+ "projection": {
+ "location": "query",
+ "type": "string",
+ "enum": [
+ "full"
+ ],
+ "enumDescriptions": [
+ "Include absolutely everything"
+ ]
+ },
+ "start-token": {
+ "location": "query",
+ "description": "Pagination token",
+ "type": "string"
+ }
+ },
+ "response": {
+ "$ref": "AnimalFeed"
+ }
+ },
+ "patch": {
+ "path": "animals/{name}",
+ "id": "zoo.animals.patch",
+ "httpMethod": "PATCH",
+ "description": "Update animals",
+ "parameters": {
+ "name": {
+ "location": "path",
+ "required": true,
+ "description": "Name of the animal to update",
+ "type": "string"
+ }
+ },
+ "parameterOrder": [
+ "name"
+ ],
+ "request": {
+ "$ref": "Animal"
+ },
+ "response": {
+ "$ref": "Animal"
+ }
+ },
+ "update": {
+ "path": "animals/{name}",
+ "id": "zoo.animals.update",
+ "httpMethod": "PUT",
+ "description": "Update animals",
+ "parameters": {
+ "name": {
+ "location": "path",
+ "description": "Name of the animal to update",
+ "type": "string"
+ }
+ },
+ "parameterOrder": [
+ "name"
+ ],
+ "request": {
+ "$ref": "Animal"
+ },
+ "response": {
+ "$ref": "Animal"
+ }
+ }
+ }
+ },
+ "load": {
+ "methods": {
+ "list": {
+ "path": "load",
+ "id": "zoo.load.list",
+ "httpMethod": "GET",
+ "response": {
+ "$ref": "LoadFeed"
+ }
+ }
+ }
+ },
+ "loadNoTemplate": {
+ "methods": {
+ "list": {
+ "path": "loadNoTemplate",
+ "id": "zoo.loadNoTemplate.list",
+ "httpMethod": "GET"
+ }
+ }
+ },
+ "scopedAnimals": {
+ "methods": {
+ "list": {
+ "path": "scopedanimals",
+ "id": "zoo.scopedAnimals.list",
+ "httpMethod": "GET",
+ "description": "List animals (scoped)",
+ "parameters": {
+ "max-results": {
+ "location": "query",
+ "description": "Maximum number of results to return",
+ "type": "integer",
+ "minimum": "0"
+ },
+ "name": {
+ "location": "query",
+ "description": "Restrict result to animals with this name",
+ "type": "string"
+ },
+ "projection": {
+ "location": "query",
+ "type": "string",
+ "enum": [
+ "full"
+ ],
+ "enumDescriptions": [
+ "Include absolutely everything"
+ ]
+ },
+ "start-token": {
+ "location": "query",
+ "description": "Pagination token",
+ "type": "string"
+ }
+ },
+ "response": {
+ "$ref": "AnimalFeed"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+# 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
\ No newline at end of file
require 'signet/oauth_1/client'
require 'google/api_client'
+fixtures_path = File.expand_path('../../../fixtures', __FILE__)
+
RSpec.describe Google::APIClient do
include ConnectionHelpers
CLIENT = Google::APIClient.new(:application_name => 'API Client Tests') unless defined?(CLIENT)
expect(CLIENT.preferred_version('bogus')).to eq(nil)
end
+ describe 'with zoo API' do
+ it 'should return API instance registered from file' do
+ zoo_json = File.join(fixtures_path, 'files', 'zoo.json')
+ contents = File.open(zoo_json, 'rb') { |io| io.read }
+ api = CLIENT.register_discovery_document('zoo', 'v1', contents)
+ expect(api).to be_kind_of(Google::APIClient::API)
+ end
+ end
+
describe 'with the prediction API' do
before do
CLIENT.authorization = nil
+# Encoding: utf-8
# Copyright 2012 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
def create_connection(&block)
Faraday.new do |b|
+ b.response :charset
b.response :gzip
b.adapter :test do |stub|
stub.get '/', &block
expect(result.body).to eq("Hello world\n")
end
+ it 'should inflate with the correct charset encoding' do
+ conn = create_connection do |env|
+ [200,
+ { 'Content-Encoding' => 'deflate', 'Content-Type' => 'application/json;charset=BIG5'},
+ Base64.decode64('eJxb8nLp7t2VAA8fBCI=')]
+ end
+ result = conn.get('/')
+ expect(result.body.encoding).to eq(Encoding::BIG5)
+ expect(result.body).to eq('日本語'.encode("BIG5"))
+ end
+
describe 'with API Client' do
before do
end
RSpec.describe Google::APIClient::Service::BatchRequest do
+
+ include ConnectionHelpers
+
+ context 'with a service connection' do
+ before do
+ @conn = stub_connection do |stub|
+ stub.post('/batch') do |env|
+ [500, {'Content-Type' => 'application/json'}, '{}']
+ end
+ end
+ @discovery = Google::APIClient::Service.new('discovery', 'v1',
+ {:application_name => APPLICATION_NAME, :authorization => nil,
+ :cache_store => nil, :connection => @conn})
+ @calls = [
+ @discovery.apis.get_rest(:api => 'plus', :version => 'v1'),
+ @discovery.apis.get_rest(:api => 'discovery', :version => 'v1')
+ ]
+ end
+
+ it 'should use the service connection' do
+ batch = @discovery.batch(@calls) do
+ end
+ batch.execute
+ @conn.verify
+ end
+ end
+
describe 'with the discovery API' do
before do
@discovery = Google::APIClient::Service.new('discovery', 'v1',
batch.execute
expect(call1_returned).to eq(true)
expect(call2_returned).to eq(true)
- end
+ end
end
end
end
\ No newline at end of file