google-apis-core-0.11.3/0000755000175100017510000000000014563425620013765 5ustar pravipravigoogle-apis-core-0.11.3/google-apis-core.gemspec0000644000175100017510000000572114563425620020473 0ustar pravipravi######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: google-apis-core 0.11.3 ruby lib Gem::Specification.new do |s| s.name = "google-apis-core".freeze s.version = "0.11.3" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.metadata = { "bug_tracker_uri" => "https://github.com/googleapis/google-api-ruby-client/issues", "changelog_uri" => "https://github.com/googleapis/google-api-ruby-client/tree/main/google-apis-core/CHANGELOG.md", "documentation_uri" => "https://googleapis.dev/ruby/google-apis-core/v0.11.3", "source_code_uri" => "https://github.com/googleapis/google-api-ruby-client/tree/main/google-apis-core" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Google LLC".freeze] s.date = "2024-01-17" s.email = "googleapis-packages@google.com".freeze s.files = [".yardopts".freeze, "CHANGELOG.md".freeze, "LICENSE.md".freeze, "OVERVIEW.md".freeze, "lib/google/api_client/auth/installed_app.rb".freeze, "lib/google/api_client/auth/key_utils.rb".freeze, "lib/google/api_client/auth/storage.rb".freeze, "lib/google/api_client/auth/storages/file_store.rb".freeze, "lib/google/api_client/auth/storages/redis_store.rb".freeze, "lib/google/api_client/client_secrets.rb".freeze, "lib/google/apis.rb".freeze, "lib/google/apis/core.rb".freeze, "lib/google/apis/core/api_command.rb".freeze, "lib/google/apis/core/base_service.rb".freeze, "lib/google/apis/core/batch.rb".freeze, "lib/google/apis/core/composite_io.rb".freeze, "lib/google/apis/core/download.rb".freeze, "lib/google/apis/core/hashable.rb".freeze, "lib/google/apis/core/http_command.rb".freeze, "lib/google/apis/core/json_representation.rb".freeze, "lib/google/apis/core/logging.rb".freeze, "lib/google/apis/core/multipart.rb".freeze, "lib/google/apis/core/storage_download.rb".freeze, "lib/google/apis/core/storage_upload.rb".freeze, "lib/google/apis/core/upload.rb".freeze, "lib/google/apis/core/version.rb".freeze, "lib/google/apis/errors.rb".freeze, "lib/google/apis/options.rb".freeze] s.homepage = "https://github.com/google/google-api-ruby-client".freeze s.licenses = ["Apache-2.0".freeze] s.required_ruby_version = Gem::Requirement.new(">= 2.5".freeze) s.rubygems_version = "3.4.20".freeze s.summary = "Common utility and base classes for legacy Google REST clients".freeze s.specification_version = 4 s.add_runtime_dependency(%q.freeze, ["~> 2.5", ">= 2.5.1"]) s.add_runtime_dependency(%q.freeze, [">= 0.16.2", "< 2.a"]) s.add_runtime_dependency(%q.freeze, [">= 2.8.1", "< 3.a"]) s.add_runtime_dependency(%q.freeze, ["~> 1.0"]) s.add_runtime_dependency(%q.freeze, ["~> 3.0"]) s.add_runtime_dependency(%q.freeze, [">= 2.0", "< 4.a"]) s.add_runtime_dependency(%q.freeze, [">= 0"]) end google-apis-core-0.11.3/LICENSE.md0000644000175100017510000002636114563425620015401 0ustar pravipravi Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. google-apis-core-0.11.3/CHANGELOG.md0000644000175100017510000000701614563425620015602 0ustar pravipravi# Release History ### 0.11.3 (2024-01-17) #### Bug Fixes * download with destination as pathname ([#17120](https://github.com/googleapis/google-api-ruby-client/issues/17120)) ### 0.11.2 (2023-10-27) #### Bug Fixes * update ssl_config to point to system default root CA path ([#16446](https://github.com/googleapis/google-api-ruby-client/issues/16446)) ### 0.11.1 (2023-07-20) #### Documentation * Document send_timeout_sec and fix up some types ([#14907](https://github.com/googleapis/google-api-ruby-client/issues/14907)) ### 0.11.0 (2023-02-08) #### Features * Optimize memory usage when upload chunk size is set to 0 ### 0.10.0 (2023-01-26) #### Features * Allow chunk size zero for storage resumable upload ([#13283](https://github.com/googleapis/google-api-ruby-client/issues/13283)) * Make chunk size configurable ([#13216](https://github.com/googleapis/google-api-ruby-client/issues/13216)) ### 0.9.5 (2023-01-12) #### Bug Fixes * Improve upload performance for Cloud Storage ([#13213](https://github.com/googleapis/google-api-ruby-client/issues/13213)) ### 0.9.4 (2023-01-07) #### Bug Fixes * Recursively redact unsafe payloads from logs ([#13189](https://github.com/googleapis/google-api-ruby-client/issues/13189)) ### 0.9.3 (2023-01-04) #### Bug Fixes * Removed some dead code ([#13099](https://github.com/googleapis/google-api-ruby-client/issues/13099)) * Replace `File.exists?` with `File.exist?` for compatibility with Ruby 3.2 ([#13161](https://github.com/googleapis/google-api-ruby-client/issues/13161)) ### 0.9.2 (2022-12-13) #### Bug Fixes * Update UNSAFE_CLASS_NAMES ([#13030](https://github.com/googleapis/google-api-ruby-client/issues/13030)) ### 0.9.1 (2022-10-18) #### Bug Fixes * Storage upload to handle empty string/file cases ([#12306](https://github.com/googleapis/google-api-ruby-client/issues/12306)) ### 0.9.0 (2022-09-18) #### Features * add support to have invocation-id header ([#11655](https://github.com/googleapis/google-api-ruby-client/issues/11655)) ### 0.8.0 (2022-09-16) #### Features * Add storage upload to move away from unified upload protocol ([#11508](https://github.com/googleapis/google-api-ruby-client/issues/11508)) ### 0.7.2 (2022-09-15) #### Bug Fixes * do not reset query_values in case of form encoding ([#11654](https://github.com/googleapis/google-api-ruby-client/issues/11654)) ### 0.7.1 (2022-09-14) #### Bug Fixes * Revert "chore(core): log errors as error instead of debug([#6494](https://github.com/googleapis/google-api-ruby-client/issues/6494))" ([#11561](https://github.com/googleapis/google-api-ruby-client/issues/11561)) ### 0.7.0 (2022-06-30) #### Features * Add storage specific download to respond with http header ### 0.6.0 (2022-06-15) #### Features * add few more errors as retriable errors ### 0.5.0 (2022-05-15) #### Features * Add support for retry options to be configurable ### 0.4.2 (2022-01-21) #### Bug Fixes * Support for max elapsed time configuration. ### 0.4.1 (2021-07-19) * FIX: Prevent duplicated pagination when a response returns an empty string as the next page token. ### 0.4.0 (2021-06-28) * Expanded googleauth dependency to include future 1.x versions ### 0.3.0 (2021-03-07) #### Features * Drop support for Ruby 2.4 and add support for Ruby 3.0 ### 0.2.1 (2021-01-25) #### Bug Fixes * Add webrick to the gem dependencies, for Ruby 3 compatibility ### 0.2.0 (2021-01-06) * Munge reported client version so backends can recognize split clients ### 0.1.0 (2021-01-01) * Initial release, extracted from google-api-client. google-apis-core-0.11.3/.yardopts0000644000175100017510000000026714563425620015640 0ustar pravipravi--hide-void-return --no-private --verbose --title=google-apis-core --markup-provider=redcarpet --markup=markdown --main OVERVIEW.md lib/**/*.rb - OVERVIEW.md CHANGELOG.md LICENSE.md google-apis-core-0.11.3/lib/0000755000175100017510000000000014563425620014533 5ustar pravipravigoogle-apis-core-0.11.3/lib/google/0000755000175100017510000000000014563425620016007 5ustar pravipravigoogle-apis-core-0.11.3/lib/google/apis/0000755000175100017510000000000014563425620016743 5ustar pravipravigoogle-apis-core-0.11.3/lib/google/apis/core.rb0000644000175100017510000000117614563425620020225 0ustar pravipravi# Copyright 2020 Google LLC # # 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 Google module Apis module Core end end end google-apis-core-0.11.3/lib/google/apis/errors.rb0000644000175100017510000000460314563425620020607 0ustar pravipravi# Copyright 2020 Google LLC # # 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 Google module Apis # Base error, capable of wrapping another class Error < StandardError attr_reader :status_code attr_reader :header attr_reader :body def initialize(err, status_code: nil, header: nil, body: nil) @cause = nil if err.respond_to?(:backtrace) super(err.message) @cause = err else super(err.to_s) end @status_code = status_code @header = header.dup unless header.nil? @body = body end def backtrace if @cause @cause.backtrace else super end end def inspect extra = "" extra << " status_code: #{status_code.inspect}" unless status_code.nil? extra << " header: #{header.inspect}" unless header.nil? extra << " body: #{body.inspect}" unless body.nil? "#<#{self.class.name}: #{message}#{extra}>" end end # An error which is raised when there is an unexpected response or other # transport error that prevents an operation from succeeding. class TransmissionError < Error end # An exception that is raised if a redirect is required # class RedirectError < Error end # A 4xx class HTTP error occurred. class ClientError < Error end # A 408 HTTP error occurred. class RequestTimeOutError < ClientError end # A 429 HTTP error occurred. class RateLimitError < Error end # A 403 HTTP error occurred. class ProjectNotLinkedError < Error end # A 401 HTTP error occurred. class AuthorizationError < Error end # A 5xx class HTTP error occurred. class ServerError < Error end # Error class for problems in batch requests. class BatchError < Error end end end google-apis-core-0.11.3/lib/google/apis/core/0000755000175100017510000000000014563425620017673 5ustar pravipravigoogle-apis-core-0.11.3/lib/google/apis/core/storage_upload.rb0000644000175100017510000001717614563425620023244 0ustar pravipravi# Copyright 2022 Google LLC # # 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 'google/apis/core/http_command' require 'google/apis/core/api_command' require 'google/apis/errors' require 'stringio' require 'tempfile' require 'mini_mime' module Google module Apis module Core # Base upload command. Not intended to be used directly # @private class StorageUploadCommand < ApiCommand CONTENT_LENGTH_HEADER = "Content-Length" CONTENT_TYPE_HEADER = "Content-Type" UPLOAD_CONTENT_TYPE_HEADER = "X-Upload-Content-Type" LOCATION_HEADER = "Location" CONTENT_RANGE_HEADER = "Content-Range" RESUMABLE = "resumable" OK_STATUS = 200 # File name or IO containing the content to upload # @return [String, File, #read] attr_accessor :upload_source # Content type of the upload material # @return [String] attr_accessor :upload_content_type # Content, as UploadIO # @return [Google::Apis::Core::UploadIO] attr_accessor :upload_io # Upload chunk size # @return [Integer] attr_accessor :upload_chunk_size # Ensure the content is readable and wrapped in an IO instance. # # @return [void] # @raise [Google::Apis::ClientError] if upload source is invalid def prepare! @upload_url = nil @offset = 0 @upload_incomplete = true # Prevent the command from populating the body with form encoding, by # asserting that it already has a body. Form encoding is never used # by upload requests. self.body = '' unless self.body super if streamable?(upload_source) self.upload_io = upload_source @close_io_on_finish = false elsif self.upload_source.is_a?(String) self.upload_io = File.new(upload_source, 'r') if self.upload_content_type.nil? type = MiniMime.lookup_by_filename(upload_source) self.upload_content_type = type&.content_type end @close_io_on_finish = true else fail Google::Apis::ClientError, 'Invalid upload source' end end # Close IO stream when command done. Only closes the stream if it was opened by the command. def release! upload_io.close if @close_io_on_finish end # Execute the command, retrying as necessary # # @param [HTTPClient] client # HTTP client # @yield [result, err] Result or error if block supplied # @return [Object] # @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried # @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification # @raise [Google::Apis::AuthorizationError] Authorization is required def execute(client) prepare! opencensus_begin_span @upload_chunk_size = options.upload_chunk_size do_retry :initiate_resumable_upload, client while @upload_incomplete res = do_retry :send_upload_command, client end res ensure opencensus_end_span @http_res = nil release! end def initiate_resumable_upload(client) logger.debug { sprintf('Intiating resumable upload command to %s', url) } request_header = header.dup apply_request_options(request_header) request_query = query.dup request_query['uploadType'] = RESUMABLE request_header[CONTENT_LENGTH_HEADER] = upload_io.size.to_s request_header[CONTENT_TYPE_HEADER] = JSON_CONTENT_TYPE request_header[UPLOAD_CONTENT_TYPE_HEADER] = upload_content_type unless upload_content_type.nil? response = client.post(url.to_s, query: request_query, body: body, header: request_header, follow_redirect: true) result = process_response(response.status_code, response.header, response.body) success(result) rescue => e error(e, rethrow: true) end # Send the actual content # # @param [HTTPClient] client # HTTP client # @return [HTTP::Message] # @raise [Google::Apis::ServerError] Unable to send the request def send_upload_command(client) logger.debug { sprintf('Sending upload command to %s', @upload_url) } remaining_content_size = upload_io.size - @offset current_chunk_size = get_current_chunk_size remaining_content_size request_header = header.dup request_header[CONTENT_RANGE_HEADER] = get_content_range_header current_chunk_size request_header[CONTENT_LENGTH_HEADER] = current_chunk_size chunk_body = if @upload_chunk_size == 0 upload_io else StringIO.new(upload_io.read(current_chunk_size)) end response = client.put(@upload_url, body: chunk_body, header: request_header, follow_redirect: true) result = process_response(response.status_code, response.header, response.body) @upload_incomplete = false if response.status_code.eql? OK_STATUS @offset += current_chunk_size if @upload_incomplete success(result) rescue => e upload_io.pos = @offset error(e, rethrow: true) end # Check the to see if the upload is complete or needs to be resumed. # # @param [Integer] status # HTTP status code of response # @param [HTTP::Message::Headers] header # Response headers # @param [String, #read] body # Response body # @return [Object] # Response object # @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried # @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification # @raise [Google::Apis::AuthorizationError] Authorization is required def process_response(status, header, body) @upload_url = header[LOCATION_HEADER].first unless header[LOCATION_HEADER].empty? super(status, header, body) end def streamable?(upload_source) upload_source.is_a?(IO) || upload_source.is_a?(StringIO) || upload_source.is_a?(Tempfile) end def get_current_chunk_size remaining_content_size # Disable chunking if the chunk size is set to zero. if @upload_chunk_size == 0 remaining_content_size else remaining_content_size < @upload_chunk_size ? remaining_content_size : @upload_chunk_size end end def get_content_range_header current_chunk_size if upload_io.size == 0 numerator = "*" else numerator = sprintf("%d-%d", @offset, @offset+current_chunk_size-1) end sprintf('bytes %s/%d', numerator, upload_io.size) end end end end end google-apis-core-0.11.3/lib/google/apis/core/composite_io.rb0000644000175100017510000000525014563425620022713 0ustar pravipravi# Copyright 2020 Google LLC # # 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. # 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 'google/apis/core/http_command' require 'google/apis/core/upload' require 'google/apis/core/download' require 'addressable/uri' require 'securerandom' module Google module Apis module Core class CompositeIO def initialize(*ios) @ios = ios.flatten @pos = 0 @index = 0 @sizes = @ios.map(&:size) end def read(length = nil, buf = nil) buf = buf ? buf.replace('') : '' begin io = @ios[@index] break if io.nil? result = io.read(length) if result buf << result if length length -= result.length break if length == 0 end end @index += 1 end while @index < @ios.length buf.length > 0 ? buf : nil end def size @sizes.reduce(:+) end alias_method :length, :size def pos @pos end def pos=(pos) fail ArgumentError, "Position can not be negative" if pos < 0 @pos = pos new_index = nil @ios.each_with_index do |io,idx| size = io.size if pos <= size new_index ||= idx io.pos = pos pos = 0 else io.pos = size pos -= size end end @index = new_index unless new_index.nil? end def rewind self.pos = 0 end end end end endgoogle-apis-core-0.11.3/lib/google/apis/core/batch.rb0000644000175100017510000002021614563425620021302 0ustar pravipravi# Copyright 2020 Google LLC # # 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. # 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 'google/apis/core/multipart' require 'google/apis/core/http_command' require 'google/apis/core/upload' require 'google/apis/core/storage_upload' require 'google/apis/core/download' require 'google/apis/core/composite_io' require 'addressable/uri' require 'securerandom' module Google module Apis module Core # Wrapper request for batching multiple calls in a single server request class BatchCommand < HttpCommand MULTIPART_MIXED = 'multipart/mixed' # @param [symbol] method # HTTP method # @param [String,Addressable::URI, Addressable::Template] url # HTTP URL or template def initialize(method, url) super(method, url) @calls = [] @base_id = SecureRandom.uuid end ## # Add a new call to the batch request. # # @param [Google::Apis::Core::HttpCommand] call API Request to add # @yield [result, err] Result & error when response available # @return [Google::Apis::Core::BatchCommand] self def add(call, &block) ensure_valid_command(call) @calls << [call, block] self end protected ## # Deconstruct the batch response and process the individual results # # @param [String] content_type # Content type of body # @param [String, #read] body # Response body # @return [Object] # Response object def decode_response_body(content_type, body) m = /.*boundary=(.+)/.match(content_type) if m parts = split_parts(body, m[1]) deserializer = CallDeserializer.new parts.each_index do |index| response = deserializer.to_http_response(parts[index]) outer_header = response.shift call_id = header_to_id(outer_header['Content-ID'].first) || index call, callback = @calls[call_id] begin result = call.process_response(*response) unless call.nil? success(result, &callback) rescue => e error(e, &callback) end end end nil end def split_parts(body, boundary) parts = body.split(/\r?\n?--#{Regexp.escape(boundary)}/) parts[1...-1] end # Encode the batch request # @return [void] # @raise [Google::Apis::BatchError] if batch is empty def prepare! fail BatchError, 'Cannot make an empty batch request' if @calls.empty? serializer = CallSerializer.new multipart = Multipart.new(content_type: MULTIPART_MIXED) @calls.each_index do |index| call, _ = @calls[index] content_id = id_to_header(index) io = serializer.to_part(call) multipart.add_upload(io, content_type: 'application/http', content_id: content_id) end self.body = multipart.assemble header['Content-Type'] = multipart.content_type super end def ensure_valid_command(command) if command.is_a?(Google::Apis::Core::BaseUploadCommand) || command.is_a?(Google::Apis::Core::DownloadCommand) || command.is_a?(Google::Apis::Core::StorageDownloadCommand) || command.is_a?(Google::Apis::Core::StorageUploadCommand) fail Google::Apis::ClientError, 'Can not include media requests in batch' end fail Google::Apis::ClientError, 'Invalid command object' unless command.is_a?(HttpCommand) end def id_to_header(call_id) return sprintf('<%s+%i>', @base_id, call_id) end def header_to_id(content_id) match = //.match(content_id) return match[1].to_i if match return nil end end # Wrapper request for batching multiple uploads in a single server request class BatchUploadCommand < BatchCommand def ensure_valid_command(command) fail Google::Apis::ClientError, 'Can only include upload commands in batch' \ unless command.is_a?(Google::Apis::Core::BaseUploadCommand) end def prepare! header['X-Goog-Upload-Protocol'] = 'batch' super end end # Serializes a command for embedding in a multipart batch request # @private class CallSerializer ## # Serialize a single batched call for assembling the multipart message # # @param [Google::Apis::Core::HttpCommand] call # the call to serialize. # @return [IO] # the serialized request def to_part(call) call.prepare! # This will add the Authorization header if needed. call.apply_request_options(call.header) parts = [] parts << build_head(call) parts << build_body(call) unless call.body.nil? Google::Apis::Core::CompositeIO.new(*parts) end protected def build_head(call) request_head = "#{call.method.to_s.upcase} #{Addressable::URI.parse(call.url).request_uri} HTTP/1.1" call.header.each do |key, value| request_head << sprintf("\r\n%s: %s", key, value) end request_head << sprintf("\r\nHost: %s", call.url.host) request_head << "\r\n\r\n" StringIO.new(request_head) end def build_body(call) return nil if call.body.nil? return call.body if call.body.respond_to?(:read) StringIO.new(call.body) end end # Deconstructs a raw HTTP response part # @private class CallDeserializer # Parse a batched response. # # @param [String] call_response # the response to parse. # @return [Array<(Fixnum, Hash, String)>] # Status, header, and response body. def to_http_response(call_response) outer_header, outer_body = split_header_and_body(call_response) status_line, payload = outer_body.split(/\n/, 2) _, status = status_line.split(' ', 3) header, body = split_header_and_body(payload) [outer_header, status.to_i, header, body] end protected # Auxiliary method to split the header from the body in an HTTP response. # # @param [String] response # the response to parse. # @return [Array<(HTTP::Message::Headers, String)>] # the header and the body, separately. def split_header_and_body(response) header = HTTP::Message::Headers.new payload = response.lstrip while payload line, payload = payload.split(/\n/, 2) line.sub!(/\s+\z/, '') break if line.empty? match = /\A([^:]+):\s*/.match(line) fail BatchError, sprintf('Invalid header line in response: %s', line) if match.nil? header[match[1]] = match.post_match end [header, payload] end end end end end google-apis-core-0.11.3/lib/google/apis/core/base_service.rb0000644000175100017510000004565014563425620022664 0ustar pravipravi# Copyright 2020 Google LLC # # 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 'addressable/uri' require 'addressable/template' require 'google/apis' require 'google/apis/core/version' require 'google/apis/core/api_command' require 'google/apis/core/batch' require 'google/apis/core/upload' require 'google/apis/core/storage_upload' require 'google/apis/core/download' require 'google/apis/core/storage_download' require 'google/apis/options' require 'googleauth' require 'httpclient' module Google module Apis module Core # Helper class for enumerating over a result set requiring multiple fetches class PagedResults include Enumerable attr_reader :last_result # @param [BaseService] service # Current service instance # @param [Fixnum] max # Maximum number of items to iterate over. Nil if no limit # @param [Boolean] cache # True (default) if results should be cached so multiple iterations can be used. # @param [Symbol] items # Name of the field in the result containing the items. Defaults to :items def initialize(service, max: nil, items: :items, cache: true, response_page_token: :next_page_token, &block) @service = service @block = block @max = max @items_field = items @response_page_token_field = response_page_token if cache @result_cache = Hash.new do |h, k| h[k] = @block.call(k, @service) end @fetch_proc = Proc.new { |token| @result_cache[token] } else @fetch_proc = Proc.new { |token| @block.call(token, @service) } end end # Iterates over result set, fetching additional pages as needed def each page_token = nil item_count = 0 loop do @last_result = @fetch_proc.call(page_token) items = @last_result.send(@items_field) if items.kind_of?(Array) for item in items item_count = item_count + 1 break if @max && item_count > @max yield item end elsif items.kind_of?(Hash) items.each do |key, val| item_count = item_count + 1 break if @max && item_count > @max yield key, val end elsif items # yield singular non-nil items (for genomics API) yield items end break if @max && item_count >= @max next_page_token = @last_result.send(@response_page_token_field) break if next_page_token.to_s.empty? || next_page_token == page_token page_token = next_page_token end end end # Base service for all APIs. Not to be used directly. # class BaseService include Logging # Root URL (host/port) for the API # @return [Addressable::URI] attr_accessor :root_url # Additional path prefix for all API methods # @return [Addressable::URI] attr_accessor :base_path # Alternate path prefix for media uploads # @return [Addressable::URI] attr_accessor :upload_path # Alternate path prefix for all batch methods # @return [Addressable::URI] attr_accessor :batch_path # HTTP client # @return [HTTPClient] attr_accessor :client # General settings # @return [Google::Apis::ClientOptions] attr_accessor :client_options # Default options for all requests # @return [Google::Apis::RequestOptions] attr_accessor :request_options # Client library name. # @return [String] attr_accessor :client_name # Client library version. # @return [String] attr_accessor :client_version # @param [String,Addressable::URI] root_url # Root URL for the API # @param [String,Addressable::URI] base_path # Additional path prefix for all API methods # @api private def initialize(root_url, base_path, client_name: nil, client_version: nil) self.root_url = root_url self.base_path = base_path self.client_name = client_name || 'google-api-ruby-client' self.client_version = client_version || Google::Apis::Core::VERSION self.upload_path = "upload/#{base_path}" self.batch_path = 'batch' self.client_options = Google::Apis::ClientOptions.default.dup self.request_options = Google::Apis::RequestOptions.default.dup end # @!attribute [rw] authorization # @return [Signet::OAuth2::Client] # OAuth2 credentials def authorization=(authorization) request_options.authorization = authorization end def authorization request_options.authorization end # TODO: with(options) method # Perform a batch request. Calls made within the block are sent in a single network # request to the server. # # @example # service.batch do |s| # s.get_item(id1) do |res, err| # # process response for 1st call # end # # ... # s.get_item(idN) do |res, err| # # process response for Nth call # end # end # # @param [Hash, Google::Apis::RequestOptions] options # Request-specific options # @yield [self] # @return [void] def batch(options = nil) batch_command = BatchCommand.new(:post, Addressable::URI.parse(root_url + batch_path)) batch_command.options = request_options.merge(options) apply_command_defaults(batch_command) begin start_batch(batch_command) yield self ensure end_batch end batch_command.execute(client) end # Perform a batch upload request. Calls made within the block are sent in a single network # request to the server. Batch uploads are useful for uploading multiple small files. For larger # files, use single requests which use a resumable upload protocol. # # @example # service.batch do |s| # s.insert_item(upload_source: 'file1.txt') do |res, err| # # process response for 1st call # end # # ... # s.insert_item(upload_source: 'fileN.txt') do |res, err| # # process response for Nth call # end # end # # @param [Hash, Google::Apis::RequestOptions] options # Request-specific options # @yield [self] # @return [void] def batch_upload(options = nil) batch_command = BatchUploadCommand.new(:put, Addressable::URI.parse(root_url + upload_path)) batch_command.options = request_options.merge(options) apply_command_defaults(batch_command) begin start_batch(batch_command) yield self ensure end_batch end batch_command.execute(client) end # Get the current HTTP client # @return [HTTPClient] def client @client ||= new_client end # Simple escape hatch for making API requests directly to a given # URL. This is not intended to be used as a generic HTTP client # and should be used only in cases where no service method exists # (e.g. fetching an export link for a Google Drive file.) # # @param [Symbol] method # HTTP method as symbol (e.g. :get, :post, :put, ...) # @param [String] url # URL to call # @param [Hash] params # Optional hash of query parameters # @param [#read] body # Optional body for POST/PUT # @param [IO, String] download_dest # IO stream or filename to receive content download # @param [Google::Apis::RequestOptions] options # Request-specific options # # @yield [result, err] Result & error if block supplied # @yieldparam result [String] HTTP response body # @yieldparam err [StandardError] error object if request failed # # @return [String] HTTP response body def http(method, url, params: nil, body: nil, download_dest: nil, options: nil, &block) if download_dest command = DownloadCommand.new(method, url, body: body, client_version: client_version) else command = HttpCommand.new(method, url, body: body) end command.options = request_options.merge(options) apply_command_defaults(command) command.query.merge(Hash(params)) execute_or_queue_command(command, &block) end # Executes a given query with paging, automatically retrieving # additional pages as necessary. Requires a block that returns the # result set of a page. The current page token is supplied as an argument # to the block. # # Note: The returned enumerable also contains a `last_result` field # containing the full result of the last query executed. # # @param [Fixnum] max # Maximum number of items to iterate over. Defaults to nil -- no upper bound. # @param [Symbol] items # Name of the field in the result containing the items. Defaults to :items # @param [Boolean] cache # True (default) if results should be cached so multiple iterations can be used. # @return [Enumerble] # @yield [token, service] # Current page token & service instance # @yieldparam [String] token # Current page token to be used in the query # @yieldparam [service] # Current service instance # @since 0.9.4 # # @example Retrieve all files, # file_list = service.fetch_all { |token, s| s.list_files(page_token: token) } # file_list.each { |f| ... } def fetch_all(max: nil, items: :items, cache: true, response_page_token: :next_page_token, &block) fail "fetch_all may not be used inside a batch" if batch? return PagedResults.new(self, max: max, items: items, cache: cache, response_page_token: response_page_token, &block) end protected # Create a new upload command. # # @param [symbol] method # HTTP method for uploading (typically :put or :post) # @param [String] path # Additional path to upload endpoint, appended to API base path # @param [Hash, Google::Apis::RequestOptions] options # Request-specific options # @return [Google::Apis::Core::UploadCommand] def make_upload_command(method, path, options) template = Addressable::Template.new(root_url + upload_path + path) if batch? command = MultipartUploadCommand.new(method, template, client_version: client_version) else command = ResumableUploadCommand.new(method, template, client_version: client_version) end command.options = request_options.merge(options) apply_command_defaults(command) command end # Create a new storage upload command. # This is specifically for storage because we are moving to a new upload protocol. # Ref: https://cloud.google.com/storage/docs/performing-resumable-uploads # # @param [Symbol] method # HTTP method for uploading. The initial request to initiate a resumable session # is :post and the subsequent chunks uploaded to the session are :put # @param [String] path # Additional path to upload endpoint, appended to API base path # @param [Hash, Google::Apis::RequestOptions] options # Request-specific options # @return [Google::Apis::Core::StorageUploadCommand] def make_storage_upload_command(method, path, options) template = Addressable::Template.new(root_url + upload_path + path) command = StorageUploadCommand.new(method, template, client_version: client_version) command.options = request_options.merge(options) apply_command_defaults(command) command end # Create a new download command. # # @param [symbol] method # HTTP method for uploading (typically :get) # @param [String] path # Additional path to download endpoint, appended to API base path # @param [Hash, Google::Apis::RequestOptions] options # Request-specific options # @return [Google::Apis::Core::DownloadCommand] def make_download_command(method, path, options) template = Addressable::Template.new(root_url + base_path + path) command = DownloadCommand.new(method, template, client_version: client_version) command.options = request_options.merge(options) command.query['alt'] = 'media' apply_command_defaults(command) command end # Create a new storage download command. This is specifically for storage because # we want to return response header too in the response. # # @param [symbol] method # HTTP method for uploading (typically :get) # @param [String] path # Additional path to download endpoint, appended to API base path # @param [Hash, Google::Apis::RequestOptions] options # Request-specific options # @return [Google::Apis::Core::StorageDownloadCommand] def make_storage_download_command(method, path, options) template = Addressable::Template.new(root_url + base_path + path) command = StorageDownloadCommand.new(method, template, client_version: client_version) command.options = request_options.merge(options) command.query['alt'] = 'media' apply_command_defaults(command) command end # Create a new command. # # @param [symbol] method # HTTP method (:get, :post, :delete, etc...) # @param [String] path # Additional path, appended to API base path # @param [Hash, Google::Apis::RequestOptions] options # Request-specific options # @return [Google::Apis::Core::DownloadCommand] def make_simple_command(method, path, options) full_path = if path.start_with? "/" path[1..-1] else base_path + path end template = Addressable::Template.new(root_url + full_path) command = ApiCommand.new(method, template, client_version: client_version) command.options = request_options.merge(options) apply_command_defaults(command) command end # Execute the request. If a batch is in progress, the request is added to the batch instead. # # @param [Google::Apis::Core::HttpCommand] command # Command to execute # @return [Object] response object if command executed and no callback supplied # @yield [result, err] Result & error if block supplied # @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried # @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification # @raise [Google::Apis::AuthorizationError] Authorization is required def execute_or_queue_command(command, &callback) batch_command = current_batch if batch_command fail "Can not combine services in a batch" if Thread.current[:google_api_batch_service] != self batch_command.add(command, &callback) nil else command.execute(client, &callback) end end # Update commands with service-specific options. To be implemented by subclasses # @param [Google::Apis::Core::HttpCommand] _command def apply_command_defaults(_command) end private # Get the current batch context # # @return [Google:Apis::Core::BatchRequest] def current_batch Thread.current[:google_api_batch] end # Check if a batch is in progress # @return [Boolean] def batch? !current_batch.nil? end # Start a new thread-local batch context # @param [Google::Apis::Core::BatchCommand] cmd def start_batch(cmd) fail "Batch already in progress" if batch? Thread.current[:google_api_batch] = cmd Thread.current[:google_api_batch_service] = self end # Clear thread-local batch context def end_batch Thread.current[:google_api_batch] = nil Thread.current[:google_api_batch_service] = nil end # Create a new HTTP client # @return [HTTPClient] def new_client client = ::HTTPClient.new if client_options.transparent_gzip_decompression client.transparent_gzip_decompression = client_options.transparent_gzip_decompression end client.proxy = client_options.proxy_url if client_options.proxy_url if client_options.open_timeout_sec client.connect_timeout = client_options.open_timeout_sec end if client_options.read_timeout_sec client.receive_timeout = client_options.read_timeout_sec end if client_options.send_timeout_sec client.send_timeout = client_options.send_timeout_sec end client.follow_redirect_count = 5 client.default_header = { 'User-Agent' => user_agent } client.debug_dev = logger if client_options.log_http_requests # Make HttpClient use system default root CA path # https://github.com/nahi/httpclient/issues/445 client.ssl_config.clear_cert_store client.ssl_config.cert_store.set_default_paths client end # Build the user agent header # @return [String] def user_agent sprintf('%s/%s %s/%s %s (gzip)', client_options.application_name, client_options.application_version, client_name, client_version, Google::Apis::OS_VERSION) end end end end end google-apis-core-0.11.3/lib/google/apis/core/multipart.rb0000644000175100017510000000776114563425620022254 0ustar pravipravi# Copyright 2020 Google LLC # # 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 Google module Apis module Core # Part of a multipart request for holding JSON data # # @private class JsonPart # @param [String] value # JSON content # @param [Hash] header # Additional headers def initialize(value, header = {}) @value = value @header = header end def to_io(boundary) part = '' part << "--#{boundary}\r\n" part << "Content-Type: application/json\r\n" @header.each do |(k, v)| part << "#{k}: #{v}\r\n" end part << "\r\n" part << "#{@value}\r\n" StringIO.new(part) end end # Part of a multipart request for holding arbitrary content. # # @private class FilePart # @param [IO] io # IO stream # @param [Hash] header # Additional headers def initialize(io, header = {}) @io = io @header = header @length = io.respond_to?(:size) ? io.size : nil end def to_io(boundary) head = '' head << "--#{boundary}\r\n" @header.each do |(k, v)| head << "#{k}: #{v}\r\n" end head << "Content-Length: #{@length}\r\n" unless @length.nil? head << "Content-Transfer-Encoding: binary\r\n" head << "\r\n" Google::Apis::Core::CompositeIO.new(StringIO.new(head), @io, StringIO.new("\r\n")) end end # Helper for building multipart requests class Multipart MULTIPART_RELATED = 'multipart/related' # @return [String] # Content type header attr_reader :content_type # @param [String] content_type # Content type for the multipart request # @param [String] boundary # Part delimiter def initialize(content_type: MULTIPART_RELATED, boundary: nil) @parts = [] @boundary = boundary || Digest::SHA1.hexdigest(SecureRandom.random_bytes(8)) @content_type = "#{content_type}; boundary=#{@boundary}" end # Append JSON data part # # @param [String] body # JSON text # @param [String] content_id # Optional unique ID of this part # @return [self] def add_json(body, content_id: nil) header = {} header['Content-ID'] = content_id unless content_id.nil? @parts << Google::Apis::Core::JsonPart.new(body, header).to_io(@boundary) self end # Append arbitrary data as a part # # @param [IO] upload_io # IO stream # @param [String] content_id # Optional unique ID of this part # @return [self] def add_upload(upload_io, content_type: nil, content_id: nil) header = { 'Content-Type' => content_type || 'application/octet-stream' } header['Content-Id'] = content_id unless content_id.nil? @parts << Google::Apis::Core::FilePart.new(upload_io, header).to_io(@boundary) self end # Assemble the multipart requests # # @return [IO] # IO stream def assemble @parts << StringIO.new("--#{@boundary}--\r\n\r\n") Google::Apis::Core::CompositeIO.new(*@parts) end end end end endgoogle-apis-core-0.11.3/lib/google/apis/core/api_command.rb0000644000175100017510000002100214563425620022462 0ustar pravipravi# Copyright 2020 Google LLC # # 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 'addressable/uri' require 'addressable/template' require 'google/apis/core/http_command' require 'google/apis/errors' require 'json' require 'retriable' require "securerandom" module Google module Apis module Core # Command for executing most basic API request with JSON requests/responses class ApiCommand < HttpCommand JSON_CONTENT_TYPE = 'application/json' FIELDS_PARAM = 'fields' ERROR_REASON_MAPPING = { 'rateLimitExceeded' => Google::Apis::RateLimitError, 'userRateLimitExceeded' => Google::Apis::RateLimitError, 'projectNotLinked' => Google::Apis::ProjectNotLinkedError } # JSON serializer for request objects # @return [Google::Apis::Core::JsonRepresentation] attr_accessor :request_representation # Request body to serialize # @return [Object] attr_accessor :request_object # JSON serializer for response objects # @return [Google::Apis::Core::JsonRepresentation] attr_accessor :response_representation # Class to instantiate when de-serializing responses # @return [Object] attr_accessor :response_class # Client library version. # @return [String] attr_accessor :client_version # @param [symbol] method # HTTP method # @param [String,Addressable::URI, Addressable::Template] url # HTTP URL or template # @param [String, #read] body # Request body def initialize(method, url, body: nil, client_version: nil) super(method, url, body: body) self.client_version = client_version || Core::VERSION end # Serialize the request body # # @return [void] def prepare! set_api_client_header set_user_project_header if options&.api_format_version header['X-Goog-Api-Format-Version'] = options.api_format_version.to_s end query[FIELDS_PARAM] = normalize_fields_param(query[FIELDS_PARAM]) if query.key?(FIELDS_PARAM) if request_representation && request_object header['Content-Type'] ||= JSON_CONTENT_TYPE if options && options.skip_serialization self.body = request_object else self.body = request_representation.new(request_object).to_json(user_options: { skip_undefined: true }) end end super end # Deserialize the response body if present # # @param [String] content_type # Content type of body # @param [String, #read] body # Response body # @return [Object] # Response object # noinspection RubyUnusedLocalVariable def decode_response_body(content_type, body) return super unless response_representation return super if options && options.skip_deserialization return super if content_type.nil? return nil unless content_type.start_with?(JSON_CONTENT_TYPE) body = "{}" if body.empty? instance = response_class.new response_representation.new(instance).from_json(body, unwrap: response_class) instance end # Check the response and raise error if needed # # @param [Fixnum] status # HTTP status code of response # @param [Hash] header # HTTP response headers # @param [String] body # HTTP response body # @param [String] message # Error message text # @return [void] # @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried # @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification # @raise [Google::Apis::AuthorizationError] Authorization is required def check_status(status, header = nil, body = nil, message = nil) case status when 400, 402...500 reason, message = parse_error(body) if reason message = sprintf('%s: %s', reason, message) raise ERROR_REASON_MAPPING[reason].new( message, status_code: status, header: header, body: body ) if ERROR_REASON_MAPPING.key?(reason) end super(status, header, body, message) else super(status, header, body, message) end end def allow_form_encoding? request_representation.nil? && super end private def set_api_client_header old_xgac = header .find_all { |k, v| k.downcase == 'x-goog-api-client' } .map { |(a, b)| b } .join(' ') .split .find_all { |s| s !~ %r{^gl-ruby/|^gdcl/} } .join(' ') # Report 0.x.y versions that are in separate packages as 1.x.y. # Thus, reported gdcl/0.x.y versions are monopackage clients, while # reported gdcl/1.x.y versions are split clients. # In the unlikely event that we want to release post-1.0 versions of # these clients, we should start the versioning at 2.0 to avoid # confusion. munged_client_version = client_version.sub(/^0\./, "1.") xgac = "gl-ruby/#{RUBY_VERSION} gdcl/#{munged_client_version}" xgac = old_xgac.empty? ? xgac : "#{old_xgac} #{xgac}" header.delete_if { |k, v| k.downcase == 'x-goog-api-client' } xgac.concat(" ",invocation_id_header) if options.add_invocation_id_header header['X-Goog-Api-Client'] = xgac end def set_user_project_header quota_project_id = options.quota_project if !quota_project_id && options&.authorization.respond_to?(:quota_project_id) quota_project_id = options.authorization.quota_project_id end header['X-Goog-User-Project'] = quota_project_id if quota_project_id end def invocation_id_header "gccl-invocation-id/#{SecureRandom.uuid}" end # Attempt to parse a JSON error message # @param [String] body # HTTP response body # @return [Array<(String, String)>] # Error reason and message def parse_error(body) obj = JSON.load(body) error = obj['error'] if error['details'] return extract_v2_error_details(error) elsif error['errors'] return extract_v1_error_details(error) else fail 'Can not parse error message. No "details" or "errors" detected' end rescue return [nil, nil] end # Extracts details from a v1 error message # @param [Hash] error # Parsed JSON # @return [Array<(String, String)>] # Error reason and message def extract_v1_error_details(error) reason = error['errors'].first['reason'] message = error['message'] return [reason, message] end # Extracts details from a v2error message # @param [Hash] error # Parsed JSON # @return [Array<(String, String)>] # Error reason and message def extract_v2_error_details(error) reason = error['status'] message = error['message'] return [reason, message] end # Convert field names from ruby conventions to original names in JSON # # @param [String] fields # Value of 'fields' param # @return [String] # Updated header value def normalize_fields_param(fields) # TODO: Generate map of parameter names during code gen. Small possibility that camelization fails fields.gsub(/:/, '').gsub(/\w+/) do |str| str.gsub(/(?:^|_)([a-z])/){ Regexp.last_match.begin(0) == 0 ? $1 : $1.upcase } end end end end end end google-apis-core-0.11.3/lib/google/apis/core/logging.rb0000644000175100017510000000150614563425620021650 0ustar pravipravi# Copyright 2020 Google LLC # # 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 'google/apis' module Google module Apis module Core # Logging support module Logging # Get the logger instance # @return [Logger] def logger Google::Apis.logger end end end end end google-apis-core-0.11.3/lib/google/apis/core/http_command.rb0000644000175100017510000004264414563425620022707 0ustar pravipravi# Copyright 2020 Google LLC # # 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 'addressable/uri' require 'addressable/template' require 'google/apis/options' require 'google/apis/errors' require 'retriable' require 'google/apis/core/logging' require 'pp' module Google module Apis module Core # Command for HTTP request/response. class HttpCommand include Logging RETRIABLE_ERRORS = [Google::Apis::ServerError, Google::Apis::RateLimitError, Google::Apis::TransmissionError, Google::Apis::RequestTimeOutError] begin require 'opencensus' OPENCENSUS_AVAILABLE = true rescue LoadError OPENCENSUS_AVAILABLE = false end # Request options # @return [Google::Apis::RequestOptions] attr_accessor :options # HTTP request URL # @return [String, Addressable::URI] attr_accessor :url # HTTP headers # @return [Hash] attr_accessor :header # Request body # @return [#read] attr_accessor :body # HTTP method # @return [symbol] attr_accessor :method # HTTP Client # @return [HTTPClient] attr_accessor :connection # Query params # @return [Hash] attr_accessor :query # Path params for URL Template # @return [Hash] attr_accessor :params # @param [symbol] method # HTTP method # @param [String,Addressable::URI, Addressable::Template] url # HTTP URL or template # @param [String, #read] body # Request body def initialize(method, url, body: nil) self.options = Google::Apis::RequestOptions.default.dup self.url = url self.url = Addressable::Template.new(url) if url.is_a?(String) self.method = method self.header = Hash.new self.body = body self.query = {} self.params = {} @opencensus_span = nil if OPENCENSUS_AVAILABLE logger.warn 'OpenCensus support is now deprecated. ' + 'Please refer https://github.com/googleapis/google-api-ruby-client#tracing for migrating to use OpenTelemetry.' end end # Execute the command, retrying as necessary # # @param [HTTPClient] client # HTTP client # @yield [result, err] Result or error if block supplied # @return [Object] # @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried # @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification # @raise [Google::Apis::AuthorizationError] Authorization is required def execute(client, &block) prepare! opencensus_begin_span do_retry :execute_once, client, &block ensure opencensus_end_span @http_res = nil release! end def do_retry func, client begin Retriable.retriable tries: options.retries + 1, max_elapsed_time: options.max_elapsed_time, base_interval: options.base_interval, max_interval: options.max_interval, multiplier: options.multiplier, on: RETRIABLE_ERRORS do |try| # This 2nd level retriable only catches auth errors, and supports 1 retry, which allows # auth to be re-attempted without having to retry all sorts of other failures like # NotFound, etc auth_tries = (try == 1 && authorization_refreshable? ? 2 : 1) Retriable.retriable tries: auth_tries, on: [Google::Apis::AuthorizationError, Signet::AuthorizationError, Signet::RemoteServerError, Signet::UnexpectedStatusError], on_retry: proc { |*| refresh_authorization } do send(func, client).tap do |result| if block_given? yield result, nil end end end end rescue => e if block_given? yield nil, e else raise e end end end # Refresh the authorization authorization after a 401 error # # @private # @return [void] def refresh_authorization # Handled implicitly by auth lib, here in case need to override logger.debug('Retrying after authentication failure') end # Check if attached credentials can be automatically refreshed # @return [Boolean] def authorization_refreshable? options.authorization.respond_to?(:apply!) end # Prepare the request (e.g. calculate headers, add query params, serialize data, etc) before sending # # @private # @return [void] def prepare! normalize_unicode = true if options header.update(options.header) if options.header query.update(options.query) if options.query normalize_unicode = options.normalize_unicode end self.url = url.expand(params, nil, normalize_unicode) if url.is_a?(Addressable::Template) url.query_values = normalize_query_values(query).merge(url.query_values || {}) if allow_form_encoding? @form_encoded = true self.body = Addressable::URI.form_encode(url.query_values(Array)) self.header['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8' else @form_encoded = false end self.body = '' unless self.body end # Release any resources used by this command # @private # @return [void] def release! end # Check the response and either decode body or raise error # # @param [Fixnum] status # HTTP status code of response # @param [Hash] header # Response headers # @param [String, #read] body # Response body # @return [Object] # Response object # @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried # @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification # @raise [Google::Apis::AuthorizationError] Authorization is required def process_response(status, header, body) check_status(status, header, body) decode_response_body(header['Content-Type'].first, body) end # Check the response and raise error if needed # # @param [Fixnum] status # HTTP status code of response # @param [Hash] header # HTTP response headers # @param [String] body # HTTP response body # @param [String] message # Error message text # @return [void] # @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried # @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification # @raise [Google::Apis::AuthorizationError] Authorization is required def check_status(status, header = nil, body = nil, message = nil) # TODO: 304 Not Modified depends on context... case status when 200...300, 308 nil when 301, 302, 303, 307 message ||= sprintf('Redirect to %s', header['Location']) raise Google::Apis::RedirectError.new(message, status_code: status, header: header, body: body) when 401 message ||= 'Unauthorized' raise Google::Apis::AuthorizationError.new(message, status_code: status, header: header, body: body) when 429 message ||= 'Rate limit exceeded' raise Google::Apis::RateLimitError.new(message, status_code: status, header: header, body: body) when 408 message ||= 'Request time out' raise Google::Apis::RequestTimeOutError.new(message, status_code: status, header: header, body: body) when 304, 400, 402...500 message ||= 'Invalid request' raise Google::Apis::ClientError.new(message, status_code: status, header: header, body: body) when 500...600 message ||= 'Server error' raise Google::Apis::ServerError.new(message, status_code: status, header: header, body: body) else logger.warn(sprintf('Encountered unexpected status code %s', status)) message ||= 'Unknown error' raise Google::Apis::TransmissionError.new(message, status_code: status, header: header, body: body) end end # Process the actual response body. Intended to be overridden by subclasses # # @param [String] _content_type # Content type of body # @param [String, #read] body # Response body # @return [Object] def decode_response_body(_content_type, body) body end # Process a success response # @param [Object] result # Result object # @return [Object] result if no block given # @yield [result, nil] if block given def success(result, &block) logger.debug { sprintf('Success - %s', safe_pretty_representation(result)) } block.call(result, nil) if block_given? result end # Process an error response # @param [StandardError] err # Error object # @param [Boolean] rethrow # True if error should be raised again after handling # @return [void] # @yield [nil, err] if block given # @raise [StandardError] if no block def error(err, rethrow: false, &block) logger.debug { sprintf('Error - %s', PP.pp(err, '')) } if err.is_a?(HTTPClient::BadResponseError) begin res = err.res raise Google::Apis::TransmissionError.new(err) if res.nil? check_status(res.status.to_i, res.header, res.body) rescue Google::Apis::Error => e err = e end elsif err.is_a?(HTTPClient::TimeoutError) || err.is_a?(SocketError) || err.is_a?(HTTPClient::KeepAliveDisconnected) || err.is_a?(Errno::ECONNREFUSED) || err.is_a?(Errno::ETIMEDOUT) err = Google::Apis::TransmissionError.new(err) end block.call(nil, err) if block_given? fail err if rethrow || block.nil? end # Execute the command once. # # @private # @param [HTTPClient] client # HTTP client # @return [Object] # @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried # @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification # @raise [Google::Apis::AuthorizationError] Authorization is required def execute_once(client) body.rewind if body.respond_to?(:rewind) begin logger.debug { sprintf('Sending HTTP %s %s', method, url) } request_header = header.dup apply_request_options(request_header) @http_res = client.request(method.to_s.upcase, url.to_s, query: nil, body: body, header: request_header, follow_redirect: true) logger.debug { @http_res.status } logger.debug { safe_single_line_representation @http_res } response = process_response(@http_res.status.to_i, @http_res.header, @http_res.body) success(response) rescue => e logger.debug { sprintf('Caught error %s', e) } error(e, rethrow: true) end end # Update the request with any specified options. # @param [Hash] req_header # HTTP headers # @return [void] def apply_request_options(req_header) if options.authorization.respond_to?(:apply!) options.authorization.apply!(req_header) elsif options.authorization.is_a?(String) req_header['Authorization'] = sprintf('Bearer %s', options.authorization) end req_header.update(header) end def allow_form_encoding? [:post, :put].include?(method) && body.nil? end private UNSAFE_CLASS_NAMES = [ "Google::Apis::CloudkmsV1::DecryptResponse", "Google::Apis::SecretmanagerV1::SecretPayload", "Google::Apis::SecretmanagerV1beta1::SecretPayload" ] module RedactingPPMethods def pp_object obj return super unless UNSAFE_CLASS_NAMES.include? obj.class.name object_address_group obj do text "(fields redacted)" end end end class RedactingPP < PP include RedactingPPMethods end class RedactingSingleLine < PP::SingleLine include RedactingPPMethods end def safe_pretty_representation obj out = "" printer = RedactingPP.new out, 79 printer.guard_inspect_key { printer.pp obj } printer.flush out << "\n" end def safe_single_line_representation obj out = "" printer = RedactingSingleLine.new out printer.guard_inspect_key { printer.pp obj } printer.flush out end def opencensus_begin_span return unless OPENCENSUS_AVAILABLE && options.use_opencensus return if @opencensus_span return unless OpenCensus::Trace.span_context @opencensus_span = OpenCensus::Trace.start_span url.path.to_s @opencensus_span.kind = OpenCensus::Trace::SpanBuilder::CLIENT @opencensus_span.put_attribute "http.host", url.host.to_s @opencensus_span.put_attribute "http.method", method.to_s.upcase @opencensus_span.put_attribute "http.path", url.path.to_s if body.respond_to? :bytesize @opencensus_span.put_message_event \ OpenCensus::Trace::SpanBuilder::SENT, 1, body.bytesize end formatter = OpenCensus::Trace.config.http_formatter if formatter.respond_to? :header_name header[formatter.header_name] = formatter.serialize @opencensus_span.context.trace_context end rescue StandardError => e # Log exceptions and continue, so opencensus failures don't cause # the entire request to fail. logger.debug { sprintf('Error opening OpenCensus span: %s', e) } end def opencensus_end_span return unless OPENCENSUS_AVAILABLE return unless @opencensus_span return unless OpenCensus::Trace.span_context if @http_res if @http_res.body.respond_to? :bytesize @opencensus_span.put_message_event \ OpenCensus::Trace::SpanBuilder::RECEIVED, 1, @http_res.body.bytesize end status = @http_res.status.to_i if status > 0 @opencensus_span.set_status map_http_status status @opencensus_span.put_attribute "http.status_code", status end end OpenCensus::Trace.end_span @opencensus_span @opencensus_span = nil rescue StandardError => e # Log exceptions and continue, so failures don't cause leaks by # aborting cleanup. logger.debug { sprintf('Error finishing OpenCensus span: %s', e) } end def form_encoded? @form_encoded end def map_http_status http_status case http_status when 200..399 then 0 # OK when 400 then 3 # INVALID_ARGUMENT when 401 then 16 # UNAUTHENTICATED when 403 then 7 # PERMISSION_DENIED when 404 then 5 # NOT_FOUND when 429 then 8 # RESOURCE_EXHAUSTED when 501 then 12 # UNIMPLEMENTED when 503 then 14 # UNAVAILABLE when 504 then 4 # DEADLINE_EXCEEDED else 2 # UNKNOWN end end def normalize_query_values(input) input.inject({}) do |h, (k, v)| h[k] = normalize_query_value(v) h end end def normalize_query_value(v) case v when Array v.map { |v2| normalize_query_value(v2) } when nil nil else v.to_s end end end end end end google-apis-core-0.11.3/lib/google/apis/core/json_representation.rb0000644000175100017510000001227414563425620024321 0ustar pravipravi# Copyright 2020 Google LLC # # 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 'representable/json' require 'representable/json/hash' require 'base64' require 'date' module Google module Apis module Core # Support for serializing hashes + property value/nil/unset tracking # To be included in representers as a feature. # @private module JsonRepresentationSupport def self.included(base) base.extend(JsonSupport) end # @private module JsonSupport # Returns a customized getter function for Representable. Allows # indifferent hash/attribute access. # # @param [String] name Property name # @return [Proc] def getter_fn(name) ivar_name = "@#{name}".to_sym lambda do |_| if respond_to?(:fetch) fetch(name, instance_variable_get(ivar_name)) else instance_variable_get(ivar_name) end end end # Returns a customized function for Representable that checks whether or not # an attribute should be serialized. Allows proper patch semantics by distinguishing # between nil & unset values # # @param [String] name Property name # @return [Proc] def if_fn(name) ivar_name = "@#{name}".to_sym lambda do |opts| if opts[:user_options] && opts[:user_options][:skip_undefined] if respond_to?(:key?) self.key?(name) || instance_variable_defined?(ivar_name) else instance_variable_defined?(ivar_name) end else true end end end def set_default_options(name, options) if options[:base64] options[:render_filter] = ->(value, _doc, *_args) { value.nil? ? nil : Base64.urlsafe_encode64(value) } options[:parse_filter] = ->(fragment, _doc, *_args) { Base64.urlsafe_decode64(fragment) } end if options[:numeric_string] options[:render_filter] = ->(value, _doc, *_args) { value.nil? ? nil : value.to_s} options[:parse_filter] = ->(fragment, _doc, *_args) { fragment.to_i } end if options[:type] == DateTime options[:render_filter] = ->(value, _doc, *_args) { value.nil? ? nil : value.is_a?(DateTime) ? value.rfc3339(3) : value.to_s } options[:parse_filter] = ->(fragment, _doc, *_args) { DateTime.parse(fragment) } end if options[:type] == Date options[:render_filter] = ->(value, _doc, *_args) { value.nil? ? nil : value.to_s} options[:parse_filter] = ->(fragment, _doc, *_args) { Date.parse(fragment) } end options[:render_nil] = true options[:getter] = getter_fn(name) options[:if] = if_fn(name) end # Define a single value property # # @param [String] name # Property name # @param [Hash] options def property(name, options = {}) set_default_options(name, options) super(name, options) end # Define a collection property # # @param [String] name # Property name # @param [Hash] options def collection(name, options = {}) set_default_options(name, options) super(name, options) end # Define a hash property # # @param [String] name # Property name # @param [Hash] options def hash(name = nil, options = nil) return super() unless name # Allow Object.hash set_default_options(name, options) super(name, options) end end end # Base decorator for JSON representers # # @see https://github.com/apotonick/representable class JsonRepresentation < Representable::Decorator include Representable::JSON feature JsonRepresentationSupport end module JsonObjectSupport def self.included(base) base.extend(ClassMethods) end module ClassMethods def from_json(json) representation = self.const_get(:Representation) representation.new(self.new).from_json(json, unwrap: self) end end def to_json(*a) representation = self.class.const_get(:Representation) representation.new(self).to_hash(user_options: { skip_undefined: true }).to_json(*a) end end end end end google-apis-core-0.11.3/lib/google/apis/core/upload.rb0000644000175100017510000002462514563425620021515 0ustar pravipravi# Copyright 2020 Google LLC # # 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 'google/apis/core/multipart' require 'google/apis/core/http_command' require 'google/apis/core/api_command' require 'google/apis/errors' require 'addressable/uri' require 'tempfile' require 'mini_mime' module Google module Apis module Core # Base upload command. Not intended to be used directly # @private class BaseUploadCommand < ApiCommand UPLOAD_PROTOCOL_HEADER = 'X-Goog-Upload-Protocol' UPLOAD_CONTENT_TYPE_HEADER = 'X-Goog-Upload-Header-Content-Type' UPLOAD_CONTENT_LENGTH = 'X-Goog-Upload-Header-Content-Length' CONTENT_TYPE_HEADER = 'Content-Type' # File name or IO containing the content to upload # @return [String, File, #read] attr_accessor :upload_source # Content type of the upload material # @return [String] attr_accessor :upload_content_type # Content, as UploadIO # @return [Google::Apis::Core::UploadIO] attr_accessor :upload_io # Ensure the content is readable and wrapped in an IO instance. # # @return [void] # @raise [Google::Apis::ClientError] if upload source is invalid def prepare! super if streamable?(upload_source) self.upload_io = upload_source @close_io_on_finish = false elsif self.upload_source.is_a?(String) self.upload_io = File.new(upload_source, 'r') if self.upload_content_type.nil? type = MiniMime.lookup_by_filename(upload_source) self.upload_content_type = type && type.content_type end @close_io_on_finish = true else fail Google::Apis::ClientError, 'Invalid upload source' end if self.upload_content_type.nil? || self.upload_content_type.empty? self.upload_content_type = 'application/octet-stream' end end # Close IO stream when command done. Only closes the stream if it was opened by the command. def release! upload_io.close if @close_io_on_finish end private def streamable?(upload_source) upload_source.is_a?(IO) || upload_source.is_a?(StringIO) || upload_source.is_a?(Tempfile) end end # Implementation of the raw upload protocol class RawUploadCommand < BaseUploadCommand RAW_PROTOCOL = 'raw' # Ensure the content is readable and wrapped in an {{Google::Apis::Core::UploadIO}} instance. # # @return [void] # @raise [Google::Apis::ClientError] if upload source is invalid def prepare! super self.body = upload_io header[UPLOAD_PROTOCOL_HEADER] = RAW_PROTOCOL header[UPLOAD_CONTENT_TYPE_HEADER] = upload_content_type end end # Implementation of the multipart upload protocol class MultipartUploadCommand < BaseUploadCommand MULTIPART_PROTOCOL = 'multipart' MULTIPART_RELATED = 'multipart/related' # Encode the multipart request # # @return [void] # @raise [Google::Apis::ClientError] if upload source is invalid def prepare! super multipart = Multipart.new multipart.add_json(body) multipart.add_upload(upload_io, content_type: upload_content_type) self.body = multipart.assemble header['Content-Type'] = multipart.content_type header[UPLOAD_PROTOCOL_HEADER] = MULTIPART_PROTOCOL end end # Implementation of the resumable upload protocol class ResumableUploadCommand < BaseUploadCommand UPLOAD_COMMAND_HEADER = 'X-Goog-Upload-Command' UPLOAD_OFFSET_HEADER = 'X-Goog-Upload-Offset' BYTES_RECEIVED_HEADER = 'X-Goog-Upload-Size-Received' UPLOAD_URL_HEADER = 'X-Goog-Upload-URL' UPLOAD_STATUS_HEADER = 'X-Goog-Upload-Status' STATUS_ACTIVE = 'active' STATUS_FINAL = 'final' STATUS_CANCELLED = 'cancelled' RESUMABLE = 'resumable' START_COMMAND = 'start' QUERY_COMMAND = 'query' UPLOAD_COMMAND = 'upload, finalize' # Reset upload to initial state. # # @return [void] # @raise [Google::Apis::ClientError] if upload source is invalid def prepare! @state = :start @upload_url = nil @offset = 0 # Prevent the command from populating the body with form encoding, by # asserting that it already has a body. Form encoding is never used # by upload requests. self.body = '' unless self.body super end # Check the to see if the upload is complete or needs to be resumed. # # @param [Fixnum] status # HTTP status code of response # @param [HTTP::Message::Headers] header # Response headers # @param [String, #read] body # Response body # @return [Object] # Response object # @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried # @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification # @raise [Google::Apis::AuthorizationError] Authorization is required def process_response(status, header, body) @offset = Integer(header[BYTES_RECEIVED_HEADER].first) unless header[BYTES_RECEIVED_HEADER].empty? @upload_url = header[UPLOAD_URL_HEADER].first unless header[UPLOAD_URL_HEADER].empty? upload_status = header[UPLOAD_STATUS_HEADER].first logger.debug { sprintf('Upload status %s', upload_status) } if upload_status == STATUS_ACTIVE @state = :active elsif upload_status == STATUS_FINAL @state = :final elsif upload_status == STATUS_CANCELLED @state = :cancelled fail Google::Apis::ClientError, body end super(status, header, body) end def send_start_command(client) logger.debug { sprintf('Sending upload start command to %s', url) } request_header = header.dup apply_request_options(request_header) request_header[UPLOAD_PROTOCOL_HEADER] = RESUMABLE request_header[UPLOAD_COMMAND_HEADER] = START_COMMAND request_header[UPLOAD_CONTENT_LENGTH] = upload_io.size.to_s request_header[UPLOAD_CONTENT_TYPE_HEADER] = upload_content_type client.request(method.to_s.upcase, url.to_s, query: nil, body: body, header: request_header, follow_redirect: true) rescue => e raise Google::Apis::ServerError, e.message end # Query for the status of an incomplete upload # # @param [HTTPClient] client # HTTP client # @return [HTTP::Message] # @raise [Google::Apis::ServerError] Unable to send the request def send_query_command(client) logger.debug { sprintf('Sending upload query command to %s', @upload_url) } request_header = header.dup apply_request_options(request_header) request_header[UPLOAD_COMMAND_HEADER] = QUERY_COMMAND client.post(@upload_url, body: '', header: request_header, follow_redirect: true) end # Send the actual content # # @param [HTTPClient] client # HTTP client # @return [HTTP::Message] # @raise [Google::Apis::ServerError] Unable to send the request def send_upload_command(client) logger.debug { sprintf('Sending upload command to %s', @upload_url) } content = upload_io content.pos = @offset request_header = header.dup apply_request_options(request_header) request_header[UPLOAD_COMMAND_HEADER] = QUERY_COMMAND request_header[UPLOAD_COMMAND_HEADER] = UPLOAD_COMMAND request_header[UPLOAD_OFFSET_HEADER] = @offset.to_s request_header[CONTENT_TYPE_HEADER] = upload_content_type client.post(@upload_url, body: content, header: request_header, follow_redirect: true) end # Execute the upload request once. This will typically perform two HTTP requests -- one to initiate or query # for the status of the upload, the second to send the (remaining) content. # # @private # @param [HTTPClient] client # HTTP client # @yield [result, err] Result or error if block supplied # @return [Object] # @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried # @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification # @raise [Google::Apis::AuthorizationError] Authorization is required def execute_once(client, &block) case @state when :start response = send_start_command(client) result = process_response(response.status_code, response.header, response.body) when :active response = send_query_command(client) result = process_response(response.status_code, response.header, response.body) when :cancelled, :final error(@last_error, rethrow: true, &block) end if @state == :active response = send_upload_command(client) result = process_response(response.status_code, response.header, response.body) end success(result, &block) if @state == :final rescue => e # Some APIs like Youtube generate non-retriable 401 errors and mark # the upload as finalized. Save the error just in case we get # retried. @last_error = e error(e, rethrow: true, &block) end end end end end google-apis-core-0.11.3/lib/google/apis/core/version.rb0000644000175100017510000000126314563425620021707 0ustar pravipravi# Copyright 2020 Google LLC # # 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 Google module Apis module Core # Core version VERSION = "0.11.3".freeze end end end google-apis-core-0.11.3/lib/google/apis/core/download.rb0000644000175100017510000001040114563425620022023 0ustar pravipravi# Copyright 2020 Google LLC # # 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 'google/apis/core/api_command' require 'google/apis/errors' require 'addressable/uri' module Google module Apis module Core # Streaming/resumable media download support class DownloadCommand < ApiCommand RANGE_HEADER = 'Range' OK_STATUS = [200, 201, 206] # File or IO to write content to # @return [String, File, #write] attr_accessor :download_dest # Ensure the download destination is a writable stream. # # @return [void] def prepare! @state = :start @download_url = nil @offset = 0 if @download_dest.is_a?(Pathname) @download_io = File.open(download_dest, 'wb') @close_io_on_finish = true elsif download_dest.respond_to?(:write) @download_io = download_dest @close_io_on_finish = false elsif download_dest.is_a?(String) @download_io = File.open(download_dest, 'wb') @close_io_on_finish = true else @download_io = StringIO.new('', 'wb') @close_io_on_finish = false end super end # Close IO stream when command done. Only closes the stream if it was opened by the command. def release! @download_io.close if @close_io_on_finish end # Execute the upload request once. Overrides the default implementation to handle streaming/chunking # of file content. # # @private # @param [HTTPClient] client # HTTP client # @yield [result, err] Result or error if block supplied # @return [Object] # @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried # @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification # @raise [Google::Apis::AuthorizationError] Authorization is required def execute_once(client, &block) request_header = header.dup apply_request_options(request_header) download_offset = nil if @offset > 0 logger.debug { sprintf('Resuming download from offset %d', @offset) } request_header[RANGE_HEADER] = sprintf('bytes=%d-', @offset) end http_res = client.get(url.to_s, query: query, header: request_header, follow_redirect: true) do |res, chunk| status = res.http_header.status_code.to_i next unless OK_STATUS.include?(status) download_offset ||= (status == 206 ? @offset : 0) download_offset += chunk.bytesize if download_offset - chunk.bytesize == @offset next_chunk = chunk else # Oh no! Requested a chunk, but received the entire content chunk_index = @offset - (download_offset - chunk.bytesize) next_chunk = chunk.byteslice(chunk_index..-1) next if next_chunk.nil? end # logger.debug { sprintf('Writing chunk (%d bytes, %d total)', chunk.length, bytes_read) } @download_io.write(next_chunk) @offset += next_chunk.bytesize end @download_io.flush if @download_io.respond_to?(:flush) if @close_io_on_finish result = nil else result = @download_io end check_status(http_res.status.to_i, http_res.header, http_res.body) success(result, &block) rescue => e @download_io.flush if @download_io.respond_to?(:flush) error(e, rethrow: true, &block) end end end end end google-apis-core-0.11.3/lib/google/apis/core/hashable.rb0000644000175100017510000000251114563425620021766 0ustar pravipravi# Copyright 2020 Google LLC # # 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 Google module Apis module Core # Adds to_hash to objects module Hashable # Convert object to hash representation # # @return [Hash] def to_h Hash[instance_variables.map { |k| [k[1..-1].to_sym, Hashable.process_value(instance_variable_get(k))] }] end # Recursively serialize an object # # @param [Object] val # @return [Hash] def self.process_value(val) case val when Hash Hash[val.map {|k, v| [k.to_sym, Hashable.process_value(v)] }] when Array val.map{ |v| Hashable.process_value(v) } else val.respond_to?(:to_h) ? val.to_h : val end end end end end end google-apis-core-0.11.3/lib/google/apis/core/storage_download.rb0000644000175100017510000000713014563425620023554 0ustar pravipravi# Copyright 2022 Google LLC # # 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 'google/apis/core/api_command' require 'google/apis/errors' require 'addressable/uri' module Google module Apis module Core # Streaming/resumable media download support specifically for storage API so that # we can respond with response headers too. class StorageDownloadCommand < DownloadCommand # Execute the upload request once. Overrides the default implementation to handle streaming/chunking # of file content. # Note: This method is overriden from DownloadCommand in order to respond back with # http header. All changes made to `execute_once` of DownloadCommand, should be made # here too. # # @private # @param [HTTPClient] client # HTTP client # @yield [result, err] Result or error if block supplied # @return [Object] # @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried # @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification # @raise [Google::Apis::AuthorizationError] Authorization is required def execute_once(client, &block) request_header = header.dup apply_request_options(request_header) download_offset = nil if @offset > 0 logger.debug { sprintf('Resuming download from offset %d', @offset) } request_header[RANGE_HEADER] = sprintf('bytes=%d-', @offset) end http_res = client.get(url.to_s, query: query, header: request_header, follow_redirect: true) do |res, chunk| status = res.http_header.status_code.to_i next unless OK_STATUS.include?(status) download_offset ||= (status == 206 ? @offset : 0) download_offset += chunk.bytesize if download_offset - chunk.bytesize == @offset next_chunk = chunk else # Oh no! Requested a chunk, but received the entire content chunk_index = @offset - (download_offset - chunk.bytesize) next_chunk = chunk.byteslice(chunk_index..-1) next if next_chunk.nil? end # logger.debug { sprintf('Writing chunk (%d bytes, %d total)', chunk.length, bytes_read) } @download_io.write(next_chunk) @offset += next_chunk.bytesize end @download_io.flush if @download_io.respond_to?(:flush) if @close_io_on_finish result = nil else result = @download_io end check_status(http_res.status.to_i, http_res.header, http_res.body) # In case of file download in storage, we need to respond back with http # header along with the actual object. success([result, http_res], &block) rescue => e @download_io.flush if @download_io.respond_to?(:flush) error(e, rethrow: true, &block) end end end end end google-apis-core-0.11.3/lib/google/apis/options.rb0000644000175100017510000001362114563425620020766 0ustar pravipravi# Copyright 2020 Google LLC # # 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 Google module Apis # General options for API requests ClientOptions = Struct.new( :application_name, :application_version, :proxy_url, :open_timeout_sec, :read_timeout_sec, :send_timeout_sec, :log_http_requests, :transparent_gzip_decompression) RequestOptions = Struct.new( :authorization, :retries, :max_elapsed_time, :base_interval, :max_interval, :multiplier, :header, :normalize_unicode, :skip_serialization, :skip_deserialization, :api_format_version, :use_opencensus, :quota_project, :query, :add_invocation_id_header, :upload_chunk_size) # General client options class ClientOptions # @!attribute [rw] application_name # @return [String] Name of the application, for identification in the User-Agent header # @!attribute [rw] application_version # @return [String] Version of the application, for identification in the User-Agent header # @!attribute [rw] proxy_url # @return [String] URL of a proxy server # @!attribute [rw] log_http_requests # @return [Boolean] True if raw HTTP requests should be logged # @!attribute [rw] open_timeout_sec # @return [Integer] How long, in seconds, before failed connections time out # @!attribute [rw] send_timeout_sec # @return [Integer] How long, in seconds, before sending data times out # @!attribute [rw] read_timeout_sec # @return [Integer] How long, in seconds, before receiving data times out # @!attribute [rw] transparent_gzip_decompression # @return [Boolean] True if gzip compression needs to be enabled # Get the default options # @return [Google::Apis::ClientOptions] def self.default @options ||= ClientOptions.new end end # Request options class RequestOptions # @!attribute [rw] authorization # @return [Signet::OAuth2::Client, #apply(Hash)] OAuth2 credentials. # @!attribute [rw] retries # @return [Integer] Number of times to retry requests on server error. # @!attribute [rw] max_elapsed_time # @return [Integer] Total time in seconds that requests are allowed to keep being retried. # @!attribute [rw] base_interval # @return [Float] The initial interval in seconds between tries. # @!attribute [rw] max_interval # @return [Integer] The maximum interval in seconds that any individual retry can reach. # @!attribute [rw] multiplier # @return [Numeric] Each successive interval grows by this factor. A multipler of 1.5 means the next interval # will be 1.5x the current interval. # @!attribute [rw] header # @return [Hash] Additional HTTP headers to include in requests. # @!attribute [rw] normalize_unicode # @return [Boolean] True if unicode strings should be normalized in path parameters. # @!attribute [rw] skip_serialization # @return [Boolean] True if body object should be treated as raw text instead of an object. # @!attribute [rw] skip_deserialization # @return [Boolean] True if response should be returned in raw form instead of deserialized. # @!attribute [rw] api_format_version # @return [Integer] Version of the error format to request/expect. # @!attribute [rw] use_opencensus # @return [Boolean] Whether OpenCensus spans should be generated for requests. Default is true. # @!attribute [rw] quota_project # @return [String] Project ID to charge quota, or `nil` to default to the credentials-specified project. # @!attribute [rw] query # @return [Hash] Additional HTTP URL query parameters to include in requests. # @!attribute [rw] add_invocation_id_header # @return [Boolean] True if the header gccl-invocation-id need to be set # @!attribute [rw] upload_chunk_size # @return [Integer] The chunk size of storage upload. The default value is 100 MB. # Get the default options # @return [Google::Apis::RequestOptions] def self.default @options ||= RequestOptions.new end def merge(options) return self if options.nil? new_options = dup members.each do |opt| opt = opt.to_sym new_options[opt] = options[opt] unless options[opt].nil? end new_options end end ClientOptions.default.log_http_requests = false ClientOptions.default.application_name = 'unknown' ClientOptions.default.application_version = '0.0.0' ClientOptions.default.transparent_gzip_decompression = true RequestOptions.default.retries = 0 RequestOptions.default.max_elapsed_time = 900 RequestOptions.default.base_interval = 1 RequestOptions.default.max_interval = 60 RequestOptions.default.multiplier = 2 RequestOptions.default.normalize_unicode = false RequestOptions.default.skip_serialization = false RequestOptions.default.skip_deserialization = false RequestOptions.default.api_format_version = nil RequestOptions.default.use_opencensus = true RequestOptions.default.quota_project = nil RequestOptions.default.add_invocation_id_header = false RequestOptions.default.upload_chunk_size = 100 * 1024 * 1024 # 100 MB end end google-apis-core-0.11.3/lib/google/apis.rb0000644000175100017510000000445714563425620017302 0ustar pravipravi# Copyright 2020 Google LLC # # 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 'google/apis/core/version' require 'logger' require 'open3' module Google module Apis ROOT = File.expand_path('..', File.dirname(__dir__)) # Current operating system # @private OS_VERSION = begin if RUBY_PLATFORM =~ /mswin|win32|mingw|bccwin|cygwin/ output, _ = Open3.capture2('ver') output.sub(/\s*\[Version\s*/, '/').sub(']', '') elsif RUBY_PLATFORM =~ /darwin/i output, _ = Open3.capture2('sw_vers', '-productVersion') "Mac OS X/#{output}" elsif RUBY_PLATFORM == 'java' require 'java' name = java.lang.System.getProperty('os.name') version = java.lang.System.getProperty('os.version') "#{name}/#{version}" else output, _ = Open3.capture2('uname', '-sr') output.sub(' ', '/') end.strip rescue RUBY_PLATFORM end # @!attribute [rw] logger # @return [Logger] The logger. def self.logger @logger ||= rails_logger || default_logger end class << self attr_writer :logger end private # Create and configure a logger # @return [Logger] def self.default_logger logger = Logger.new($stdout) logger.level = Logger::WARN logger end # Check to see if client is being used in a Rails environment and get the logger if present. # Setting the ENV variable 'GOOGLE_API_USE_RAILS_LOGGER' to false will force the client # to use its own logger. # # @return [Logger] def self.rails_logger if 'true' == ENV.fetch('GOOGLE_API_USE_RAILS_LOGGER', 'true') && defined?(::Rails) && ::Rails.respond_to?(:logger) && !::Rails.logger.nil? ::Rails.logger else nil end end end end google-apis-core-0.11.3/lib/google/api_client/0000755000175100017510000000000014563425620020116 5ustar pravipravigoogle-apis-core-0.11.3/lib/google/api_client/client_secrets.rb0000644000175100017510000001510614563425620023454 0ustar pravipravi# Copyright 2010 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 'json' require 'googleauth' module Google class APIClient ## # Manages the persistence of client configuration data and secrets. Format # inspired by the Google API Python client. # # @see https://developers.google.com/api-client-library/python/guide/aaa_client_secrets # @deprecated Use google-auth-library-ruby instead # @example # { # "web": { # "client_id": "asdfjasdljfasdkjf", # "client_secret": "1912308409123890", # "redirect_uris": ["https://www.example.com/oauth2callback"], # "auth_uri": "https://accounts.google.com/o/oauth2/auth", # "token_uri": "https://accounts.google.com/o/oauth2/token" # } # } # # @example # { # "installed": { # "client_id": "837647042410-75ifg...usercontent.com", # "client_secret":"asdlkfjaskd", # "redirect_uris": ["http://localhost", "urn:ietf:oauth:2.0:oob"], # "auth_uri": "https://accounts.google.com/o/oauth2/auth", # "token_uri": "https://accounts.google.com/o/oauth2/token" # } # } class ClientSecrets ## # Reads client configuration from a file # # @param [String] filename # Path to file to load # # @return [Google::APIClient::ClientSecrets] # OAuth client settings def self.load(filename=nil) if filename && File.directory?(filename) search_path = File.expand_path(filename) filename = nil end while filename == nil search_path ||= File.expand_path('.') if File.exist?(File.join(search_path, 'client_secrets.json')) filename = File.join(search_path, 'client_secrets.json') elsif search_path == File.expand_path('..', search_path) raise ArgumentError, 'No client_secrets.json filename supplied ' + 'and/or could not be found in search path.' else search_path = File.expand_path(File.join(search_path, '..')) end end data = File.open(filename, 'r') { |file| JSON.load(file.read) } return self.new(data) end ## # Initialize OAuth client settings. # # @param [Hash] options # Parsed client secrets files def initialize(options={}) # Client auth configuration @flow = options[:flow] || options.keys.first.to_s || 'web' fdata = options[@flow.to_sym] || options[@flow] @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] || fdata["redirect_uri"]].compact @javascript_origins = ( fdata[:javascript_origins] || fdata["javascript_origins"] ) @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"] @token_credential_uri ||= fdata[:token_credential_uri] # Associated token info @access_token = fdata[:access_token] || fdata["access_token"] @refresh_token = fdata[:refresh_token] || fdata["refresh_token"] @id_token = fdata[:id_token] || fdata["id_token"] @expires_in = fdata[:expires_in] || fdata["expires_in"] @expires_at = fdata[:expires_at] || fdata["expires_at"] @issued_at = fdata[:issued_at] || fdata["issued_at"] end attr_reader( :flow, :client_id, :client_secret, :redirect_uris, :javascript_origins, :authorization_uri, :token_credential_uri, :access_token, :refresh_token, :id_token, :expires_in, :expires_at, :issued_at ) ## # Serialize back to the original JSON form # # @return [String] # JSON def to_json return Json.dump(to_hash) end def to_hash { self.flow => ({ 'client_id' => self.client_id, 'client_secret' => self.client_secret, 'redirect_uris' => self.redirect_uris, 'javascript_origins' => self.javascript_origins, 'auth_uri' => self.authorization_uri, 'token_uri' => self.token_credential_uri, 'access_token' => self.access_token, 'refresh_token' => self.refresh_token, 'id_token' => self.id_token, 'expires_in' => self.expires_in, 'expires_at' => self.expires_at, 'issued_at' => self.issued_at }).inject({}) do |accu, (k, v)| # Prunes empty values from JSON output. unless v == nil || (v.respond_to?(:empty?) && v.empty?) accu[k] = v end accu end } end def to_authorization # NOTE: Do not rely on this default value, as it may change new_authorization = Signet::OAuth2::Client.new new_authorization.client_id = self.client_id new_authorization.client_secret = self.client_secret new_authorization.authorization_uri = ( self.authorization_uri || 'https://accounts.google.com/o/oauth2/auth' ) new_authorization.token_credential_uri = ( self.token_credential_uri || 'https://accounts.google.com/o/oauth2/token' ) new_authorization.redirect_uri = self.redirect_uris.first # These are supported, but unlikely. new_authorization.access_token = self.access_token new_authorization.refresh_token = self.refresh_token new_authorization.id_token = self.id_token new_authorization.expires_in = self.expires_in new_authorization.issued_at = self.issued_at if self.issued_at new_authorization.expires_at = self.expires_at if self.expires_at return new_authorization end end end end google-apis-core-0.11.3/lib/google/api_client/auth/0000755000175100017510000000000014563425620021057 5ustar pravipravigoogle-apis-core-0.11.3/lib/google/api_client/auth/storage.rb0000644000175100017510000000662414563425620023060 0ustar pravipravi# 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 'signet/oauth_2/client' module Google class APIClient ## # Represents cached OAuth 2 tokens stored on local disk in a # JSON serialized file. Meant to resemble the serialized format # http://google-api-python-client.googlecode.com/hg/docs/epy/oauth2client.file.Storage-class.html # # @deprecated Use google-auth-library-ruby instead class Storage AUTHORIZATION_URI = 'https://accounts.google.com/o/oauth2/auth' TOKEN_CREDENTIAL_URI = 'https://accounts.google.com/o/oauth2/token' # @return [Object] Storage object. attr_accessor :store # @return [Signet::OAuth2::Client] attr_reader :authorization ## # Initializes the Storage object. # # @param [Object] store # Storage object def initialize(store) @store= store @authorization = nil end ## # Write the credentials to the specified store. # # @param [Signet::OAuth2::Client] authorization # Optional authorization instance. If not provided, the authorization # already associated with this instance will be written. def write_credentials(authorization=nil) @authorization = authorization if authorization if @authorization.respond_to?(:refresh_token) && @authorization.refresh_token store.write_credentials(credentials_hash) end end ## # Loads credentials and authorizes an client. # @return [Object] Signet::OAuth2::Client or NIL def authorize @authorization = nil cached_credentials = load_credentials if cached_credentials && cached_credentials.size > 0 @authorization = Signet::OAuth2::Client.new(cached_credentials) @authorization.issued_at = Time.at(cached_credentials['issued_at'].to_i) self.refresh_authorization if @authorization.expired? end return @authorization end ## # refresh credentials and save them to store def refresh_authorization authorization.refresh! self.write_credentials end private ## # Attempt to read in credentials from the specified store. def load_credentials store.load_credentials end ## # @return [Hash] with credentials def credentials_hash { :access_token => authorization.access_token, :authorization_uri => AUTHORIZATION_URI, :client_id => authorization.client_id, :client_secret => authorization.client_secret, :expires_in => authorization.expires_in, :refresh_token => authorization.refresh_token, :token_credential_uri => TOKEN_CREDENTIAL_URI, :issued_at => authorization.issued_at.to_i } end end end end google-apis-core-0.11.3/lib/google/api_client/auth/storages/0000755000175100017510000000000014563425620022706 5ustar pravipravigoogle-apis-core-0.11.3/lib/google/api_client/auth/storages/redis_store.rb0000644000175100017510000000324214563425620025556 0ustar pravipravi# 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 'json' module Google class APIClient # @deprecated Use google-auth-library-ruby instead class RedisStore DEFAULT_REDIS_CREDENTIALS_KEY = "google_api_credentials" attr_accessor :redis ## # Initializes the RedisStore object. # # @param [Object] redis # Redis instance # @param [Object] key # Optional key to store credentials under. Defaults to 'google_api_credentials' def initialize(redis, key = nil) @redis= redis @redis_credentials_key = key end ## # Attempt to read in credentials from redis. # @return [Hash] def load_credentials credentials = redis.get redis_credentials_key JSON.parse(credentials) if credentials end def redis_credentials_key @redis_credentials_key || DEFAULT_REDIS_CREDENTIALS_KEY end ## # Write the credentials to redis. # # @param [Hash] credentials_hash def write_credentials(credentials_hash) redis.set(redis_credentials_key, credentials_hash.to_json) end end end end google-apis-core-0.11.3/lib/google/api_client/auth/storages/file_store.rb0000644000175100017510000000313414563425620025367 0ustar pravipravi# 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 'json' module Google class APIClient ## # Represents cached OAuth 2 tokens stored on local disk in a # JSON serialized file. Meant to resemble the serialized format # http://google-api-python-client.googlecode.com/hg/docs/epy/oauth2client.file.Storage-class.html # # @deprecated Use google-auth-library-ruby instead class FileStore attr_accessor :path ## # Initializes the FileStorage object. # # @param [String] path # Path to the credentials file. def initialize(path) @path= path end ## # Attempt to read in credentials from the specified file. def load_credentials open(path, 'r') { |f| JSON.parse(f.read) } rescue nil end ## # Write the credentials to the specified file. # # @param [Hash] credentials_hash def write_credentials(credentials_hash) open(self.path, 'w+') do |f| f.write(credentials_hash.to_json) end end end end end google-apis-core-0.11.3/lib/google/api_client/auth/key_utils.rb0000644000175100017510000000622314563425620023417 0ustar pravipravi# Copyright 2010 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. module Google class APIClient ## # Helper for loading keys from the PKCS12 files downloaded when # setting up service accounts at the APIs Console. # # @deprecated Use google-auth-library-ruby instead module KeyUtils ## # Loads a key from PKCS12 file, assuming a single private key # is present. # # @param [String] keyfile # Path of the PKCS12 file to load. If not a path to an actual file, # assumes the string is the content of the file itself. # @param [String] passphrase # Passphrase for unlocking the private key # # @return [OpenSSL::PKey] The private key for signing assertions. def self.load_from_pkcs12(keyfile, passphrase) load_key(keyfile, passphrase) do |content, pass_phrase| OpenSSL::PKCS12.new(content, pass_phrase).key end end ## # Loads a key from a PEM file. # # @param [String] keyfile # Path of the PEM file to load. If not a path to an actual file, # assumes the string is the content of the file itself. # @param [String] passphrase # Passphrase for unlocking the private key # # @return [OpenSSL::PKey] The private key for signing assertions. # def self.load_from_pem(keyfile, passphrase) load_key(keyfile, passphrase) do | content, pass_phrase| OpenSSL::PKey::RSA.new(content, pass_phrase) end end private ## # Helper for loading keys from file or memory. Accepts a block # to handle the specific file format. # # @param [String] keyfile # Path of thefile to load. If not a path to an actual file, # assumes the string is the content of the file itself. # @param [String] passphrase # Passphrase for unlocking the private key # # @yield [String, String] # Key file & passphrase to extract key from # @yieldparam [String] keyfile # Contents of the file # @yieldparam [String] passphrase # Passphrase to unlock key # @yieldreturn [OpenSSL::PKey] # Private key # # @return [OpenSSL::PKey] The private key for signing assertions. def self.load_key(keyfile, passphrase, &block) begin begin content = File.open(keyfile, 'rb') { |io| io.read } rescue content = keyfile end block.call(content, passphrase) rescue OpenSSL::OpenSSLError raise ArgumentError.new("Invalid keyfile or passphrase") end end end end end google-apis-core-0.11.3/lib/google/api_client/auth/installed_app.rb0000644000175100017510000001135614563425620024231 0ustar pravipravi# Copyright 2010 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 'webrick' launchy_available = begin require 'launchy' true rescue LoadError warn "Attempted to require google/api_client/auth/installed_app.rb when" \ " launchy is not available. The InstalledAppFlow class is disabled." false end module Google class APIClient # Small helper for the sample apps for performing OAuth 2.0 flows from the command # line or in any other installed app environment. # # This class is used in some sample apps and tests but is not really part # of the client libraries, and probably does not belong here. As such, it # is deprecated. If you do choose to use it, note that you must include the # `launchy` gem in your bundle, as it is required by this class but not # listed in the google-api-client gem's requirements. # # @example # # flow = Google::APIClient::InstalledAppFlow.new( # :client_id => '691380668085.apps.googleusercontent.com', # :client_secret => '...', # :scope => 'https://www.googleapis.com/auth/drive' # ) # authorization = flow.authorize # Drive = Google::Apis::DriveV2 # drive = Drive::DriveService.new # drive.authorization = authorization # # @deprecated Use google-auth-library-ruby instead class InstalledAppFlow RESPONSE_BODY = <<-HTML You may close this window. HTML ## # Configure the flow # # @param [Hash] options The configuration parameters for the client. # @option options [Fixnum] :port # Port to run the embedded server on. Defaults to 9292 # @option options [String] :client_id # A unique identifier issued to the client to identify itself to the # authorization server. # @option options [String] :client_secret # A shared symmetric secret issued by the authorization server, # which is used to authenticate the client. # @option options [String] :scope # The scope of the access request, expressed either as an Array # or as a space-delimited String. # # @see Signet::OAuth2::Client def initialize(options) @port = options[:port] || 9292 @authorization = Signet::OAuth2::Client.new({ :authorization_uri => 'https://accounts.google.com/o/oauth2/auth', :token_credential_uri => 'https://accounts.google.com/o/oauth2/token', :redirect_uri => "http://localhost:#{@port}/"}.update(options) ) end ## # Request authorization. Opens a browser and waits for response. # # @param [Google::APIClient::Storage] storage # Optional object that responds to :write_credentials, used to serialize # the OAuth 2 credentials after completing the flow. # # @return [Signet::OAuth2::Client] # Authorization instance, nil if user cancelled. def authorize(storage=nil, options={}) auth = @authorization server = WEBrick::HTTPServer.new( :Port => @port, :BindAddress =>"localhost", :Logger => WEBrick::Log.new(STDOUT, 0), :AccessLog => [] ) begin trap("INT") { server.shutdown } server.mount_proc '/' do |req, res| auth.code = req.query['code'] if auth.code auth.fetch_access_token! end res.status = WEBrick::HTTPStatus::RC_ACCEPTED res.body = RESPONSE_BODY server.stop end Launchy.open(auth.authorization_uri(options).to_s) server.start ensure server.shutdown end if @authorization.access_token if storage.respond_to?(:write_credentials) storage.write_credentials(@authorization) end return @authorization else return nil end end end end end if launchy_available google-apis-core-0.11.3/OVERVIEW.md0000644000175100017510000000260214563425620015555 0ustar pravipravi# Core classes for Google REST Clients This library includes common base classes and dependencies used by legacy REST clients for Google APIs. It is used by client libraries, but you should not need to install it by itself. ## Documentation More detailed descriptions of the Google legacy REST clients are available in two documents. * The [Usage Guide](https://github.com/googleapis/google-api-ruby-client/blob/main/docs/usage-guide.md) discusses how to make API calls, how to use the provided data structures, and how to work the various features of the client library, including media upload and download, error handling, retries, pagination, and logging. * The [Auth Guide](https://github.com/googleapis/google-api-ruby-client/blob/main/docs/auth-guide.md) discusses authentication in the client libraries, including API keys, OAuth 2.0, service accounts, and environment variables. For reference information on specific calls in the clients, see the {Google::Apis class reference docs}. ## License This library is licensed under Apache 2.0. Full license text is available in the {file:LICENSE.md LICENSE}. ## Support Please [report bugs at the project on Github](https://github.com/google/google-api-ruby-client/issues). Don't hesitate to [ask questions](http://stackoverflow.com/questions/tagged/google-api-ruby-client) about the client or APIs on [StackOverflow](http://stackoverflow.com).