search_console_api 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5cd291d9d52be7af01d919c485db008c77e102cc7a11b91cb47bed9da882b1e5
4
+ data.tar.gz: '09f0b97510c12afb3adf85b3969a8526b5b3e15a7ffa4209cc4dc62800617313'
5
+ SHA512:
6
+ metadata.gz: f2c2ae2790058ea42f01533493ab8f6e11c4a9e6f1ac6a1917cb4189317cbe8385e2c07c465303fef3c03f325711ecd1724f5ed07d6c73f8645760c178a01726
7
+ data.tar.gz: 77fc275c283e23aa72a9611d2a82963436dc9e4900bdbd600b32e8f56f714f872f218ef728e36c3eec6913c763e736e80c4540ad1ebb7f904784ecce6677bdb0
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.gem
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at tavo@hey.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "rake", "~> 13.0.6"
6
+ gem "minitest", "~> 5.16.3"
data/Gemfile.lock ADDED
@@ -0,0 +1,21 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ search_console_api (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ minitest (5.16.3)
10
+ rake (13.0.6)
11
+
12
+ PLATFORMS
13
+ arm64-darwin-21
14
+
15
+ DEPENDENCIES
16
+ minitest (~> 5.16.3)
17
+ rake (~> 13.0.6)
18
+ search_console_api!
19
+
20
+ BUNDLED WITH
21
+ 2.3.26
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Gustavo Garcia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # Google Search Console API Ruby Gem [WIP]
2
+ This is a simple wrapper to interact with the Google Search Console API with Ruby.
3
+ It's based on the [Official API reference](https://developers.google.com/webmaster-tools/v1/api_reference_index).
4
+
5
+ The first version is intended for read access only, not adding sites, sitemaps, etc.
6
+
7
+ ## Usage
8
+ Add this gem to your Gemfile:
9
+ ```rb
10
+ gem 'search_console_api'
11
+ ```
12
+
13
+ You will need a way to get a user's valid (and fresh) token (I personally use the `gem omnioauth`).
14
+ Make sure you added the following scope:
15
+
16
+ ```
17
+ https://www.googleapis.com/auth/webmasters.readonly
18
+ ```
19
+
20
+ ### Initialize a Client
21
+ ```rb
22
+ client = SearchConsoleApi::Client.new(token)
23
+ ```
24
+
25
+ ### List all the sites
26
+ ```rb
27
+ # List all the sites
28
+ sites = client.sites
29
+ =>
30
+ [#<SearchConsoleApi::Objects::Site:0x0000000109194e98 @permission_level="siteOwner", @site_url="https://dailytics.com/">,
31
+ #<SearchConsoleApi::Objects::Site:0x0000000109194e48 @permission_level="siteUnverifiedUser", @site_url="http://www.anothersite.com/">,
32
+ #<SearchConsoleApi::Objects::Site:0x0000000109194e20 @permission_level="siteOwner", @site_url="sc-domain:anothersite2.com">,
33
+ #<SearchConsoleApi::Objects::Site:0x0000000109194df8 @permission_level="siteOwner", @site_url="sc-domain:anothersite3.com">]
34
+ my_site = sites[0]
35
+ ```
36
+ The response will be an array of `SearchConsoleApi::Objects::Site` objects, you will need to set one of them as a variable (let's call it `my_site`)
37
+ for the next step.
38
+
39
+ ### Search Analytics API
40
+ The `query` method will receive the `site` parameter plus all the allowed parameters according to the [Google documentation](https://developers.google.com/webmaster-tools/v1/api_reference_index).
41
+ The 2 required parameters are `start_date` and `end_date`.
42
+ You will probably want to add the `dimensions` parameters (like in the second example).
43
+ ```rb
44
+ # a basic query:
45
+ response = client.query(site: my_site, start_date: "2024/03/01", end_date: "2024/04/01")
46
+ => [#<SearchConsoleApi::Objects::QueryResponseRow:0x000000010929d510 @clicks=69, @ctr=0.04542462146148782, @impressions=1519, @keys=nil, @position=15.596445029624753>]
47
+
48
+ # a more compled query:
49
+ response = client.query(site: my_site, start_date: "2024/03/01", end_date: "2024/04/01", dimensions: ["query"], row_limit: 3)
50
+ =>
51
+ [#<SearchConsoleApi::Objects::QueryResponseRow:0x0000000109324c18
52
+ @clicks=7,
53
+ @ctr=0.21212121212121213,
54
+ @impressions=33,
55
+ @keys=["crew release form"],
56
+ @position=2.0606060606060606,
57
+ @query="crew release form">,
58
+ #<SearchConsoleApi::Objects::QueryResponseRow:0x0000000109324b78
59
+ @clicks=3,
60
+ @ctr=0.16666666666666666,
61
+ @impressions=18,
62
+ @keys=["filming notice template"],
63
+ @position=1.5,
64
+ @query="filming notice template">,
65
+ #<SearchConsoleApi::Objects::QueryResponseRow:0x0000000109324ad8
66
+ @clicks=3,
67
+ @ctr=0.10714285714285714,
68
+ @impressions=28,
69
+ @keys=["location agreement template"],
70
+ @position=5.392857142857143,
71
+ @query="location agreement template">]
72
+ ```
73
+
74
+ The response will be an array of `SearchConsoleApi::Objects::QueryResponseRow` objects, representing each row of the original response.
75
+ In case you added the `dimensions` parameter, each `QueryResponseRow` object will have the same dimensions as attributes for a
76
+ simpler manipulation.
77
+
78
+
79
+ ## Development
80
+
81
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
82
+
83
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
84
+
85
+ ## Contributing
86
+
87
+ Bug reports and pull requests are welcome on GitHub at https://github.com/dailytics/search_console_api. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/dailytics/search_console_api/blob/main/CODE_OF_CONDUCT.md).
88
+
89
+
90
+ ## License
91
+
92
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
93
+
94
+ ## Code of Conduct
95
+
96
+ Everyone interacting in the PlausibleApi project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/dailytics/search_console_api/blob/main/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task default: :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "search_console_api"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SearchConsoleApi
4
+ class Client
5
+
6
+ attr_accessor :access_token
7
+
8
+ def initialize(access_token)
9
+ @access_token = access_token
10
+ end
11
+
12
+ def sites
13
+ @sites ||= Resources::Sites::List.new(access_token: @access_token).call
14
+ end
15
+
16
+ def query(site:, start_date:, end_date:, dimensions: [], type: nil, dimension_filter_groups: [], aggregation_type: nil, row_limit: nil, start_row: nil, data_state: nil)
17
+ Resources::SearchAnalytics::Query.new(
18
+ access_token: @access_token, site: site, start_date: start_date, end_date: end_date,
19
+ dimensions: dimensions, type: type, dimension_filter_groups: dimension_filter_groups,
20
+ aggregation_type: aggregation_type, row_limit: row_limit, start_row: start_row,
21
+ data_state: data_state
22
+ ).call
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,4 @@
1
+ module SearchConsoleApi
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -0,0 +1,20 @@
1
+ module SearchConsoleApi
2
+ module Objects
3
+ class QueryResponseRow
4
+ attr_accessor :keys, :clicks, :impressions, :ctr, :position,
5
+ :date, :device, :page, :query, :country, :search_appearance
6
+
7
+ def initialize(attributes = {}, dimensions = [])
8
+ @keys = attributes["keys"]
9
+ @clicks = attributes["clicks"]
10
+ @impressions = attributes["impressions"]
11
+ @ctr = attributes["ctr"]
12
+ @position = attributes["position"]
13
+
14
+ dimensions.each_with_index do |dimension, index|
15
+ instance_variable_set("@#{dimension}", @keys[index])
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "uri"
5
+
6
+ module SearchConsoleApi
7
+ module Objects
8
+ class Site
9
+ attr_accessor :site_url, :permission_level
10
+
11
+ def initialize(attributes = {})
12
+ @site_url = attributes["siteUrl"]
13
+ @permission_level = attributes["permissionLevel"]
14
+ end
15
+
16
+ def encoded_site_url
17
+ URI.encode_www_form_component(@site_url)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "uri"
5
+ require "json"
6
+
7
+ module SearchConsoleApi
8
+ class Request
9
+
10
+ BASE_URL = "https://www.googleapis.com/webmasters/v3"
11
+
12
+ def self.get(access_token:, path:, params: {})
13
+ url = "#{BASE_URL}#{path}"
14
+ url += "?#{URI.encode_www_form params}" unless params.empty?
15
+ uri = URI(url)
16
+
17
+ response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
18
+ request = Net::HTTP::Get.new uri
19
+ request["Authorization"] = "Bearer #{access_token}"
20
+ request["Content-Type"] = "application/json"
21
+
22
+ http.request request
23
+ end
24
+ raise SearchConsoleApi::Error.new(response) unless response.is_a?(Net::HTTPSuccess)
25
+
26
+ JSON.parse response.body
27
+ end
28
+
29
+ def self.post(access_token:, path:, payload: nil)
30
+ url = URI("#{BASE_URL}#{path}")
31
+
32
+ https = Net::HTTP.new(url.host, url.port)
33
+ https.use_ssl = true
34
+
35
+ request = Net::HTTP::Post.new(url)
36
+ request["Authorization"] = "Bearer #{access_token}"
37
+ request["Content-Type"] = "application/javascript"
38
+ request.body = payload
39
+
40
+ response = https.request(request)
41
+ raise SearchConsoleApi::Error.new(response) unless response.is_a?(Net::HTTPSuccess)
42
+
43
+ JSON.parse response.body
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,52 @@
1
+ module SearchConsoleApi
2
+ module Resources
3
+ module SearchAnalytics
4
+ class Query
5
+ def initialize(access_token:, site: nil, start_date: nil, end_date: nil, dimensions: [], type: nil, dimension_filter_groups: [], aggregation_type: nil, row_limit: nil, start_row: nil, data_state: nil)
6
+ @access_token = access_token
7
+ @site = site
8
+ @start_date = start_date
9
+ @end_date = end_date
10
+ @dimensions = dimensions
11
+ @type = type
12
+ @dimension_filter_groups = dimension_filter_groups
13
+ @aggregation_type = aggregation_type
14
+ @row_limit = row_limit
15
+ @start_row = start_row
16
+ @data_state = data_state
17
+ end
18
+
19
+ def call
20
+ response["rows"].each_with_object([]) do |attrs, rows|
21
+ rows << Objects::QueryResponseRow.new(attrs, @dimensions)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def response
28
+ Request.post(
29
+ access_token: @access_token,
30
+ path: "/sites/#{@site.encoded_site_url}/searchAnalytics/query",
31
+ payload: payload
32
+ )
33
+ end
34
+
35
+ def payload
36
+ output = {
37
+ startDate: @start_date,
38
+ endDate: @end_date
39
+ }
40
+ output[:dimensions] = @dimensions if @dimensions.any?
41
+ output[:type] = @type if @type
42
+ output[:dimensionFilterGroups] = @dimension_filter_groups if @dimension_filter_groups.any?
43
+ output[:aggregationType] = @aggregation_type if @aggregation_type
44
+ output[:rowLimit] = @row_limit if @row_limit
45
+ output[:startRow] = @start_row if @start_row
46
+ output[:dataState] = @data_state if @data_state
47
+ output.to_json
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,23 @@
1
+ module SearchConsoleApi
2
+ module Resources
3
+ module Sites
4
+ class List
5
+ def initialize(access_token:)
6
+ @access_token = access_token
7
+ end
8
+
9
+ def call
10
+ response["siteEntry"].each_with_object([]) do |attrs, sites|
11
+ sites << Objects::Site.new(attrs)
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def response
18
+ Request.get(access_token: @access_token, path: "/sites")
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module SearchConsoleApi
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,23 @@
1
+ require "search_console_api/version"
2
+
3
+ module SearchConsoleApi
4
+ autoload :Client, "search_console_api/client"
5
+ autoload :Request, "search_console_api/request"
6
+
7
+ module Objects
8
+ autoload :Site, "search_console_api/objects/site"
9
+ autoload :QueryResponseRow, "search_console_api/objects/query_response_row"
10
+ end
11
+
12
+ module Resources
13
+ module Sites
14
+ autoload :List, "search_console_api/resources/sites/list"
15
+ end
16
+
17
+ module SearchAnalytics
18
+ autoload :Query, "search_console_api/resources/search_analytics/query"
19
+ end
20
+ end
21
+
22
+ autoload :Error, "search_console_api/error"
23
+ end
@@ -0,0 +1,27 @@
1
+ require_relative "lib/search_console_api/version"
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "search_console_api"
5
+ spec.version = SearchConsoleApi::VERSION
6
+ spec.authors = ["Gustavo Garcia"]
7
+ spec.email = ["tavo@hey.com"]
8
+
9
+ spec.summary = "A simple Google Search Console API wrapper for Ruby"
10
+ spec.description = "A very humble wrapper for the Google Search Console API to be used in conjuntion with OAuth2"
11
+ spec.homepage = "https://github.com/dailytics/search_console_api"
12
+ spec.license = "MIT"
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = spec.homepage
17
+ spec.metadata["changelog_uri"] = spec.homepage
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: search_console_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Gustavo Garcia
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-03-10 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A very humble wrapper for the Google Search Console API to be used in
14
+ conjuntion with OAuth2
15
+ email:
16
+ - tavo@hey.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - ".gitignore"
22
+ - CODE_OF_CONDUCT.md
23
+ - Gemfile
24
+ - Gemfile.lock
25
+ - LICENSE.txt
26
+ - README.md
27
+ - Rakefile
28
+ - bin/console
29
+ - bin/setup
30
+ - lib/search_console_api.rb
31
+ - lib/search_console_api/client.rb
32
+ - lib/search_console_api/error.rb
33
+ - lib/search_console_api/objects/query_response_row.rb
34
+ - lib/search_console_api/objects/site.rb
35
+ - lib/search_console_api/request.rb
36
+ - lib/search_console_api/resources/search_analytics/query.rb
37
+ - lib/search_console_api/resources/sites/list.rb
38
+ - lib/search_console_api/version.rb
39
+ - search_console_api.gemspec
40
+ homepage: https://github.com/dailytics/search_console_api
41
+ licenses:
42
+ - MIT
43
+ metadata:
44
+ homepage_uri: https://github.com/dailytics/search_console_api
45
+ source_code_uri: https://github.com/dailytics/search_console_api
46
+ changelog_uri: https://github.com/dailytics/search_console_api
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: 2.3.0
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ requirements: []
62
+ rubygems_version: 3.3.7
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: A simple Google Search Console API wrapper for Ruby
66
+ test_files: []