From 1c7da3553276853cd45e8d907a7c2aedc2fc43de Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Wed, 11 Sep 2024 23:18:26 +1200 Subject: [PATCH 1/9] Modernize gem. --- .editorconfig | 4 + .github/workflows/development.yml | 89 ------------------- .github/workflows/documentation-coverage.yaml | 25 ++++++ .github/workflows/documentation.yaml | 58 ++++++++++++ .github/workflows/rubocop.yaml | 24 +++++ .github/workflows/test-coverage.yaml | 57 ++++++++++++ .github/workflows/test-external.yaml | 36 ++++++++ .github/workflows/test.yaml | 50 +++++++++++ .gitignore | 10 +-- .mailmap | 2 + .rubocop.yml | 53 +++++++++++ cloudflare.gemspec | 19 ++-- gems.rb | 25 ++++-- lib/cloudflare.rb | 34 +++---- lib/cloudflare/accounts.rb | 32 ++----- lib/cloudflare/connection.rb | 50 +++++------ .../custom_hostname/ssl_attribute.rb | 10 ++- .../custom_hostname/ssl_attribute/settings.rb | 10 ++- lib/cloudflare/custom_hostnames.rb | 13 +-- lib/cloudflare/dns.rb | 29 ++---- lib/cloudflare/firewall.rb | 28 ++---- lib/cloudflare/kv/namespaces.rb | 16 ++-- lib/cloudflare/kv/rest_wrapper.rb | 77 ++++++++-------- lib/cloudflare/logs.rb | 27 ++---- lib/cloudflare/paginate.rb | 26 ++---- lib/cloudflare/representation.rb | 33 ++----- lib/cloudflare/rspec/connection.rb | 34 ++----- lib/cloudflare/user.rb | 25 +----- lib/cloudflare/version.rb | 28 ++---- lib/cloudflare/zones.rb | 54 +++++------ license.md | 41 +++++++++ README.md => readme.md | 44 +++------ release.cert | 28 ++++++ spec/cloudflare/accounts_spec.rb | 9 +- .../ssl_attribute/settings_spec.rb | 37 ++++---- .../custom_hostname/ssl_attribute_spec.rb | 31 ++++--- spec/cloudflare/custom_hostnames_spec.rb | 87 +++++++++--------- spec/cloudflare/dns_spec.rb | 7 +- spec/cloudflare/firewall_spec.rb | 22 +++-- spec/cloudflare/kv/namespaces_spec.rb | 19 ++-- spec/cloudflare/zone_spec.rb | 10 ++- spec/spec_helper.rb | 37 ++++---- 42 files changed, 759 insertions(+), 591 deletions(-) delete mode 100644 .github/workflows/development.yml create mode 100644 .github/workflows/documentation-coverage.yaml create mode 100644 .github/workflows/documentation.yaml create mode 100644 .github/workflows/rubocop.yaml create mode 100644 .github/workflows/test-coverage.yaml create mode 100644 .github/workflows/test-external.yaml create mode 100644 .github/workflows/test.yaml create mode 100644 .mailmap create mode 100644 .rubocop.yml create mode 100644 license.md rename README.md => readme.md (58%) create mode 100644 release.cert diff --git a/.editorconfig b/.editorconfig index 538ba2b..a6e7d26 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,3 +3,7 @@ root = true [*] indent_style = tab indent_size = 2 + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml deleted file mode 100644 index ed2a9bf..0000000 --- a/.github/workflows/development.yml +++ /dev/null @@ -1,89 +0,0 @@ -name: Development - -on: [push, pull_request] - -jobs: - test: - runs-on: ${{matrix.os}}-latest - continue-on-error: ${{matrix.experimental}} - - strategy: - matrix: - os: - - ubuntu - - macos - - ruby: - - "2.6" - - "2.7" - - "3.0" - - experimental: [false] - env: [""] - - include: - - os: ubuntu - ruby: truffleruby - experimental: true - - os: ubuntu - ruby: jruby - experimental: true - - os: ubuntu - ruby: head - experimental: true - - steps: - - uses: actions/checkout@v2 - - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{matrix.ruby}} - bundler-cache: true - - - name: Run tests - timeout-minutes: 5 - env: - CLOUDFLARE_TEST_ZONE_MANAGEMENT: true - CLOUDFLARE_EMAIL: ${{secrets.CLOUDFLARE_EMAIL}} - CLOUDFLARE_KEY: ${{secrets.CLOUDFLARE_KEY}} - run: ${{matrix.env}} bundle exec rspec - - test-with-proxy: - runs-on: ${{matrix.os}}-latest - continue-on-error: ${{matrix.experimental}} - - strategy: - matrix: - os: - - ubuntu - ruby: - - 2.7 - - experimental: [false] - env: [""] - - proxy: - - http://localhost:3128 - - steps: - - uses: actions/checkout@v2 - - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{matrix.ruby}} - - - name: Install dependencies - run: ${{matrix.env}} bundle install - - - name: Prepare squid - if: matrix.proxy - run: | - sudo apt-get install squid - sudo systemctl start squid - - - name: Run tests - timeout-minutes: 5 - env: - CLOUDFLARE_TEST_ZONE_MANAGEMENT: true - CLOUDFLARE_PROXY: ${{matrix.proxy}} - CLOUDFLARE_EMAIL: ${{secrets.CLOUDFLARE_EMAIL}} - CLOUDFLARE_KEY: ${{secrets.CLOUDFLARE_KEY}} - run: ${{matrix.env}} bundle exec rspec diff --git a/.github/workflows/documentation-coverage.yaml b/.github/workflows/documentation-coverage.yaml new file mode 100644 index 0000000..b3bac9a --- /dev/null +++ b/.github/workflows/documentation-coverage.yaml @@ -0,0 +1,25 @@ +name: Documentation Coverage + +on: [push, pull_request] + +permissions: + contents: read + +env: + CONSOLE_OUTPUT: XTerm + COVERAGE: PartialSummary + +jobs: + validate: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.3" + bundler-cache: true + + - name: Validate coverage + timeout-minutes: 5 + run: bundle exec bake decode:index:coverage lib diff --git a/.github/workflows/documentation.yaml b/.github/workflows/documentation.yaml new file mode 100644 index 0000000..f5f553a --- /dev/null +++ b/.github/workflows/documentation.yaml @@ -0,0 +1,58 @@ +name: Documentation + +on: + push: + branches: + - main + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages: +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment: +concurrency: + group: "pages" + cancel-in-progress: true + +env: + CONSOLE_OUTPUT: XTerm + BUNDLE_WITH: maintenance + +jobs: + generate: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.3" + bundler-cache: true + + - name: Installing packages + run: sudo apt-get install wget + + - name: Generate documentation + timeout-minutes: 5 + run: bundle exec bake utopia:project:static --force no + + - name: Upload documentation artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs + + deploy: + runs-on: ubuntu-latest + + environment: + name: github-pages + url: ${{steps.deployment.outputs.page_url}} + + needs: generate + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/rubocop.yaml b/.github/workflows/rubocop.yaml new file mode 100644 index 0000000..287c06d --- /dev/null +++ b/.github/workflows/rubocop.yaml @@ -0,0 +1,24 @@ +name: RuboCop + +on: [push, pull_request] + +permissions: + contents: read + +env: + CONSOLE_OUTPUT: XTerm + +jobs: + check: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ruby + bundler-cache: true + + - name: Run RuboCop + timeout-minutes: 10 + run: bundle exec rubocop diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml new file mode 100644 index 0000000..ffa0927 --- /dev/null +++ b/.github/workflows/test-coverage.yaml @@ -0,0 +1,57 @@ +name: Test Coverage + +on: [push, pull_request] + +permissions: + contents: read + +env: + CONSOLE_OUTPUT: XTerm + COVERAGE: PartialSummary + +jobs: + test: + name: ${{matrix.ruby}} on ${{matrix.os}} + runs-on: ${{matrix.os}}-latest + + strategy: + matrix: + os: + - ubuntu + - macos + + ruby: + - "3.3" + + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{matrix.ruby}} + bundler-cache: true + + - name: Run tests + timeout-minutes: 5 + run: bundle exec bake test + + - uses: actions/upload-artifact@v4 + with: + name: coverage-${{matrix.os}}-${{matrix.ruby}} + path: .covered.db + + validate: + needs: test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.3" + bundler-cache: true + + - uses: actions/download-artifact@v4 + + - name: Validate coverage + timeout-minutes: 5 + run: bundle exec bake covered:validate --paths */.covered.db \; diff --git a/.github/workflows/test-external.yaml b/.github/workflows/test-external.yaml new file mode 100644 index 0000000..21898f5 --- /dev/null +++ b/.github/workflows/test-external.yaml @@ -0,0 +1,36 @@ +name: Test External + +on: [push, pull_request] + +permissions: + contents: read + +env: + CONSOLE_OUTPUT: XTerm + +jobs: + test: + name: ${{matrix.ruby}} on ${{matrix.os}} + runs-on: ${{matrix.os}}-latest + + strategy: + matrix: + os: + - ubuntu + - macos + + ruby: + - "3.1" + - "3.2" + - "3.3" + + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{matrix.ruby}} + bundler-cache: true + + - name: Run tests + timeout-minutes: 10 + run: bundle exec bake test:external diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..0769a98 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,50 @@ +name: Test + +on: [push, pull_request] + +permissions: + contents: read + +env: + CONSOLE_OUTPUT: XTerm + +jobs: + test: + name: ${{matrix.ruby}} on ${{matrix.os}} + runs-on: ${{matrix.os}}-latest + continue-on-error: ${{matrix.experimental}} + + strategy: + matrix: + os: + - ubuntu + - macos + + ruby: + - "3.1" + - "3.2" + - "3.3" + + experimental: [false] + + include: + - os: ubuntu + ruby: truffleruby + experimental: true + - os: ubuntu + ruby: jruby + experimental: true + - os: ubuntu + ruby: head + experimental: true + + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{matrix.ruby}} + bundler-cache: true + + - name: Run tests + timeout-minutes: 10 + run: bundle exec bake test diff --git a/.gitignore b/.gitignore index 88ac4bc..5bc8957 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,8 @@ /.bundle/ -/.yardoc -/gems.locked -/_yardoc/ -/coverage/ -/doc/ /pkg/ -/spec/reports/ -/tmp/ +/gems.locked +/.covered.db +/external # rspec failure tracking .rspec_status diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..8da6766 --- /dev/null +++ b/.mailmap @@ -0,0 +1,2 @@ +Denis Sadomowski +莫粒 diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..3b8d476 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,53 @@ +AllCops: + DisabledByDefault: true + +Layout/IndentationStyle: + Enabled: true + EnforcedStyle: tabs + +Layout/InitialIndentation: + Enabled: true + +Layout/IndentationWidth: + Enabled: true + Width: 1 + +Layout/IndentationConsistency: + Enabled: true + EnforcedStyle: normal + +Layout/BlockAlignment: + Enabled: true + +Layout/EndAlignment: + Enabled: true + EnforcedStyleAlignWith: start_of_line + +Layout/BeginEndAlignment: + Enabled: true + EnforcedStyleAlignWith: start_of_line + +Layout/ElseAlignment: + Enabled: true + +Layout/DefEndAlignment: + Enabled: true + +Layout/CaseIndentation: + Enabled: true + +Layout/CommentIndentation: + Enabled: true + +Layout/EmptyLinesAroundClassBody: + Enabled: true + +Layout/EmptyLinesAroundModuleBody: + Enabled: true + +Style/FrozenStringLiteralComment: + Enabled: true + +Style/StringLiterals: + Enabled: true + EnforcedStyle: double_quotes diff --git a/cloudflare.gemspec b/cloudflare.gemspec index d52acc3..1dbf0b4 100644 --- a/cloudflare.gemspec +++ b/cloudflare.gemspec @@ -1,3 +1,4 @@ +# frozen_string_literal: true require_relative "lib/cloudflare/version" @@ -6,19 +7,21 @@ Gem::Specification.new do |spec| spec.version = Cloudflare::VERSION spec.summary = "A Ruby wrapper for the Cloudflare API." - spec.authors = ["Marcin Prokop", "Samuel Williams"] + spec.authors = ["Samuel Williams", "Marcin Prokop", "Leonhardt Wille", "Rob Widmer", "Akinori MUSHA", "Sherman K", "Michael Kalygin", "Denis Sadomowski", "Nazar", "emckay", "Casey Lopez", "David Wegman", "Greg Retkowski", "Guillaume Leseur", "Jason Green", "Kugayama Nana", "Kyle Corbitt", "Mike Perham", "Olle Jonsson", "Terry Kerr", "莫粒"] spec.license = "MIT" + spec.cert_chain = ["release.cert"] + spec.signing_key = File.expand_path("~/.gem/release.pem") + spec.homepage = "https://github.com/socketry/cloudflare" - spec.files = Dir.glob('{lib}/**/*', File::FNM_DOTMATCH, base: __dir__) + spec.metadata = { + "source_code_uri" => "https://github.com/socketry/cloudflare.git", + } - spec.required_ruby_version = ">= 2.5" + spec.files = Dir.glob(["{lib}/**/*", "*.md"], File::FNM_DOTMATCH, base: __dir__) - spec.add_dependency "async-rest", "~> 0.12.3" + spec.required_ruby_version = ">= 3.1" - spec.add_development_dependency "async-rspec" - spec.add_development_dependency "bundler" - spec.add_development_dependency "covered" - spec.add_development_dependency "rspec", "~> 3.6" + spec.add_dependency "async-rest", "~> 0.12" end diff --git a/gems.rb b/gems.rb index 68877f6..8a43a13 100644 --- a/gems.rb +++ b/gems.rb @@ -1,22 +1,31 @@ # frozen_string_literal: true -source 'https://rubygems.org' +# Released under the MIT License. +# Copyright, 2014-2020, by Samuel Williams. +# Copyright, 2014, by Marcin Prokop. +# Copyright, 2018, by Leonhardt Wille. + +source "https://rubygems.org" # Specify your gem's dependencies in cloudflare.gemspec gemspec -gem 'async-http', '~> 0.48', '>= 0.48.2' - group :maintenance, optional: true do - gem "bake-bundler" + gem "bake-gem" gem "bake-modernize" gem "utopia-project" end group :test do - gem 'coveralls', require: false - gem 'simplecov' - gem 'sinatra' - gem 'webmock' + gem "rspec" + gem "decode" + gem "rubocop" + + gem "async-rspec" + + gem "coveralls", require: false + gem "simplecov" + gem "sinatra" + gem "webmock" end diff --git a/lib/cloudflare.rb b/lib/cloudflare.rb index 8b44929..47a2c71 100644 --- a/lib/cloudflare.rb +++ b/lib/cloudflare.rb @@ -1,31 +1,19 @@ # frozen_string_literal: true -# Copyright, 2012, by Marcin Prokop. -# Copyright, 2017, by Samuel G. D. Williams. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. +# Released under the MIT License. +# Copyright, 2012-2016, by Marcin Prokop. +# Copyright, 2013, by emckay. +# Copyright, 2014, by Jason Green. +# Copyright, 2014-2019, by Samuel Williams. +# Copyright, 2014, by Greg Retkowski. +# Copyright, 2018, by Leonhardt Wille. +# Copyright, 2019, by Akinori MUSHA. -require 'async' -require_relative 'cloudflare/connection' +require "async" +require_relative "cloudflare/connection" module Cloudflare - DEFAULT_ENDPOINT = Async::HTTP::Endpoint.parse('https://api.cloudflare.com/client/v4') + DEFAULT_ENDPOINT = Async::HTTP::Endpoint.parse("https://api.cloudflare.com/client/v4/") def self.connect(endpoint = DEFAULT_ENDPOINT, **auth_info) representation = Connection.for(endpoint) diff --git a/lib/cloudflare/accounts.rb b/lib/cloudflare/accounts.rb index b3f23a6..e552199 100644 --- a/lib/cloudflare/accounts.rb +++ b/lib/cloudflare/accounts.rb @@ -1,30 +1,12 @@ # frozen_string_literal: true -# Copyright, 2012, by Marcin Prokop. -# Copyright, 2017, by Samuel G. D. Williams. -# Copyright, 2017, by David Rosenbloom. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. +# Released under the MIT License. +# Copyright, 2018-2019, by Samuel Williams. +# Copyright, 2019, by Rob Widmer. -require_relative 'representation' -require_relative 'paginate' -require_relative 'kv/namespaces' +require_relative "representation" +require_relative "paginate" +require_relative "kv/namespaces" module Cloudflare class Account < Representation @@ -33,7 +15,7 @@ def id end def kv_namespaces - self.with(KV::Namespaces, path: 'storage/kv/namespaces') + self.with(KV::Namespaces, path: "storage/kv/namespaces") end end diff --git a/lib/cloudflare/connection.rb b/lib/cloudflare/connection.rb index 86e9ad2..d39c02f 100644 --- a/lib/cloudflare/connection.rb +++ b/lib/cloudflare/connection.rb @@ -1,30 +1,20 @@ # frozen_string_literal: true -# Copyright, 2018, by Samuel G. D. Williams. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. +# Released under the MIT License. +# Copyright, 2014-2016, by Marcin Prokop. +# Copyright, 2014-2019, by Samuel Williams. +# Copyright, 2015, by Kyle Corbitt. +# Copyright, 2015, by Guillaume Leseur. +# Copyright, 2018, by Leonhardt Wille. +# Copyright, 2018, by Michael Kalygin. +# Copyright, 2018, by Sherman K. +# Copyright, 2019, by Akinori MUSHA. -require_relative 'representation' +require_relative "representation" -require_relative 'zones' -require_relative 'accounts' -require_relative 'user' +require_relative "zones" +require_relative "accounts" +require_relative "user" module Cloudflare class Connection < Representation @@ -32,13 +22,13 @@ def authenticated(token: nil, key: nil, email: nil) headers = {} if token - headers['Authorization'] = "Bearer #{token}" + headers["Authorization"] = "Bearer #{token}" elsif key if email - headers['X-Auth-Key'] = key - headers['X-Auth-Email'] = email + headers["X-Auth-Key"] = key + headers["X-Auth-Email"] = email else - headers['X-Auth-User-Service-Key'] = key + headers["X-Auth-User-Service-Key"] = key end end @@ -46,15 +36,15 @@ def authenticated(token: nil, key: nil, email: nil) end def zones - self.with(Zones, path: 'zones') + self.with(Zones, path: "zones/") end def accounts - self.with(Accounts, path: 'accounts') + self.with(Accounts, path: "accounts") end def user - self.with(User, path: 'user') + self.with(User, path: "user") end end end diff --git a/lib/cloudflare/custom_hostname/ssl_attribute.rb b/lib/cloudflare/custom_hostname/ssl_attribute.rb index 87ff188..a60ce6c 100644 --- a/lib/cloudflare/custom_hostname/ssl_attribute.rb +++ b/lib/cloudflare/custom_hostname/ssl_attribute.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true -require_relative './ssl_attribute/settings' +# Released under the MIT License. +# Copyright, 2019, by Rob Widmer. +# Copyright, 2019, by Samuel Williams. + +require_relative "./ssl_attribute/settings" module Cloudflare class CustomHostname < Representation @@ -10,7 +14,7 @@ def initialize(params) end def active? - status == 'active' + status == "active" end def cname @@ -34,7 +38,7 @@ def method end def pending_validation? - status == 'pending_validation' + status == "pending_validation" end # Wraps the settings hash if it exists or initializes the settings hash and then wraps it diff --git a/lib/cloudflare/custom_hostname/ssl_attribute/settings.rb b/lib/cloudflare/custom_hostname/ssl_attribute/settings.rb index 53824a8..d3db84f 100644 --- a/lib/cloudflare/custom_hostname/ssl_attribute/settings.rb +++ b/lib/cloudflare/custom_hostname/ssl_attribute/settings.rb @@ -1,5 +1,9 @@ # frozen_string_literal: true +# Released under the MIT License. +# Copyright, 2019, by Rob Widmer. +# Copyright, 2019, by Samuel Williams. + module Cloudflare class CustomHostname < Representation class SSLAttribute @@ -25,7 +29,7 @@ def http2 # Always coerce into a boolean, if the key is not # provided, this value may not be accurate def http2? - http2 == 'on' + http2 == "on" end def http2=(value) @@ -49,7 +53,7 @@ def tls_1_3 # Always coerce into a boolean, if the key is not # provided, this value may not be accurate def tls_1_3? - tls_1_3 == 'on' + tls_1_3 == "on" end def tls_1_3=(value) @@ -62,7 +66,7 @@ def process_boolean(key, value) if value.nil? @settings.delete(key) else - @settings[key] = !value || value == 'off' ? 'off' : 'on' + @settings[key] = !value || value == "off" ? "off" : "on" end end end diff --git a/lib/cloudflare/custom_hostnames.rb b/lib/cloudflare/custom_hostnames.rb index 3ad550b..9081927 100644 --- a/lib/cloudflare/custom_hostnames.rb +++ b/lib/cloudflare/custom_hostnames.rb @@ -1,11 +1,12 @@ # frozen_string_literal: true -# This implements the Custom Hostname API -# https://api.cloudflare.com/#custom-hostname-for-a-zone-properties +# Released under the MIT License. +# Copyright, 2019, by Rob Widmer. +# Copyright, 2019, by Samuel Williams. -require_relative 'custom_hostname/ssl_attribute' -require_relative 'paginate' -require_relative 'representation' +require_relative "custom_hostname/ssl_attribute" +require_relative "paginate" +require_relative "representation" module Cloudflare class CustomHostname < Representation @@ -68,7 +69,7 @@ def representation # initializes a custom hostname object and yields it for customization before saving def create(hostname, metadata: nil, origin: nil, ssl: {}, &block) - attrs = { hostname: hostname, ssl: { method: 'http', type: 'dv' }.merge(ssl) } + attrs = { hostname: hostname, ssl: { method: "http", type: "dv" }.merge(ssl) } attrs[:custom_metadata] = metadata if metadata attrs[:custom_origin_server] = origin if origin diff --git a/lib/cloudflare/dns.rb b/lib/cloudflare/dns.rb index 0c4567c..4f19d9f 100644 --- a/lib/cloudflare/dns.rb +++ b/lib/cloudflare/dns.rb @@ -1,29 +1,12 @@ # frozen_string_literal: true -# Copyright, 2012, by Marcin Prokop. -# Copyright, 2017, by Samuel G. D. Williams. -# Copyright, 2017, by David Rosenbloom. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. +# Released under the MIT License. +# Copyright, 2019, by Samuel Williams. +# Copyright, 2019, by Rob Widmer. +# Copyright, 2019, by David Wegman. -require_relative 'representation' -require_relative 'paginate' +require_relative "representation" +require_relative "paginate" module Cloudflare module DNS diff --git a/lib/cloudflare/firewall.rb b/lib/cloudflare/firewall.rb index 40ba423..d156736 100644 --- a/lib/cloudflare/firewall.rb +++ b/lib/cloudflare/firewall.rb @@ -1,27 +1,11 @@ # frozen_string_literal: true -# Copyright, 2018, by Samuel G. D. Williams. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. +# Released under the MIT License. +# Copyright, 2019, by Samuel Williams. +# Copyright, 2019, by Rob Widmer. -require_relative 'representation' -require_relative 'paginate' +require_relative "representation" +require_relative "paginate" module Cloudflare module Firewall @@ -50,7 +34,7 @@ def representation Rule end - def set(mode, value, notes: nil, target: 'ip') + def set(mode, value, notes: nil, target: "ip") notes ||= "cloudflare gem [#{mode}] #{Time.now.strftime('%m/%d/%y')}" message = self.post({ diff --git a/lib/cloudflare/kv/namespaces.rb b/lib/cloudflare/kv/namespaces.rb index 059e737..14ad2e5 100644 --- a/lib/cloudflare/kv/namespaces.rb +++ b/lib/cloudflare/kv/namespaces.rb @@ -1,11 +1,13 @@ # frozen_string_literal: true -# This implements the Worker KV Store API -# https://api.cloudflare.com/#workers-kv-namespace-properties +# Released under the MIT License. +# Copyright, 2019, by Rob Widmer. +# Copyright, 2019, by Samuel Williams. +# Copyright, 2021, by Terry Kerr. -require_relative '../paginate' -require_relative '../representation' -require_relative 'rest_wrapper' +require_relative "../paginate" +require_relative "../representation" +require_relative "rest_wrapper" module Cloudflare module KV @@ -33,7 +35,7 @@ def id end def keys - self.with(Keys, path: 'keys') + self.with(Keys, path: "keys/") end def read_value(name) @@ -57,7 +59,7 @@ def write_value(name, value) def value_representation(name) @representation_class ||= Representation[RESTWrapper] - self.with(@representation_class, path: "values/#{name}") + self.with(@representation_class, path: "values/#{name}/") end end diff --git a/lib/cloudflare/kv/rest_wrapper.rb b/lib/cloudflare/kv/rest_wrapper.rb index bccc44e..7508582 100644 --- a/lib/cloudflare/kv/rest_wrapper.rb +++ b/lib/cloudflare/kv/rest_wrapper.rb @@ -1,44 +1,47 @@ # frozen_string_literal: true -require 'json' +# Released under the MIT License. +# Copyright, 2021, by Terry Kerr. + +require "json" module Cloudflare - module KV - class RESTWrapper < Async::REST::Wrapper::Generic - APPLICATION_OCTET_STREAM = 'application/octet-stream' - APPLICATION_JSON = 'application/json' + module KV + class RESTWrapper < Async::REST::Wrapper::Generic + APPLICATION_OCTET_STREAM = "application/octet-stream" + APPLICATION_JSON = "application/json" ACCEPT_HEADER = "#{APPLICATION_JSON}, #{APPLICATION_OCTET_STREAM}" - def prepare_request(payload, headers) - headers['accept'] ||= ACCEPT_HEADER - - if payload - headers['content-type'] = APPLICATION_OCTET_STREAM - ::Protocol::HTTP::Body::Buffered.new([payload.to_s]) - end - end - - def parser_for(response) - if response.headers['content-type'].start_with?(APPLICATION_OCTET_STREAM) - OctetParser - elsif response.headers['content-type'].start_with?(APPLICATION_JSON) - JsonParser - else - Async::REST::Wrapper::Generic::Unsupported - end - end - - class OctetParser < ::Protocol::HTTP::Body::Wrapper - def join - super - end - end - - class JsonParser < ::Protocol::HTTP::Body::Wrapper - def join - JSON.parse(super, symbolize_names: true) - end - end - end - end + def prepare_request(payload, headers) + headers["accept"] ||= ACCEPT_HEADER + + if payload + headers["content-type"] = APPLICATION_OCTET_STREAM + ::Protocol::HTTP::Body::Buffered.new([payload.to_s]) + end + end + + def parser_for(response) + if response.headers["content-type"].start_with?(APPLICATION_OCTET_STREAM) + OctetParser + elsif response.headers["content-type"].start_with?(APPLICATION_JSON) + JsonParser + else + Async::REST::Wrapper::Generic::Unsupported + end + end + + class OctetParser < ::Protocol::HTTP::Body::Wrapper + def join + super + end + end + + class JsonParser < ::Protocol::HTTP::Body::Wrapper + def join + JSON.parse(super, symbolize_names: true) + end + end + end + end end diff --git a/lib/cloudflare/logs.rb b/lib/cloudflare/logs.rb index 2ec4161..e3ece94 100644 --- a/lib/cloudflare/logs.rb +++ b/lib/cloudflare/logs.rb @@ -1,25 +1,10 @@ -# Copyright, 2019, by Samuel G. D. Williams. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. +# frozen_string_literal: true -require_relative 'representation' -require_relative 'paginate' +# Released under the MIT License. +# Copyright, 2019, by Samuel Williams. + +require_relative "representation" +require_relative "paginate" module Cloudflare module Logs diff --git a/lib/cloudflare/paginate.rb b/lib/cloudflare/paginate.rb index e2d087f..afdcf79 100644 --- a/lib/cloudflare/paginate.rb +++ b/lib/cloudflare/paginate.rb @@ -1,22 +1,8 @@ -# Copyright, 2018, by Samuel G. D. Williams. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2018-2019, by Samuel Williams. +# Copyright, 2019, by Rob Widmer. module Cloudflare module Paginate @@ -46,7 +32,7 @@ def empty? end def find_by_id(id) - representation.new(@resource.with(path: id)) + representation.new(@resource.with(path: "#{id}/")) end end end diff --git a/lib/cloudflare/representation.rb b/lib/cloudflare/representation.rb index c64f317..f13f094 100644 --- a/lib/cloudflare/representation.rb +++ b/lib/cloudflare/representation.rb @@ -1,29 +1,13 @@ # frozen_string_literal: true -# Copyright, 2012, by Marcin Prokop. -# Copyright, 2017, by Samuel G. D. Williams. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -require 'json' - -require 'async/rest/representation' +# Released under the MIT License. +# Copyright, 2017-2018, by Samuel Williams. +# Copyright, 2018, by Leonhardt Wille. +# Copyright, 2019, by Rob Widmer. + +require "json" + +require "async/rest/representation" module Cloudflare class RequestError < StandardError @@ -94,6 +78,7 @@ def representation def represent(metadata, attributes) resource = @resource.with(path: attributes[:id]) + binding.irb representation.new(resource, metadata: metadata, value: attributes) end diff --git a/lib/cloudflare/rspec/connection.rb b/lib/cloudflare/rspec/connection.rb index 506b767..0c0dae6 100644 --- a/lib/cloudflare/rspec/connection.rb +++ b/lib/cloudflare/rspec/connection.rb @@ -1,29 +1,13 @@ # frozen_string_literal: true -# Copyright, 2017, by Samuel G. D. Williams. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. +# Released under the MIT License. +# Copyright, 2017-2020, by Samuel Williams. +# Copyright, 2018, by Leonhardt Wille. -require 'async/rspec' -require 'async/http/proxy' +require "async/rspec" +require "async/http/proxy" -require_relative '../../cloudflare' +require_relative "../../cloudflare" module Cloudflare module RSpec @@ -34,11 +18,11 @@ module Connection include_context Async::RSpec::Reactor # You must specify these in order for the tests to run. - let(:email) {ENV['CLOUDFLARE_EMAIL']} - let(:key) {ENV['CLOUDFLARE_KEY']} + let(:email) {ENV["CLOUDFLARE_EMAIL"]} + let(:key) {ENV["CLOUDFLARE_KEY"]} let(:connection) do - if proxy_url = ENV['CLOUDFLARE_PROXY'] + if proxy_url = ENV["CLOUDFLARE_PROXY"] proxy_endpoint = Async::HTTP::Endpoint.parse(proxy_url) @client = Async::HTTP::Client.new(proxy_endpoint) @connection = Cloudflare.connect(@client.proxied_endpoint(DEFAULT_ENDPOINT), key: key, email: email) diff --git a/lib/cloudflare/user.rb b/lib/cloudflare/user.rb index 82d0543..5405989 100644 --- a/lib/cloudflare/user.rb +++ b/lib/cloudflare/user.rb @@ -1,27 +1,10 @@ # frozen_string_literal: true -# Copyright, 2012, by Marcin Prokop. -# Copyright, 2017, by Samuel G. D. Williams. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. +# Released under the MIT License. +# Copyright, 2017-2019, by Samuel Williams. +# Copyright, 2018, by Leonhardt Wille. -require_relative 'representation' +require_relative "representation" module Cloudflare class User < Representation diff --git a/lib/cloudflare/version.rb b/lib/cloudflare/version.rb index 59c0a6e..edd5353 100644 --- a/lib/cloudflare/version.rb +++ b/lib/cloudflare/version.rb @@ -1,26 +1,12 @@ # frozen_string_literal: true -# Copyright, 2012, by Marcin Prokop. -# Copyright, 2017, by Samuel G. D. Williams. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. +# Released under the MIT License. +# Copyright, 2014-2016, by Marcin Prokop. +# Copyright, 2014-2021, by Samuel Williams. +# Copyright, 2015, by Kyle Corbitt. +# Copyright, 2018, by Leonhardt Wille. +# Copyright, 2018, by Casey Lopez. module Cloudflare - VERSION = '4.3.0' + VERSION = "4.3.0" end diff --git a/lib/cloudflare/zones.rb b/lib/cloudflare/zones.rb index 69a4e5d..8068439 100644 --- a/lib/cloudflare/zones.rb +++ b/lib/cloudflare/zones.rb @@ -1,51 +1,41 @@ # frozen_string_literal: true -# Copyright, 2012, by Marcin Prokop. -# Copyright, 2017, by Samuel G. D. Williams. -# Copyright, 2017, by David Rosenbloom. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. +# Released under the MIT License. +# Copyright, 2017-2019, by Samuel Williams. +# Copyright, 2017, by Denis Sadomowski. +# Copyright, 2017, by 莫粒. +# Copyright, 2018, by Leonhardt Wille. +# Copyright, 2018, by Michael Kalygin. +# Copyright, 2018, by Sherman K. +# Copyright, 2018, by Kugayama Nana. +# Copyright, 2018, by Casey Lopez. +# Copyright, 2019, by Akinori MUSHA. +# Copyright, 2019, by Rob Widmer. -require_relative 'representation' -require_relative 'paginate' +require_relative "representation" +require_relative "paginate" -require_relative 'custom_hostnames' -require_relative 'firewall' -require_relative 'dns' -require_relative 'logs' +require_relative "custom_hostnames" +require_relative "firewall" +require_relative "dns" +require_relative "logs" module Cloudflare class Zone < Representation def custom_hostnames - self.with(CustomHostnames, path: 'custom_hostnames') + self.with(CustomHostnames, path: "custom_hostnames/") end def dns_records - self.with(DNS::Records, path: 'dns_records') + self.with(DNS::Records, path: "dns_records/") end def firewall_rules - self.with(Firewall::Rules, path: 'firewall/access_rules/rules') + self.with(Firewall::Rules, path: "firewall/access_rules/rules/") end def logs - self.with(Logs::Received, path: 'logs/received') + self.with(Logs::Received, path: "logs/received/") end DEFAULT_PURGE_CACHE_PARAMS = { @@ -53,7 +43,7 @@ def logs }.freeze def purge_cache(parameters = DEFAULT_PURGE_CACHE_PARAMS) - self.with(Zone, path: 'purge_cache').post(parameters) + self.with(Zone, path: "purge_cache").post(parameters) return self end diff --git a/license.md b/license.md new file mode 100644 index 0000000..0fed9f2 --- /dev/null +++ b/license.md @@ -0,0 +1,41 @@ +# MIT License + +Copyright, 2012-2017, by Marcin Prokop. +Copyright, 2013, by emckay. +Copyright, 2014, by Jason Green. +Copyright, 2014-2021, by Samuel Williams. +Copyright, 2014, by Greg Retkowski. +Copyright, 2015, by Kyle Corbitt. +Copyright, 2015, by Guillaume Leseur. +Copyright, 2017, by Denis Sadomowski. +Copyright, 2017, by 莫粒. +Copyright, 2018, by Leonhardt Wille. +Copyright, 2018, by Mike Perham. +Copyright, 2018, by Michael Kalygin. +Copyright, 2018, by Sherman K. +Copyright, 2018, by Kugayama Nana. +Copyright, 2018, by Casey Lopez. +Copyright, 2019, by Akinori MUSHA. +Copyright, 2019, by Rob Widmer. +Copyright, 2019, by Nazar. +Copyright, 2019, by David Wegman. +Copyright, 2020, by Olle Jonsson. +Copyright, 2021, by Terry Kerr. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/readme.md similarity index 58% rename from README.md rename to readme.md index 8545b56..9181b8d 100644 --- a/README.md +++ b/readme.md @@ -2,7 +2,7 @@ It is a Ruby wrapper for the Cloudflare V4 API. It provides a light weight wrapper using `RestClient::Resource`. The wrapper functionality is limited to zones and DNS records at this time, *PRs welcome*. -[![Development Status](https://github.com/socketry/cloudflare/workflows/Development/badge.svg)](https://github.com/socketry/cloudflare/actions?workflow=Development) +[![Development Status](https://github.com/socketry/cloudflare/workflows/Test/badge.svg)](https://github.com/socketry/cloudflare/actions?workflow=Test) ## Installation @@ -86,39 +86,23 @@ end ## Contributing -1. Fork it -2. Create your feature branch (`git checkout -b my-new-feature`) -3. Commit your changes (`git commit -am 'Add some feature'`) -4. Push to the branch (`git push origin my-new-feature`) -5. Create new Pull Request +We welcome contributions to this project. -## See Also - - - [Cloudflare::DNS::Update](https://github.com/ioquatix/cloudflare-dns-update) - A dynamic DNS updater based on this gem. - - [Rubyflare](https://github.com/trev/rubyflare) - Another implementation. +1. Fork it. +2. Create your feature branch (`git checkout -b my-new-feature`). +3. Commit your changes (`git commit -am 'Add some feature'`). +4. Push to the branch (`git push origin my-new-feature`). +5. Create new Pull Request. -## License +### Developer Certificate of Origin -Released under the MIT license. +In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed. -Copyright, 2018, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams). -Copyright, 2017, by [David Rosenbloom](http://artifactory.com). -Copyright, 2012, 2014, by [Marcin Prokop](https://github.com/b4k3r). +### Community Guidelines -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers. -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +## See Also -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. + - [Cloudflare::DNS::Update](https://github.com/ioquatix/cloudflare-dns-update) - A dynamic DNS updater based on this gem. + - [Rubyflare](https://github.com/trev/rubyflare) - Another implementation. diff --git a/release.cert b/release.cert new file mode 100644 index 0000000..d98e595 --- /dev/null +++ b/release.cert @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11 +ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK +CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz +MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd +MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj +bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB +igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2 +9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW +sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE +e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN +XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss +RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn +tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM +zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW +xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O +BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs +aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs +aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE +cBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl +xCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/ +c1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp +8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws +JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP +eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt +Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8 +voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg= +-----END CERTIFICATE----- diff --git a/spec/cloudflare/accounts_spec.rb b/spec/cloudflare/accounts_spec.rb index 8f0c209..6c94765 100644 --- a/spec/cloudflare/accounts_spec.rb +++ b/spec/cloudflare/accounts_spec.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true +# Released under the MIT License. +# Copyright, 2019, by Rob Widmer. + RSpec.describe Cloudflare::Accounts, order: :defined, timeout: 30 do include_context Cloudflare::Account @@ -7,16 +10,16 @@ account.id # Force a fetch if it hasn't happened yet end - it 'can list existing accounts' do + it "can list existing accounts" do accounts = connection.accounts.to_a expect(accounts.any? {|a| a.id == account.id }).to be true end - it 'can get a specific account' do + it "can get a specific account" do expect(connection.accounts.find_by_id(account.id).id).to eq account.id end - it 'can generate a representation for the KV namespace endpoint' do + it "can generate a representation for the KV namespace endpoint" do ns = connection.accounts.find_by_id(account.id).kv_namespaces expect(ns).to be_kind_of(Cloudflare::KV::Namespaces) expect(ns.resource.reference.path).to end_with("/#{account.id}/storage/kv/namespaces") diff --git a/spec/cloudflare/custom_hostname/ssl_attribute/settings_spec.rb b/spec/cloudflare/custom_hostname/ssl_attribute/settings_spec.rb index a96be10..d068a09 100644 --- a/spec/cloudflare/custom_hostname/ssl_attribute/settings_spec.rb +++ b/spec/cloudflare/custom_hostname/ssl_attribute/settings_spec.rb @@ -1,52 +1,57 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019, by Rob Widmer. + RSpec.describe Cloudflare::CustomHostname::SSLAttribute::Settings do subject { described_class.new({}) } - it 'has an accessor for ciphers' do + it "has an accessor for ciphers" do ciphers = double expect(subject.ciphers).to be_nil subject.ciphers = ciphers expect(subject.ciphers).to be ciphers end - it 'has a boolean accessor for http2' do + it "has a boolean accessor for http2" do expect(subject.http2).to be_nil expect(subject.http2?).to be false subject.http2 = true - expect(subject.http2).to eq 'on' + expect(subject.http2).to eq "on" expect(subject.http2?).to be true subject.http2 = false - expect(subject.http2).to eq 'off' + expect(subject.http2).to eq "off" expect(subject.http2?).to be false - subject.http2 = 'on' - expect(subject.http2).to eq 'on' + subject.http2 = "on" + expect(subject.http2).to eq "on" expect(subject.http2?).to be true - subject.http2 = 'off' - expect(subject.http2).to eq 'off' + subject.http2 = "off" + expect(subject.http2).to eq "off" expect(subject.http2?).to be false end - it 'has an accessor for min_tls_version' do + it "has an accessor for min_tls_version" do tls_version = double expect(subject.min_tls_version).to be_nil subject.min_tls_version = tls_version expect(subject.min_tls_version).to be tls_version end - it 'has a boolean accessor for tls_1_3' do + it "has a boolean accessor for tls_1_3" do expect(subject.tls_1_3).to be_nil expect(subject.tls_1_3?).to be false subject.tls_1_3 = true - expect(subject.tls_1_3).to eq 'on' + expect(subject.tls_1_3).to eq "on" expect(subject.tls_1_3?).to be true subject.tls_1_3 = false - expect(subject.tls_1_3).to eq 'off' + expect(subject.tls_1_3).to eq "off" expect(subject.tls_1_3?).to be false - subject.tls_1_3 = 'on' - expect(subject.tls_1_3).to eq 'on' + subject.tls_1_3 = "on" + expect(subject.tls_1_3).to eq "on" expect(subject.tls_1_3?).to be true - subject.tls_1_3 = 'off' - expect(subject.tls_1_3).to eq 'off' + subject.tls_1_3 = "off" + expect(subject.tls_1_3).to eq "off" expect(subject.tls_1_3?).to be false end diff --git a/spec/cloudflare/custom_hostname/ssl_attribute_spec.rb b/spec/cloudflare/custom_hostname/ssl_attribute_spec.rb index dace23f..017b7e6 100644 --- a/spec/cloudflare/custom_hostname/ssl_attribute_spec.rb +++ b/spec/cloudflare/custom_hostname/ssl_attribute_spec.rb @@ -1,3 +1,8 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019, by Rob Widmer. + RSpec.describe Cloudflare::CustomHostname::SSLAttribute do accessors = [:cname, :cname_target, :http_body, :http_url, :method, :status, :type, :validation_errors] @@ -19,41 +24,41 @@ it '#active? returns true when the status is "active" and false otherwise' do expect(subject.active?).to be false - original_hash[:status] = 'initializing' + original_hash[:status] = "initializing" expect(subject.active?).to be false - original_hash[:status] = 'pending_validation' + original_hash[:status] = "pending_validation" expect(subject.active?).to be false - original_hash[:status] = 'pending_deployment' + original_hash[:status] = "pending_deployment" expect(subject.active?).to be false - original_hash[:status] = 'active' + original_hash[:status] = "active" expect(subject.active?).to be true end it '#pending_validation? returns true when the status is "pending_validation" and false otherwise' do expect(subject.pending_validation?).to be false - original_hash[:status] = 'initializing' + original_hash[:status] = "initializing" expect(subject.pending_validation?).to be false - original_hash[:status] = 'active' + original_hash[:status] = "active" expect(subject.pending_validation?).to be false - original_hash[:status] = 'pending_deployment' + original_hash[:status] = "pending_deployment" expect(subject.pending_validation?).to be false - original_hash[:status] = 'pending_validation' + original_hash[:status] = "pending_validation" expect(subject.pending_validation?).to be true end - describe '#settings' do + describe "#settings" do - it 'should return a Settings object' do + it "should return a Settings object" do expect(subject.settings).to be_kind_of Cloudflare::CustomHostname::SSLAttribute::Settings end - it 'initailizes the settings object with the value from the settings key' do + it "initailizes the settings object with the value from the settings key" do settings = { min_tls_version: double } original_hash[:settings] = settings expect(subject.settings.min_tls_version).to be settings[:min_tls_version] end - it 'initializes the settings object with a new hash if the settings key does not exist' do + it "initializes the settings object with a new hash if the settings key does not exist" do expected_value = double expect(original_hash[:settings]).to be_nil expect(subject.settings.min_tls_version).to be_nil @@ -62,7 +67,7 @@ expect(subject.settings.min_tls_version).to be expected_value end - it 'updates the stored hash with values set on the settings object' do + it "updates the stored hash with values set on the settings object" do expected_value = double expect(subject.settings.min_tls_version).to be_nil subject.settings.min_tls_version = expected_value diff --git a/spec/cloudflare/custom_hostnames_spec.rb b/spec/cloudflare/custom_hostnames_spec.rb index c8edf44..1e0dc44 100644 --- a/spec/cloudflare/custom_hostnames_spec.rb +++ b/spec/cloudflare/custom_hostnames_spec.rb @@ -1,3 +1,8 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019, by Rob Widmer. +# Copyright, 2019-2020, by Samuel Williams. RSpec.xdescribe Cloudflare::CustomHostnames, order: :defined, timeout: 30 do include_context Cloudflare::Zone @@ -22,16 +27,16 @@ end end - it 'can create a custom hostname record' do + it "can create a custom hostname record" do expect(record).to be_kind_of Cloudflare::CustomHostname expect(record.custom_metadata).to be_nil expect(record.hostname).to eq domain expect(record.custom_origin).to be_nil - expect(record.ssl.method).to eq 'http' - expect(record.ssl.type).to eq 'dv' + expect(record.ssl.method).to eq "http" + expect(record.ssl.type).to eq "dv" end - it 'can create a custom hostname record with a custom origin' do + it "can create a custom hostname record with a custom origin" do begin @record = zone.custom_hostnames.create(domain, origin: custom_origin) @@ -39,10 +44,10 @@ expect(@record.custom_metadata).to be_nil expect(@record.hostname).to eq domain expect(@record.custom_origin).to eq custom_origin - expect(@record.ssl.method).to eq 'http' - expect(@record.ssl.type).to eq 'dv' + expect(@record.ssl.method).to eq "http" + expect(@record.ssl.type).to eq "dv" rescue Cloudflare::RequestError => e - if e.message.include?('custom origin server has not been granted') + if e.message.include?("custom origin server has not been granted") skip(e.message) # This currently doesn't work but might start eventually: https://github.com/socketry/async-rspec/issues/7 else raise @@ -50,18 +55,18 @@ end end - it 'can create a custom hostname record with different ssl options' do - @record = zone.custom_hostnames.create(domain, ssl: { method: 'cname' }) + it "can create a custom hostname record with different ssl options" do + @record = zone.custom_hostnames.create(domain, ssl: { method: "cname" }) expect(@record).to be_kind_of Cloudflare::CustomHostname expect(@record.custom_metadata).to be_nil expect(@record.hostname).to eq domain expect(@record.custom_origin).to be_nil - expect(@record.ssl.method).to eq 'cname' - expect(@record.ssl.type).to eq 'dv' + expect(@record.ssl.method).to eq "cname" + expect(@record.ssl.type).to eq "dv" end - it 'can create a custom hostname record with additional metadata' do + it "can create a custom hostname record with additional metadata" do metadata = { a: rand(1..10) } begin @@ -71,10 +76,10 @@ expect(@record.custom_metadata).to eq metadata expect(@record.hostname).to eq domain expect(@record.custom_origin).to be_nil - expect(@record.ssl.method).to eq 'http' - expect(@record.ssl.type).to eq 'dv' + expect(@record.ssl.method).to eq "http" + expect(@record.ssl.type).to eq "dv" rescue Cloudflare::RequestError => e - if e.message.include?('No custom metadata access has been allocated for this zone') + if e.message.include?("No custom metadata access has been allocated for this zone") skip(e.message) # This currently doesn't work but might start eventually: https://github.com/socketry/async-rspec/issues/7 else raise @@ -82,18 +87,18 @@ end end - it 'can look up an existing custom hostname by the hostname or id' do + it "can look up an existing custom hostname by the hostname or id" do expect(zone.custom_hostnames.find_by_hostname(record.hostname).id).to eq record.id expect(zone.custom_hostnames.find_by_id(record.id).id).to eq record.id end - context 'with existing record' do + context "with existing record" do - it 'returns the hostname when calling #to_s' do + it "returns the hostname when calling #to_s" do expect(record.to_s).to eq domain end - it 'can update metadata' do + it "can update metadata" do metadata = { c: rand(1..10) } expect(record.custom_metadata).to be_nil @@ -111,7 +116,7 @@ expect(found_record.hostname).to eq domain expect(found_record.custom_origin).to be_nil rescue Cloudflare::RequestError => e - if e.message.include?('No custom metadata access has been allocated for this zone') + if e.message.include?("No custom metadata access has been allocated for this zone") skip(e.message) # This currently doesn't work but might start eventually: https://github.com/socketry/async-rspec/issues/7 else raise @@ -119,7 +124,7 @@ end end - it 'can update the custom origin' do + it "can update the custom origin" do expect(record.custom_origin).to be_nil begin @@ -135,7 +140,7 @@ expect(found_record.hostname).to eq domain expect(found_record.custom_origin).to eq custom_origin rescue Cloudflare::RequestError => e - if e.message.include?('custom origin server has not been granted') + if e.message.include?("custom origin server has not been granted") skip(e.message) # This currently doesn't work but might start eventually: https://github.com/socketry/async-rspec/issues/7 else raise @@ -143,30 +148,30 @@ end end - it 'can update ssl information' do - expect(record.ssl.method).to eq 'http' + it "can update ssl information" do + expect(record.ssl.method).to eq "http" - record.update_settings(ssl: { method: 'cname', type: 'dv' }) + record.update_settings(ssl: { method: "cname", type: "dv" }) - # Make sure the existing object is updated - expect(record.ssl.method).to eq 'cname' + # Make sure the existing object is updated + expect(record.ssl.method).to eq "cname" - # Verify that the server has the changes - found_record = zone.custom_hostnames.find_by_id(record.id) + # Verify that the server has the changes + found_record = zone.custom_hostnames.find_by_id(record.id) - expect(found_record.custom_metadata).to be_nil - expect(found_record.hostname).to eq domain - expect(found_record.custom_origin).to be_nil - expect(found_record.ssl.method).to eq 'cname' + expect(found_record.custom_metadata).to be_nil + expect(found_record.hostname).to eq domain + expect(found_record.custom_origin).to be_nil + expect(found_record.ssl.method).to eq "cname" end - context 'has an ssl section' do + context "has an ssl section" do - it 'wraps it in an SSLAttributes object' do + it "wraps it in an SSLAttributes object" do expect(record.ssl).to be_kind_of Cloudflare::CustomHostname::SSLAttribute end - it 'has some helpers for commonly used keys' do + it "has some helpers for commonly used keys" do # Make sure our values exist before we check to make sure that they are returned correctly expect(record.value[:ssl].values_at(:method, :http_body, :http_url).compact).not_to be_empty expect(record.ssl.method).to be record.value[:ssl][:method] @@ -176,16 +181,16 @@ end - describe '#ssl_active?' do + describe "#ssl_active?" do - it 'returns the result of calling ssl.active?' do + it "returns the result of calling ssl.active?" do expected_value = double expect(record.ssl).to receive(:active?).and_return(expected_value) expect(record).not_to receive(:send_patch) expect(record.ssl_active?).to be expected_value end - it 'returns the result of calling ssl.active? without triggering an update if force_update is true and the ssl is not in the pending_validation state' do + it "returns the result of calling ssl.active? without triggering an update if force_update is true and the ssl is not in the pending_validation state" do expected_value = double expect(record.ssl).to receive(:active?).and_return(expected_value) expect(record.ssl.method).not_to be_nil @@ -195,12 +200,12 @@ expect(record.ssl_active?(true)).to be expected_value end - it 'returns the result of calling ssl.active? after triggering an update if force_update is true and the ssl is in the pending_validation state' do + it "returns the result of calling ssl.active? after triggering an update if force_update is true and the ssl is in the pending_validation state" do expected_value = double expect(record.ssl).to receive(:active?).and_return(expected_value) expect(record.ssl.method).not_to be_nil expect(record.ssl.type).not_to be_nil - record.value[:ssl][:status] = 'pending_validation' + record.value[:ssl][:status] = "pending_validation" expect(record).to receive(:send_patch).with(ssl: { method: record.ssl.method, type: record.ssl.type }) expect(record.ssl_active?(true)).to be expected_value end diff --git a/spec/cloudflare/dns_spec.rb b/spec/cloudflare/dns_spec.rb index 1c76e62..3cdfd30 100644 --- a/spec/cloudflare/dns_spec.rb +++ b/spec/cloudflare/dns_spec.rb @@ -1,5 +1,10 @@ +# frozen_string_literal: true -require 'cloudflare/rspec/connection' +# Released under the MIT License. +# Copyright, 2019-2020, by Samuel Williams. +# Copyright, 2019, by David Wegman. + +require "cloudflare/rspec/connection" RSpec.describe Cloudflare::DNS, order: :defined, timeout: 30 do include_context Cloudflare::Zone diff --git a/spec/cloudflare/firewall_spec.rb b/spec/cloudflare/firewall_spec.rb index 06f0f6e..72531d9 100644 --- a/spec/cloudflare/firewall_spec.rb +++ b/spec/cloudflare/firewall_spec.rb @@ -1,31 +1,35 @@ +# frozen_string_literal: true -require 'cloudflare/rspec/connection' +# Released under the MIT License. +# Copyright, 2019-2020, by Samuel Williams. + +require "cloudflare/rspec/connection" RSpec.describe Cloudflare::Firewall, order: :defined, timeout: 30 do include_context Cloudflare::Zone - let(:notes) {'gemtest'} + let(:notes) {"gemtest"} context "with several rules" do - let(:allow_ip) {'123.123.123.123'} - let(:block_ip) {'123.123.123.124'} + let(:allow_ip) {"123.123.123.123"} + let(:block_ip) {"123.123.123.124"} before do zone.firewall_rules.each do |rule| rule.delete end - zone.firewall_rules.set('whitelist', allow_ip) - zone.firewall_rules.set('block', block_ip) + zone.firewall_rules.set("whitelist", allow_ip) + zone.firewall_rules.set("block", block_ip) end - it 'should get all rules' do + it "should get all rules" do rules = zone.firewall_rules.to_a expect(rules.size).to be >= 2 end - it 'should get rules with specific value' do + it "should get rules with specific value" do rules = zone.firewall_rules.each_by_value(allow_ip).to_a expect(rules.size).to be == 1 @@ -39,7 +43,7 @@ expect(rule.mode).to be == mode expect(rule.configuration[:value]).to be == value - expect(rule.configuration[:target]).to be == 'ip' + expect(rule.configuration[:target]).to be == "ip" expect(rule.notes).to be == notes ensure diff --git a/spec/cloudflare/kv/namespaces_spec.rb b/spec/cloudflare/kv/namespaces_spec.rb index 5fa2551..26af749 100644 --- a/spec/cloudflare/kv/namespaces_spec.rb +++ b/spec/cloudflare/kv/namespaces_spec.rb @@ -1,3 +1,8 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019, by Rob Widmer. +# Copyright, 2019, by Samuel Williams. RSpec.describe Cloudflare::KV::Namespaces, kv_spec: true, order: :defined, timeout: 30 do include_context Cloudflare::Account @@ -11,18 +16,18 @@ end end - it 'can create a namespace' do + it "can create a namespace" do expect(namespace).to be_kind_of Cloudflare::KV::Namespace expect(namespace.id).not_to be_nil expect(namespace.title).to eq namespace_title end - it 'can find a namespace by title' do + it "can find a namespace by title" do namespace # Call this so that the namespace gets created expect(account.kv_namespaces.find_by_title(namespace_title).id).to eq namespace.id end - it 'can rename the namespace' do + it "can rename the namespace" do new_title = "#{namespace_title}-#{rand(1..100)}" namespace.rename(new_title) expect(namespace.title).to eq new_title @@ -30,21 +35,21 @@ expect(account.kv_namespaces.find_by_title(namespace_title)).to be_nil end - it 'can store a key/value, read it back' do + it "can store a key/value, read it back" do key = "key-#{rand(1..100)}" value = rand(100..999) namespace.write_value(key, value) expect(account.kv_namespaces.find_by_id(namespace.id).read_value(key)).to eq value.to_s end - it 'can read a previously stored key' do + it "can read a previously stored key" do key = "key-#{rand(1..100)}" value = rand(100..999) expect(account.kv_namespaces.find_by_id(namespace.id).write_value(key, value)).to be true expect(namespace.read_value(key)).to eq value.to_s end - it 'can delete keys' do + it "can delete keys" do key = "key-#{rand(1..100)}" value = rand(100..999) expect(namespace.write_value(key, value)).to be true @@ -55,7 +60,7 @@ end.to raise_error(Cloudflare::RequestError) end - it 'can get the keys that exist in the namespace' do + it "can get the keys that exist in the namespace" do counter = 0 keys = Array.new(rand(1..9)) { "key-#{counter += 1}" } # Keep this single digits so ordering works keys.each_with_index do |key, i| diff --git a/spec/cloudflare/zone_spec.rb b/spec/cloudflare/zone_spec.rb index 3c73a97..0af0cce 100644 --- a/spec/cloudflare/zone_spec.rb +++ b/spec/cloudflare/zone_spec.rb @@ -1,8 +1,16 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2017-2019, by Samuel Williams. +# Copyright, 2018, by Leonhardt Wille. +# Copyright, 2018, by Michael Kalygin. +# Copyright, 2018, by Sherman K. +# Copyright, 2019, by Rob Widmer. RSpec.describe Cloudflare::Zones, order: :defined, timeout: 30 do include_context Cloudflare::Zone - if ENV['CLOUDFLARE_TEST_ZONE_MANAGEMENT'] == 'true' + if ENV["CLOUDFLARE_TEST_ZONE_MANAGEMENT"] == "true" it "can delete existing domain if exists" do if zone = zones.find_by_name(name) expect(zone.delete).to be_success diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 95093e3..cff467d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,27 +1,34 @@ # frozen_string_literal: true -AUTH_EMAIL = ENV['CLOUDFLARE_EMAIL'] -AUTH_KEY = ENV['CLOUDFLARE_KEY'] +# Released under the MIT License. +# Copyright, 2017-2020, by Samuel Williams. +# Copyright, 2018, by Leonhardt Wille. +# Copyright, 2018, by Michael Kalygin. +# Copyright, 2018, by Sherman K. +# Copyright, 2019, by Rob Widmer. + +AUTH_EMAIL = ENV["CLOUDFLARE_EMAIL"] +AUTH_KEY = ENV["CLOUDFLARE_KEY"] if AUTH_EMAIL.nil? || AUTH_EMAIL.empty? || AUTH_KEY.nil? || AUTH_KEY.empty? - puts 'Please make sure you have defined CLOUDFLARE_EMAIL and CLOUDFLARE_KEY in your environment' - puts 'You can also specify CLOUDFLARE_ZONE_NAME to test with your own zone and' - puts 'CLOUDFLARE_ACCOUNT_ID to use a specific account' + puts "Please make sure you have defined CLOUDFLARE_EMAIL and CLOUDFLARE_KEY in your environment" + puts "You can also specify CLOUDFLARE_ZONE_NAME to test with your own zone and" + puts "CLOUDFLARE_ACCOUNT_ID to use a specific account" exit(1) end -ACCOUNT_ID = ENV['CLOUDFLARE_ACCOUNT_ID'] +ACCOUNT_ID = ENV["CLOUDFLARE_ACCOUNT_ID"] NAMES = %w{alligator ant bear bee bird camel cat cheetah chicken chimpanzee cow crocodile deer dog dolphin duck eagle elephant fish fly fox frog giraffe goat goldfish hamster hippopotamus horse kangaroo kitten lion lobster monkey octopus owl panda pig puppy rabbit rat scorpion seal shark sheep snail snake spider squirrel tiger turtle wolf zebra} -JOB_ID = ENV.fetch('INVOCATION_ID', 'testing').hash -ZONE_NAME = ENV['CLOUDFLARE_ZONE_NAME'] || "#{NAMES[JOB_ID % NAMES.size]}.com" +JOB_ID = ENV.fetch("INVOCATION_ID", "testing").hash +ZONE_NAME = ENV["CLOUDFLARE_ZONE_NAME"] || "#{NAMES[JOB_ID % NAMES.size]}.com" $stderr.puts "Using zone name: #{ZONE_NAME}" -require 'covered/rspec' -require 'async/rspec' +require "covered/rspec" +require "async/rspec" -require 'cloudflare/rspec/connection' -require 'cloudflare/zones' +require "cloudflare/rspec/connection" +require "cloudflare/zones" RSpec.shared_context Cloudflare::Account do include_context Cloudflare::RSpec::Connection @@ -55,7 +62,7 @@ RSpec.configure do |config| # Enable flags like --only-failures and --next-failure - config.example_status_persistence_file_path = '.rspec_status' + config.example_status_persistence_file_path = ".rspec_status" config.expect_with :rspec do |c| c.syntax = :expect @@ -73,8 +80,8 @@ end account.kv_namespaces.to_a rescue Cloudflare::RequestError => e - if e.message.include?('your account is not entitled') - puts 'Disabling KV specs due to no access' + if e.message.include?("your account is not entitled") + puts "Disabling KV specs due to no access" disabled_specs[:kv_spec] = true else raise From 8400a5b1c411979fee073778011860b747eed8d4 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Thu, 12 Sep 2024 00:28:31 +1200 Subject: [PATCH 2/9] Initial migration of code. --- .mailmap | 4 + cloudflare.gemspec | 2 +- gems.rb | 8 +- lib/cloudflare.rb | 32 +++--- lib/cloudflare/accounts.rb | 2 +- lib/cloudflare/connection.rb | 18 ++-- .../custom_hostname/ssl_attribute.rb | 2 +- .../custom_hostname/ssl_attribute/settings.rb | 2 +- lib/cloudflare/custom_hostnames.rb | 2 +- lib/cloudflare/dns.rb | 2 +- lib/cloudflare/firewall.rb | 2 +- lib/cloudflare/kv/namespaces.rb | 2 +- lib/cloudflare/kv/rest_wrapper.rb | 1 + lib/cloudflare/logs.rb | 2 +- lib/cloudflare/paginate.rb | 28 ++--- lib/cloudflare/representation.rb | 101 ++++++++---------- lib/cloudflare/rspec/connection.rb | 2 +- lib/cloudflare/user.rb | 2 +- lib/cloudflare/version.rb | 2 +- lib/cloudflare/zones.rb | 6 +- license.md | 10 +- spec/cloudflare/accounts_spec.rb | 1 + .../ssl_attribute/settings_spec.rb | 1 + .../custom_hostname/ssl_attribute_spec.rb | 1 + spec/cloudflare/custom_hostnames_spec.rb | 2 +- spec/cloudflare/dns_spec.rb | 2 +- spec/cloudflare/firewall_spec.rb | 2 +- spec/cloudflare/kv/namespaces_spec.rb | 2 +- spec/cloudflare/zone_spec.rb | 4 +- spec/spec_helper.rb | 4 +- 30 files changed, 124 insertions(+), 127 deletions(-) diff --git a/.mailmap b/.mailmap index 8da6766..2fb94e8 100644 --- a/.mailmap +++ b/.mailmap @@ -1,2 +1,6 @@ +Eric McKay Denis Sadomowski 莫粒 +Sherman Koa +Fedishin Nazar +Akinori Musha diff --git a/cloudflare.gemspec b/cloudflare.gemspec index 1dbf0b4..7d13b3e 100644 --- a/cloudflare.gemspec +++ b/cloudflare.gemspec @@ -7,7 +7,7 @@ Gem::Specification.new do |spec| spec.version = Cloudflare::VERSION spec.summary = "A Ruby wrapper for the Cloudflare API." - spec.authors = ["Samuel Williams", "Marcin Prokop", "Leonhardt Wille", "Rob Widmer", "Akinori MUSHA", "Sherman K", "Michael Kalygin", "Denis Sadomowski", "Nazar", "emckay", "Casey Lopez", "David Wegman", "Greg Retkowski", "Guillaume Leseur", "Jason Green", "Kugayama Nana", "Kyle Corbitt", "Mike Perham", "Olle Jonsson", "Terry Kerr", "莫粒"] + spec.authors = ["Samuel Williams", "Marcin Prokop", "Leonhardt Wille", "Rob Widmer", "Akinori Musha", "Sherman Koa", "Michael Kalygin", "Denis Sadomowski", "Eric McKay", "Fedishin Nazar", "Casey Lopez", "David Wegman", "Greg Retkowski", "Guillaume Leseur", "Jason Green", "Kugayama Nana", "Kyle Corbitt", "Mike Perham", "Olle Jonsson", "Terry Kerr", "莫粒"] spec.license = "MIT" spec.cert_chain = ["release.cert"] diff --git a/gems.rb b/gems.rb index 8a43a13..2853f3f 100644 --- a/gems.rb +++ b/gems.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2014-2020, by Samuel Williams. +# Copyright, 2014-2024, by Samuel Williams. # Copyright, 2014, by Marcin Prokop. # Copyright, 2018, by Leonhardt Wille. @@ -19,13 +19,15 @@ group :test do gem "rspec" + gem "covered" gem "decode" gem "rubocop" gem "async-rspec" - gem "coveralls", require: false - gem "simplecov" gem "sinatra" gem "webmock" + + gem "bake-test" + gem "bake-test-external" end diff --git a/lib/cloudflare.rb b/lib/cloudflare.rb index 47a2c71..c018dec 100644 --- a/lib/cloudflare.rb +++ b/lib/cloudflare.rb @@ -2,33 +2,31 @@ # Released under the MIT License. # Copyright, 2012-2016, by Marcin Prokop. -# Copyright, 2013, by emckay. +# Copyright, 2013, by Eric McKay. # Copyright, 2014, by Jason Green. -# Copyright, 2014-2019, by Samuel Williams. +# Copyright, 2014-2024, by Samuel Williams. # Copyright, 2014, by Greg Retkowski. # Copyright, 2018, by Leonhardt Wille. -# Copyright, 2019, by Akinori MUSHA. +# Copyright, 2019, by Akinori Musha. require "async" require_relative "cloudflare/connection" module Cloudflare - DEFAULT_ENDPOINT = Async::HTTP::Endpoint.parse("https://api.cloudflare.com/client/v4/") - - def self.connect(endpoint = DEFAULT_ENDPOINT, **auth_info) - representation = Connection.for(endpoint) - - if !auth_info.empty? - representation = representation.authenticated(**auth_info) - end - - return representation unless block_given? - - Async do + def self.connect(*arguments, **auth_info) + Sync do + connection = Connection.open(*arguments) + + if !auth_info.empty? + connection = connection.authenticated(**auth_info) + end + + return connection unless block_given? + begin - yield representation + yield connection ensure - representation.close + connection.close end end end diff --git a/lib/cloudflare/accounts.rb b/lib/cloudflare/accounts.rb index e552199..23bce50 100644 --- a/lib/cloudflare/accounts.rb +++ b/lib/cloudflare/accounts.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2018-2019, by Samuel Williams. +# Copyright, 2018-2024, by Samuel Williams. # Copyright, 2019, by Rob Widmer. require_relative "representation" diff --git a/lib/cloudflare/connection.rb b/lib/cloudflare/connection.rb index d39c02f..8704538 100644 --- a/lib/cloudflare/connection.rb +++ b/lib/cloudflare/connection.rb @@ -2,22 +2,24 @@ # Released under the MIT License. # Copyright, 2014-2016, by Marcin Prokop. -# Copyright, 2014-2019, by Samuel Williams. +# Copyright, 2014-2024, by Samuel Williams. # Copyright, 2015, by Kyle Corbitt. # Copyright, 2015, by Guillaume Leseur. # Copyright, 2018, by Leonhardt Wille. # Copyright, 2018, by Michael Kalygin. -# Copyright, 2018, by Sherman K. -# Copyright, 2019, by Akinori MUSHA. +# Copyright, 2018, by Sherman Koa. +# Copyright, 2019, by Akinori Musha. -require_relative "representation" +require "async/rest/resource" require_relative "zones" require_relative "accounts" require_relative "user" module Cloudflare - class Connection < Representation + class Connection < Async::REST::Resource + ENDPOINT = Async::HTTP::Endpoint.parse("https://api.cloudflare.com/client/v4/") + def authenticated(token: nil, key: nil, email: nil) headers = {} @@ -36,15 +38,15 @@ def authenticated(token: nil, key: nil, email: nil) end def zones - self.with(Zones, path: "zones/") + Zones.new(self.with(path: "zones/")) end def accounts - self.with(Accounts, path: "accounts") + Accounts.new(self.with(path: "accounts")) end def user - self.with(User, path: "user") + User.new(self.with(path: "user")) end end end diff --git a/lib/cloudflare/custom_hostname/ssl_attribute.rb b/lib/cloudflare/custom_hostname/ssl_attribute.rb index a60ce6c..f7479af 100644 --- a/lib/cloudflare/custom_hostname/ssl_attribute.rb +++ b/lib/cloudflare/custom_hostname/ssl_attribute.rb @@ -2,7 +2,7 @@ # Released under the MIT License. # Copyright, 2019, by Rob Widmer. -# Copyright, 2019, by Samuel Williams. +# Copyright, 2019-2024, by Samuel Williams. require_relative "./ssl_attribute/settings" diff --git a/lib/cloudflare/custom_hostname/ssl_attribute/settings.rb b/lib/cloudflare/custom_hostname/ssl_attribute/settings.rb index d3db84f..5747867 100644 --- a/lib/cloudflare/custom_hostname/ssl_attribute/settings.rb +++ b/lib/cloudflare/custom_hostname/ssl_attribute/settings.rb @@ -2,7 +2,7 @@ # Released under the MIT License. # Copyright, 2019, by Rob Widmer. -# Copyright, 2019, by Samuel Williams. +# Copyright, 2019-2024, by Samuel Williams. module Cloudflare class CustomHostname < Representation diff --git a/lib/cloudflare/custom_hostnames.rb b/lib/cloudflare/custom_hostnames.rb index 9081927..8d3e1bd 100644 --- a/lib/cloudflare/custom_hostnames.rb +++ b/lib/cloudflare/custom_hostnames.rb @@ -2,7 +2,7 @@ # Released under the MIT License. # Copyright, 2019, by Rob Widmer. -# Copyright, 2019, by Samuel Williams. +# Copyright, 2019-2024, by Samuel Williams. require_relative "custom_hostname/ssl_attribute" require_relative "paginate" diff --git a/lib/cloudflare/dns.rb b/lib/cloudflare/dns.rb index 4f19d9f..6697172 100644 --- a/lib/cloudflare/dns.rb +++ b/lib/cloudflare/dns.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2019, by Samuel Williams. +# Copyright, 2019-2024, by Samuel Williams. # Copyright, 2019, by Rob Widmer. # Copyright, 2019, by David Wegman. diff --git a/lib/cloudflare/firewall.rb b/lib/cloudflare/firewall.rb index d156736..7a42de7 100644 --- a/lib/cloudflare/firewall.rb +++ b/lib/cloudflare/firewall.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2019, by Samuel Williams. +# Copyright, 2019-2024, by Samuel Williams. # Copyright, 2019, by Rob Widmer. require_relative "representation" diff --git a/lib/cloudflare/kv/namespaces.rb b/lib/cloudflare/kv/namespaces.rb index 14ad2e5..2570ba1 100644 --- a/lib/cloudflare/kv/namespaces.rb +++ b/lib/cloudflare/kv/namespaces.rb @@ -2,7 +2,7 @@ # Released under the MIT License. # Copyright, 2019, by Rob Widmer. -# Copyright, 2019, by Samuel Williams. +# Copyright, 2019-2024, by Samuel Williams. # Copyright, 2021, by Terry Kerr. require_relative "../paginate" diff --git a/lib/cloudflare/kv/rest_wrapper.rb b/lib/cloudflare/kv/rest_wrapper.rb index 7508582..70c34a6 100644 --- a/lib/cloudflare/kv/rest_wrapper.rb +++ b/lib/cloudflare/kv/rest_wrapper.rb @@ -2,6 +2,7 @@ # Released under the MIT License. # Copyright, 2021, by Terry Kerr. +# Copyright, 2024, by Samuel Williams. require "json" diff --git a/lib/cloudflare/logs.rb b/lib/cloudflare/logs.rb index e3ece94..58aefb6 100644 --- a/lib/cloudflare/logs.rb +++ b/lib/cloudflare/logs.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2019, by Samuel Williams. +# Copyright, 2019-2024, by Samuel Williams. require_relative "representation" require_relative "paginate" diff --git a/lib/cloudflare/paginate.rb b/lib/cloudflare/paginate.rb index afdcf79..6013927 100644 --- a/lib/cloudflare/paginate.rb +++ b/lib/cloudflare/paginate.rb @@ -1,36 +1,36 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2018-2019, by Samuel Williams. +# Copyright, 2018-2024, by Samuel Williams. # Copyright, 2019, by Rob Widmer. module Cloudflare module Paginate include Enumerable - + def each(page: 1, per_page: 50, **parameters) return to_enum(:each, page: page, per_page: per_page, **parameters) unless block_given? - + while true - zones = @resource.get(self.class, page: page, per_page: per_page, **parameters) - - break if zones.empty? - - Array(zones.value).each do |attributes| - yield represent(zones.metadata, attributes) + response = self.class.get(@resource, {page: page, per_page: per_page, **parameters}) + + break if response.empty? + + response.results.each do |attributes| + yield represent(response.metadata, attributes) end - + page += 1 - + # Was this the last page? - break if zones.value.size < per_page + break if response.results.size < per_page end end - + def empty? self.value.empty? end - + def find_by_id(id) representation.new(@resource.with(path: "#{id}/")) end diff --git a/lib/cloudflare/representation.rb b/lib/cloudflare/representation.rb index f13f094..1431921 100644 --- a/lib/cloudflare/representation.rb +++ b/lib/cloudflare/representation.rb @@ -1,96 +1,83 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2017-2018, by Samuel Williams. +# Copyright, 2017-2024, by Samuel Williams. # Copyright, 2018, by Leonhardt Wille. # Copyright, 2019, by Rob Widmer. require "json" require "async/rest/representation" +require "async/rest/wrapper/json" module Cloudflare class RequestError < StandardError def initialize(resource, errors) super("#{resource}: #{errors.map{|attributes| attributes[:message]}.join(', ')}") - + @representation = representation end - + attr_reader :representation end - - class Message - def initialize(response) - @response = response - @body = response.read - - # Some endpoints return the value instead of a message object (like KV reads) - @body = { success: true, result: @body } unless @body.is_a?(Hash) - end - - attr :response - attr :body - - def headers - @response.headers - end - - def result - @body[:result] - end - - def read - @body[:result] - end - - def results - Array(result) - end - - def errors - @body[:errors] - end - - def messages - @body[:messages] - end - - def success? - @body[:success] - end + + class Wrapper < Async::REST::Wrapper::JSON end - + class Representation < Async::REST::Representation - def process_response(*) - message = Message.new(super) - - unless message.success? - raise RequestError.new(@resource, message.errors) + WRAPPER = Wrapper.new + + def initialize(...) + super(...) + + # Some endpoints return the value instead of a message object (like KV reads) + unless @value.is_a?(Hash) + @value = {success: true, result: @value} end - - return message end - + def representation Representation end - + def represent(metadata, attributes) resource = @resource.with(path: attributes[:id]) - binding.irb - + representation.new(resource, metadata: metadata, value: attributes) end - + def represent_message(message) represent(message.headers, message.result) end - + def to_hash if value.is_a?(Hash) return value end end + + def result + value[:result] + end + + def read + value[:result] + end + + def results + Array(result) + end + + def errors + value[:errors] + end + + def messages + value[:messages] + end + + def success? + value[:success] + end end end diff --git a/lib/cloudflare/rspec/connection.rb b/lib/cloudflare/rspec/connection.rb index 0c0dae6..0c1a180 100644 --- a/lib/cloudflare/rspec/connection.rb +++ b/lib/cloudflare/rspec/connection.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2017-2020, by Samuel Williams. +# Copyright, 2017-2024, by Samuel Williams. # Copyright, 2018, by Leonhardt Wille. require "async/rspec" diff --git a/lib/cloudflare/user.rb b/lib/cloudflare/user.rb index 5405989..dde3d18 100644 --- a/lib/cloudflare/user.rb +++ b/lib/cloudflare/user.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2017-2019, by Samuel Williams. +# Copyright, 2017-2024, by Samuel Williams. # Copyright, 2018, by Leonhardt Wille. require_relative "representation" diff --git a/lib/cloudflare/version.rb b/lib/cloudflare/version.rb index edd5353..35fb796 100644 --- a/lib/cloudflare/version.rb +++ b/lib/cloudflare/version.rb @@ -2,7 +2,7 @@ # Released under the MIT License. # Copyright, 2014-2016, by Marcin Prokop. -# Copyright, 2014-2021, by Samuel Williams. +# Copyright, 2014-2024, by Samuel Williams. # Copyright, 2015, by Kyle Corbitt. # Copyright, 2018, by Leonhardt Wille. # Copyright, 2018, by Casey Lopez. diff --git a/lib/cloudflare/zones.rb b/lib/cloudflare/zones.rb index 8068439..73a12a3 100644 --- a/lib/cloudflare/zones.rb +++ b/lib/cloudflare/zones.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2017-2019, by Samuel Williams. +# Copyright, 2017-2024, by Samuel Williams. # Copyright, 2017, by Denis Sadomowski. # Copyright, 2017, by 莫粒. # Copyright, 2018, by Leonhardt Wille. # Copyright, 2018, by Michael Kalygin. -# Copyright, 2018, by Sherman K. +# Copyright, 2018, by Sherman Koa. # Copyright, 2018, by Kugayama Nana. # Copyright, 2018, by Casey Lopez. -# Copyright, 2019, by Akinori MUSHA. +# Copyright, 2019, by Akinori Musha. # Copyright, 2019, by Rob Widmer. require_relative "representation" diff --git a/license.md b/license.md index 0fed9f2..2fe77a4 100644 --- a/license.md +++ b/license.md @@ -1,9 +1,9 @@ # MIT License Copyright, 2012-2017, by Marcin Prokop. -Copyright, 2013, by emckay. +Copyright, 2013, by Eric McKay. Copyright, 2014, by Jason Green. -Copyright, 2014-2021, by Samuel Williams. +Copyright, 2014-2024, by Samuel Williams. Copyright, 2014, by Greg Retkowski. Copyright, 2015, by Kyle Corbitt. Copyright, 2015, by Guillaume Leseur. @@ -12,12 +12,12 @@ Copyright, 2017, by 莫粒. Copyright, 2018, by Leonhardt Wille. Copyright, 2018, by Mike Perham. Copyright, 2018, by Michael Kalygin. -Copyright, 2018, by Sherman K. +Copyright, 2018, by Sherman Koa. Copyright, 2018, by Kugayama Nana. Copyright, 2018, by Casey Lopez. -Copyright, 2019, by Akinori MUSHA. +Copyright, 2019, by Akinori Musha. Copyright, 2019, by Rob Widmer. -Copyright, 2019, by Nazar. +Copyright, 2019, by Fedishin Nazar. Copyright, 2019, by David Wegman. Copyright, 2020, by Olle Jonsson. Copyright, 2021, by Terry Kerr. diff --git a/spec/cloudflare/accounts_spec.rb b/spec/cloudflare/accounts_spec.rb index 6c94765..c2700ff 100644 --- a/spec/cloudflare/accounts_spec.rb +++ b/spec/cloudflare/accounts_spec.rb @@ -2,6 +2,7 @@ # Released under the MIT License. # Copyright, 2019, by Rob Widmer. +# Copyright, 2024, by Samuel Williams. RSpec.describe Cloudflare::Accounts, order: :defined, timeout: 30 do include_context Cloudflare::Account diff --git a/spec/cloudflare/custom_hostname/ssl_attribute/settings_spec.rb b/spec/cloudflare/custom_hostname/ssl_attribute/settings_spec.rb index d068a09..7297a00 100644 --- a/spec/cloudflare/custom_hostname/ssl_attribute/settings_spec.rb +++ b/spec/cloudflare/custom_hostname/ssl_attribute/settings_spec.rb @@ -2,6 +2,7 @@ # Released under the MIT License. # Copyright, 2019, by Rob Widmer. +# Copyright, 2024, by Samuel Williams. RSpec.describe Cloudflare::CustomHostname::SSLAttribute::Settings do diff --git a/spec/cloudflare/custom_hostname/ssl_attribute_spec.rb b/spec/cloudflare/custom_hostname/ssl_attribute_spec.rb index 017b7e6..84653bb 100644 --- a/spec/cloudflare/custom_hostname/ssl_attribute_spec.rb +++ b/spec/cloudflare/custom_hostname/ssl_attribute_spec.rb @@ -2,6 +2,7 @@ # Released under the MIT License. # Copyright, 2019, by Rob Widmer. +# Copyright, 2024, by Samuel Williams. RSpec.describe Cloudflare::CustomHostname::SSLAttribute do diff --git a/spec/cloudflare/custom_hostnames_spec.rb b/spec/cloudflare/custom_hostnames_spec.rb index 1e0dc44..11e9411 100644 --- a/spec/cloudflare/custom_hostnames_spec.rb +++ b/spec/cloudflare/custom_hostnames_spec.rb @@ -2,7 +2,7 @@ # Released under the MIT License. # Copyright, 2019, by Rob Widmer. -# Copyright, 2019-2020, by Samuel Williams. +# Copyright, 2019-2024, by Samuel Williams. RSpec.xdescribe Cloudflare::CustomHostnames, order: :defined, timeout: 30 do include_context Cloudflare::Zone diff --git a/spec/cloudflare/dns_spec.rb b/spec/cloudflare/dns_spec.rb index 3cdfd30..28f03a6 100644 --- a/spec/cloudflare/dns_spec.rb +++ b/spec/cloudflare/dns_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2019-2020, by Samuel Williams. +# Copyright, 2019-2024, by Samuel Williams. # Copyright, 2019, by David Wegman. require "cloudflare/rspec/connection" diff --git a/spec/cloudflare/firewall_spec.rb b/spec/cloudflare/firewall_spec.rb index 72531d9..3296c49 100644 --- a/spec/cloudflare/firewall_spec.rb +++ b/spec/cloudflare/firewall_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2019-2020, by Samuel Williams. +# Copyright, 2019-2024, by Samuel Williams. require "cloudflare/rspec/connection" diff --git a/spec/cloudflare/kv/namespaces_spec.rb b/spec/cloudflare/kv/namespaces_spec.rb index 26af749..ce69086 100644 --- a/spec/cloudflare/kv/namespaces_spec.rb +++ b/spec/cloudflare/kv/namespaces_spec.rb @@ -2,7 +2,7 @@ # Released under the MIT License. # Copyright, 2019, by Rob Widmer. -# Copyright, 2019, by Samuel Williams. +# Copyright, 2019-2024, by Samuel Williams. RSpec.describe Cloudflare::KV::Namespaces, kv_spec: true, order: :defined, timeout: 30 do include_context Cloudflare::Account diff --git a/spec/cloudflare/zone_spec.rb b/spec/cloudflare/zone_spec.rb index 0af0cce..3bf4013 100644 --- a/spec/cloudflare/zone_spec.rb +++ b/spec/cloudflare/zone_spec.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2017-2019, by Samuel Williams. +# Copyright, 2017-2024, by Samuel Williams. # Copyright, 2018, by Leonhardt Wille. # Copyright, 2018, by Michael Kalygin. -# Copyright, 2018, by Sherman K. +# Copyright, 2018, by Sherman Koa. # Copyright, 2019, by Rob Widmer. RSpec.describe Cloudflare::Zones, order: :defined, timeout: 30 do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index cff467d..5586ca8 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2017-2020, by Samuel Williams. +# Copyright, 2017-2024, by Samuel Williams. # Copyright, 2018, by Leonhardt Wille. # Copyright, 2018, by Michael Kalygin. -# Copyright, 2018, by Sherman K. +# Copyright, 2018, by Sherman Koa. # Copyright, 2019, by Rob Widmer. AUTH_EMAIL = ENV["CLOUDFLARE_EMAIL"] From f53d8cee1b8dbf0510209ac6e72bc819f3ad9b89 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Fri, 13 Sep 2024 12:55:24 +1200 Subject: [PATCH 3/9] Migrate/update tests. --- .gitignore | 5 +- .rspec | 4 - cloudflare.gemspec | 2 +- config/sus.rb | 7 + fixtures/cloudflare/a_connection.rb | 62 +++++ gems.rb | 4 +- lib/cloudflare.rb | 24 +- lib/cloudflare/accounts.rb | 2 +- lib/cloudflare/connection.rb | 8 +- .../custom_hostname/ssl_attribute.rb | 3 +- .../custom_hostname/ssl_attribute/settings.rb | 4 +- lib/cloudflare/custom_hostnames.rb | 94 +++++--- lib/cloudflare/dns.rb | 73 +++--- lib/cloudflare/firewall.rb | 45 ++-- lib/cloudflare/kv/namespaces.rb | 86 ++++--- lib/cloudflare/kv/rest_wrapper.rb | 48 ---- lib/cloudflare/kv/wrapper.rb | 38 +++ lib/cloudflare/logs.rb | 3 +- lib/cloudflare/paginate.rb | 4 +- lib/cloudflare/representation.rb | 48 ++-- lib/cloudflare/rspec/connection.rb | 41 ---- lib/cloudflare/user.rb | 4 +- lib/cloudflare/zones.rb | 42 ++-- spec/cloudflare/accounts_spec.rb | 28 --- .../ssl_attribute/settings_spec.rb | 60 ----- .../custom_hostname/ssl_attribute_spec.rb | 79 ------- spec/cloudflare/custom_hostnames_spec.rb | 216 ------------------ spec/cloudflare/dns_spec.rb | 55 ----- spec/cloudflare/kv/namespaces_spec.rb | 76 ------ spec/cloudflare/zone_spec.rb | 35 --- spec/spec_helper.rb | 93 -------- test/cloudflare/accounts.rb | 39 ++++ test/cloudflare/connection.rb | 21 ++ .../custom_hostname/ssl_attribute.rb | 80 +++++++ .../custom_hostname/ssl_attribute/settings.rb | 59 +++++ test/cloudflare/custom_hostnames.rb | 210 +++++++++++++++++ test/cloudflare/dns.rb | 61 +++++ .../cloudflare/firewall.rb | 11 +- test/cloudflare/kv/namespaces.rb | 80 +++++++ test/cloudflare/logs.rb | 17 ++ test/cloudflare/zones.rb | 49 ++++ 41 files changed, 1001 insertions(+), 919 deletions(-) delete mode 100644 .rspec create mode 100644 config/sus.rb create mode 100644 fixtures/cloudflare/a_connection.rb delete mode 100644 lib/cloudflare/kv/rest_wrapper.rb create mode 100644 lib/cloudflare/kv/wrapper.rb delete mode 100644 lib/cloudflare/rspec/connection.rb delete mode 100644 spec/cloudflare/accounts_spec.rb delete mode 100644 spec/cloudflare/custom_hostname/ssl_attribute/settings_spec.rb delete mode 100644 spec/cloudflare/custom_hostname/ssl_attribute_spec.rb delete mode 100644 spec/cloudflare/custom_hostnames_spec.rb delete mode 100644 spec/cloudflare/dns_spec.rb delete mode 100644 spec/cloudflare/kv/namespaces_spec.rb delete mode 100644 spec/cloudflare/zone_spec.rb delete mode 100644 spec/spec_helper.rb create mode 100644 test/cloudflare/accounts.rb create mode 100644 test/cloudflare/connection.rb create mode 100644 test/cloudflare/custom_hostname/ssl_attribute.rb create mode 100644 test/cloudflare/custom_hostname/ssl_attribute/settings.rb create mode 100644 test/cloudflare/custom_hostnames.rb create mode 100644 test/cloudflare/dns.rb rename spec/cloudflare/firewall_spec.rb => test/cloudflare/firewall.rb (82%) create mode 100644 test/cloudflare/kv/namespaces.rb create mode 100644 test/cloudflare/logs.rb create mode 100644 test/cloudflare/zones.rb diff --git a/.gitignore b/.gitignore index 5bc8957..6c32510 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,4 @@ /.covered.db /external -# rspec failure tracking -.rspec_status -.covered.db -.env +/.env diff --git a/.rspec b/.rspec deleted file mode 100644 index 3db1ba3..0000000 --- a/.rspec +++ /dev/null @@ -1,4 +0,0 @@ ---format documentation ---backtrace ---warnings ---require spec_helper \ No newline at end of file diff --git a/cloudflare.gemspec b/cloudflare.gemspec index 7d13b3e..def98be 100644 --- a/cloudflare.gemspec +++ b/cloudflare.gemspec @@ -23,5 +23,5 @@ Gem::Specification.new do |spec| spec.required_ruby_version = ">= 3.1" - spec.add_dependency "async-rest", "~> 0.12" + spec.add_dependency "async-rest", "~> 0.18" end diff --git a/config/sus.rb b/config/sus.rb new file mode 100644 index 0000000..f99b9c2 --- /dev/null +++ b/config/sus.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2024, by Samuel Williams. + +require "covered/sus" +include Covered::Sus diff --git a/fixtures/cloudflare/a_connection.rb b/fixtures/cloudflare/a_connection.rb new file mode 100644 index 0000000..cb26a51 --- /dev/null +++ b/fixtures/cloudflare/a_connection.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2024, by Samuel Williams. + +require "cloudflare" +require "sus/fixtures/async/reactor_context" + +module Cloudflare + AUTH_EMAIL = ENV["CLOUDFLARE_EMAIL"] + AUTH_KEY = ENV["CLOUDFLARE_KEY"] + PROXY_URL = ENV["CLOUDFLARE_PROXY"] + + ACCOUNT_ID = ENV["CLOUDFLARE_ACCOUNT_ID"] + + ZONE_NAMES = %w{alligator ant bear bee bird camel cat cheetah chicken chimpanzee cow crocodile deer dog dolphin duck eagle elephant fish fly fox frog giraffe goat goldfish hamster hippopotamus horse kangaroo kitten lion lobster monkey octopus owl panda pig puppy rabbit rat scorpion seal shark sheep snail snake spider squirrel tiger turtle wolf zebra} + + JOB_ID = ENV.fetch("INVOCATION_ID", "testing").hash + + ZONE_NAME = ENV["CLOUDFLARE_ZONE_NAME"] || "#{ZONE_NAMES[JOB_ID % ZONE_NAMES.size]}.com" + + if AUTH_EMAIL.nil? || AUTH_EMAIL.empty? || AUTH_KEY.nil? || AUTH_KEY.empty? + $stderr.puts <<~EOF + Please make sure you have defined CLOUDFLARE_EMAIL and CLOUDFLARE_KEY in your environment. You can also specify CLOUDFLARE_ZONE_NAME to test with your own zone and CLOUDFLARE_ACCOUNT_ID to use a specific account + EOF + end + + AConnection = Sus::Shared("a connection") do + include Sus::Fixtures::Async::ReactorContext + + let(:connection) do + if proxy_url = PROXY_URL + proxy_endpoint = Async::HTTP::Endpoint.parse(proxy_url) + @client = Async::HTTP::Client.new(proxy_endpoint) + @connection = Cloudflare.connect(@client.proxied_endpoint(ENDPOINT), email: AUTH_EMAIL, key: AUTH_KEY) + else + @client = nil + @connection = Cloudflare.connect(email: AUTH_EMAIL, key: AUTH_KEY) + end + end + + let(:account) do + if ACCOUNT_ID + connection.accounts.find_by_id(ACCOUNT_ID) + else + connection.accounts.first + end + end + + let(:job_id) {JOB_ID} + let(:zone_names) {ZONE_NAMES} + let(:zone_name) {ZONE_NAME} + + let(:zones) {connection.zones} + let(:zone) {zones.find_by_name(zone_name) || zones.create(zone_name, account)} + + after do + @connection&.close + @client&.close + end + end +end diff --git a/gems.rb b/gems.rb index 2853f3f..7859daf 100644 --- a/gems.rb +++ b/gems.rb @@ -18,12 +18,12 @@ end group :test do - gem "rspec" + gem "sus" gem "covered" gem "decode" gem "rubocop" - gem "async-rspec" + gem "sus-fixtures-async" gem "sinatra" gem "webmock" diff --git a/lib/cloudflare.rb b/lib/cloudflare.rb index c018dec..e9e9788 100644 --- a/lib/cloudflare.rb +++ b/lib/cloudflare.rb @@ -14,20 +14,18 @@ module Cloudflare def self.connect(*arguments, **auth_info) + connection = Connection.open(*arguments) + + if !auth_info.empty? + connection = connection.authenticated(**auth_info) + end + + return connection unless block_given? + Sync do - connection = Connection.open(*arguments) - - if !auth_info.empty? - connection = connection.authenticated(**auth_info) - end - - return connection unless block_given? - - begin - yield connection - ensure - connection.close - end + yield connection + ensure + connection.close end end end diff --git a/lib/cloudflare/accounts.rb b/lib/cloudflare/accounts.rb index 23bce50..7151d00 100644 --- a/lib/cloudflare/accounts.rb +++ b/lib/cloudflare/accounts.rb @@ -11,7 +11,7 @@ module Cloudflare class Account < Representation def id - value[:id] + result[:id] end def kv_namespaces diff --git a/lib/cloudflare/connection.rb b/lib/cloudflare/connection.rb index 8704538..20eb721 100644 --- a/lib/cloudflare/connection.rb +++ b/lib/cloudflare/connection.rb @@ -24,13 +24,13 @@ def authenticated(token: nil, key: nil, email: nil) headers = {} if token - headers["Authorization"] = "Bearer #{token}" + headers["authorization"] = "bearer #{token}" elsif key if email - headers["X-Auth-Key"] = key - headers["X-Auth-Email"] = email + headers["x-auth-key"] = key + headers["x-auth-email"] = email else - headers["X-Auth-User-Service-Key"] = key + headers["x-auth-user-service-key"] = key end end diff --git a/lib/cloudflare/custom_hostname/ssl_attribute.rb b/lib/cloudflare/custom_hostname/ssl_attribute.rb index f7479af..573c5c8 100644 --- a/lib/cloudflare/custom_hostname/ssl_attribute.rb +++ b/lib/cloudflare/custom_hostname/ssl_attribute.rb @@ -4,7 +4,8 @@ # Copyright, 2019, by Rob Widmer. # Copyright, 2019-2024, by Samuel Williams. -require_relative "./ssl_attribute/settings" +require_relative "ssl_attribute/settings" +require_relative "../representation" module Cloudflare class CustomHostname < Representation diff --git a/lib/cloudflare/custom_hostname/ssl_attribute/settings.rb b/lib/cloudflare/custom_hostname/ssl_attribute/settings.rb index 5747867..c0b5ce4 100644 --- a/lib/cloudflare/custom_hostname/ssl_attribute/settings.rb +++ b/lib/cloudflare/custom_hostname/ssl_attribute/settings.rb @@ -4,11 +4,13 @@ # Copyright, 2019, by Rob Widmer. # Copyright, 2019-2024, by Samuel Williams. +require_relative "../../representation" + module Cloudflare class CustomHostname < Representation class SSLAttribute class Settings - def initialize(settings) + def initialize(settings = {}) @settings = settings end diff --git a/lib/cloudflare/custom_hostnames.rb b/lib/cloudflare/custom_hostnames.rb index 8d3e1bd..3e94b9b 100644 --- a/lib/cloudflare/custom_hostnames.rb +++ b/lib/cloudflare/custom_hostnames.rb @@ -10,70 +10,92 @@ module Cloudflare class CustomHostname < Representation + include Async::REST::Representation::Mutable + # Only available if enabled for your zone def custom_origin - value[:custom_origin_server] + result[:custom_origin_server] end - + # Only available if enabled for your zone def custom_metadata - value[:custom_metadata] + result[:custom_metadata] end - + def hostname - value[:hostname] + result[:hostname] end - + def id - value[:id] + result[:id] end - + def ssl - @ssl ||= SSLAttribute.new(value[:ssl]) + @ssl ||= SSLAttribute.new(result[:ssl]) end - + # Check if the cert has been validated # passing true will send a request to Cloudflare to try to validate the cert def ssl_active?(force_update = false) - send_patch(ssl: { method: ssl.method, type: ssl.type }) if force_update && ssl.pending_validation? - ssl.active? + if force_update && ssl.pending_validation? + self.patch(ssl: {method: ssl.method, type: ssl.type}) + end + + return ssl.active? end - + def update_settings(metadata: nil, origin: nil, ssl: nil) - attrs = {} - attrs[:custom_metadata] = metadata if metadata - attrs[:custom_origin_server] = origin if origin - attrs[:ssl] = ssl if ssl - - send_patch(attrs) + payload = {} + + payload[:custom_metadata] = metadata if metadata + payload[:custom_origin_server] = origin if origin + payload[:ssl] = ssl if ssl + + self.patch(payload) end - + alias :to_s :hostname - + private - - def send_patch(data) - response = patch(data) - - @ssl = nil # Kill off our cached version of the ssl object so it will be regenerated from the response - @value = response.result + + def patch(payload) + self.class.patch(@resource, payload) do |resource, response| + value = response.read + + if value[:sucess] + @ssl = nil + @value = value + else + raise RequestError.new(@resource, value) + end + end end end class CustomHostnames < Representation include Paginate - + def representation CustomHostname end - - # initializes a custom hostname object and yields it for customization before saving - def create(hostname, metadata: nil, origin: nil, ssl: {}, &block) - attrs = { hostname: hostname, ssl: { method: "http", type: "dv" }.merge(ssl) } - attrs[:custom_metadata] = metadata if metadata - attrs[:custom_origin_server] = origin if origin - - represent_message(self.post(attrs)) + + def create(hostname, metadata: nil, origin: nil, ssl: {}, **options) + payload = {hostname: hostname, ssl: {method: "http", type: "dv"}.merge(ssl), **options} + + payload[:custom_metadata] = metadata if metadata + payload[:custom_origin_server] = origin if origin + + CustomHostname.post(@resource, payload) do |resource, response| + value = response.read + result = value[:result] + metadata = response.headers + + if id = result[:id] + resource = resource.with(path: id) + end + + CustomHostname.new(resource, value: value, metadata: metadata) + end end def find_by_hostname(hostname) diff --git a/lib/cloudflare/dns.rb b/lib/cloudflare/dns.rb index 6697172..f5e24cb 100644 --- a/lib/cloudflare/dns.rb +++ b/lib/cloudflare/dns.rb @@ -11,59 +11,74 @@ module Cloudflare module DNS class Record < Representation - def initialize(url, record = nil, **options) - super(url, **options) - - @record = record || get.result - end - + include Async::REST::Representation::Mutable + def update_content(content, **options) - response = put( - type: @record[:type], - name: @record[:name], + self.class.put(@resource, { + type: self.type, + name: self.name, content: content, **options - ) - - @value = response.result + }) do |resource, response| + if response.success? + @value = response.read + @metadata = response.headers + else + raise RequestError.new(resource, response.read) + end + + self + end end - + def type - value[:type] + result[:type] end - + def name - value[:name] + result[:name] end - + def content - value[:content] + result[:content] end - - def proxied - value[:proxied] + + def proxied? + result[:proxied] end - + + alias proxied proxied? + def to_s - "#{@record[:name]} #{@record[:type]} #{@record[:content]}" + "#{self.name} #{self.type} #{self.content}" end end - + class Records < Representation include Paginate - + def representation Record end - - TTL_AUTO = 1 def create(type, name, content, **options) - represent_message(self.post(type: type, name: name, content: content, **options)) + payload = {type: type, name: name, content: content, **options} + + Record.post(@resource, payload) do |resource, response| + value = response.read + result = value[:result] + metadata = response.headers + + if id = result[:id] + resource = resource.with(path: id) + end + + Record.new(resource, value: value, metadata: metadata) + end end def find_by_name(name) - each(name: name).first + each(name: name).find{|record| record.name == name} end end end diff --git a/lib/cloudflare/firewall.rb b/lib/cloudflare/firewall.rb index 7a42de7..3858dbf 100644 --- a/lib/cloudflare/firewall.rb +++ b/lib/cloudflare/firewall.rb @@ -10,18 +10,20 @@ module Cloudflare module Firewall class Rule < Representation + include Async::REST::Representation::Mutable + def mode - value[:mode] + result[:mode] end - + def notes - value[:notes] + result[:notes] end - + def configuration - value[:configuration] + result[:configuration] end - + def to_s "#{configuration[:value]} - #{mode} - #{notes}" end @@ -29,26 +31,29 @@ def to_s class Rules < Representation include Paginate - + def representation Rule end - + def set(mode, value, notes: nil, target: "ip") notes ||= "cloudflare gem [#{mode}] #{Time.now.strftime('%m/%d/%y')}" - - message = self.post({ - mode: mode.to_s, - notes: notes, - configuration: { - target: target, - value: value.to_s, - } - }) - - represent_message(message) + + payload = {mode: mode.to_s, notes: notes, configuration: {target: target, value: value.to_s}} + + Rule.post(@resource, payload) do |resource, response| + value = response.read + result = value[:result] + metadata = response.headers + + if id = result[:id] + resource = resource.with(path: id) + end + + Rule.new(resource, value: value, metadata: metadata) + end end - + def each_by_value(value, &block) each(configuration_value: value, &block) end diff --git a/lib/cloudflare/kv/namespaces.rb b/lib/cloudflare/kv/namespaces.rb index 2570ba1..a62f936 100644 --- a/lib/cloudflare/kv/namespaces.rb +++ b/lib/cloudflare/kv/namespaces.rb @@ -7,73 +7,105 @@ require_relative "../paginate" require_relative "../representation" -require_relative "rest_wrapper" +require_relative "wrapper" module Cloudflare module KV class Key < Representation def name - value[:name] + result[:name] end end - + + class Value < Representation[Wrapper] + include Async::REST::Representation::Mutable + + def put(value) + self.class.put(@resource, value) do |resource, response| + value = response.read + + return value[:success] + end + end + end + class Keys < Representation include Paginate - + def representation Key end end - + class Namespace < Representation + include Async::REST::Representation::Mutable + def delete_value(name) value_representation(name).delete.success? end - + def id - value[:id] + result[:id] end - + def keys - self.with(Keys, path: "keys/") + self.with(Keys, path: "keys") end - + def read_value(name) value_representation(name).value end - + def rename(new_title) - put(title: new_title) - value[:title] = new_title + self.class.put(@resource, title: new_title) do |resource, response| + value = response.read + + if value[:success] + result[:title] = new_title + else + raise RequestError.new(resource, value) + end + end end - + def title - value[:title] + result[:title] end - + def write_value(name, value) - value_representation(name).put(value).success? + value_representation(name).put(value) end - + private - + def value_representation(name) - @representation_class ||= Representation[RESTWrapper] - self.with(@representation_class, path: "values/#{name}/") + self.with(Value, path: "values/#{name}/") end end - + class Namespaces < Representation include Paginate - + def representation Namespace end - - def create(title) - represent_message(post(title: title)) + + def create(title, **options) + payload = {title: title, **options} + + Namespace.post(@resource, payload) do |resource, response| + value = response.read + result = value[:result] + metadata = response.headers + + if id = result[:id] + resource = resource.with(path: id) + end + + Namespace.new(resource, value: value, metadata: metadata) + end end - + def find_by_title(title) each.find {|namespace| namespace.title == title } end diff --git a/lib/cloudflare/kv/rest_wrapper.rb b/lib/cloudflare/kv/rest_wrapper.rb deleted file mode 100644 index 70c34a6..0000000 --- a/lib/cloudflare/kv/rest_wrapper.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -# Released under the MIT License. -# Copyright, 2021, by Terry Kerr. -# Copyright, 2024, by Samuel Williams. - -require "json" - -module Cloudflare - module KV - class RESTWrapper < Async::REST::Wrapper::Generic - APPLICATION_OCTET_STREAM = "application/octet-stream" - APPLICATION_JSON = "application/json" - ACCEPT_HEADER = "#{APPLICATION_JSON}, #{APPLICATION_OCTET_STREAM}" - - def prepare_request(payload, headers) - headers["accept"] ||= ACCEPT_HEADER - - if payload - headers["content-type"] = APPLICATION_OCTET_STREAM - ::Protocol::HTTP::Body::Buffered.new([payload.to_s]) - end - end - - def parser_for(response) - if response.headers["content-type"].start_with?(APPLICATION_OCTET_STREAM) - OctetParser - elsif response.headers["content-type"].start_with?(APPLICATION_JSON) - JsonParser - else - Async::REST::Wrapper::Generic::Unsupported - end - end - - class OctetParser < ::Protocol::HTTP::Body::Wrapper - def join - super - end - end - - class JsonParser < ::Protocol::HTTP::Body::Wrapper - def join - JSON.parse(super, symbolize_names: true) - end - end - end - end -end diff --git a/lib/cloudflare/kv/wrapper.rb b/lib/cloudflare/kv/wrapper.rb new file mode 100644 index 0000000..ffa4572 --- /dev/null +++ b/lib/cloudflare/kv/wrapper.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2021, by Terry Kerr. +# Copyright, 2024, by Samuel Williams. + +require "json" + +module Cloudflare + module KV + class Wrapper < Cloudflare::Wrapper + APPLICATION_OCTET_STREAM = "application/octet-stream" + def prepare_request(request, payload) + request.headers.add("accept", APPLICATION_OCTET_STREAM) + + if payload + request.headers["content-type"] = APPLICATION_OCTET_STREAM + + request.body = ::Protocol::HTTP::Body::Buffered.new([payload.to_s]) + end + end + + def parser_for(response) + if response.headers["content-type"].start_with?(APPLICATION_OCTET_STREAM) + OctetParser + else + super + end + end + + class OctetParser < ::Protocol::HTTP::Body::Wrapper + def join + super.force_encoding(Encoding::BINARY) + end + end + end + end +end diff --git a/lib/cloudflare/logs.rb b/lib/cloudflare/logs.rb index 58aefb6..e20be9d 100644 --- a/lib/cloudflare/logs.rb +++ b/lib/cloudflare/logs.rb @@ -10,7 +10,7 @@ module Cloudflare module Logs class Entry < Representation def to_s - "#{value[:rayid]}-#{value[:ClientRequestURI]}" + "#{result[:rayid]}-#{result[:ClientRequestURI]}" end end @@ -23,4 +23,3 @@ def representation end end end - diff --git a/lib/cloudflare/paginate.rb b/lib/cloudflare/paginate.rb index 6013927..25af9a0 100644 --- a/lib/cloudflare/paginate.rb +++ b/lib/cloudflare/paginate.rb @@ -12,7 +12,9 @@ def each(page: 1, per_page: 50, **parameters) return to_enum(:each, page: page, per_page: per_page, **parameters) unless block_given? while true - response = self.class.get(@resource, {page: page, per_page: per_page, **parameters}) + resource = @resource.with(parameters: {page: page, per_page: per_page, **parameters}) + + response = self.class.get(resource) break if response.empty? diff --git a/lib/cloudflare/representation.rb b/lib/cloudflare/representation.rb index 1431921..30c1a4f 100644 --- a/lib/cloudflare/representation.rb +++ b/lib/cloudflare/representation.rb @@ -12,30 +12,34 @@ module Cloudflare class RequestError < StandardError - def initialize(resource, errors) - super("#{resource}: #{errors.map{|attributes| attributes[:message]}.join(', ')}") + def initialize(request, value) + if error = value[:error] + super("#{request}: #{error}") + elsif errors = value[:errors] + super("#{request}: #{errors.map{|attributes| attributes[:message]}.join(', ')}") + else + super("#{request}: #{value.inspect}") + end - @representation = representation + @value = value end - attr_reader :representation + attr :value end class Wrapper < Async::REST::Wrapper::JSON + def process_response(request, response) + super + + if response.failure? + raise RequestError.new(request, response.read) + end + end end class Representation < Async::REST::Representation WRAPPER = Wrapper.new - def initialize(...) - super(...) - - # Some endpoints return the value instead of a message object (like KV reads) - unless @value.is_a?(Hash) - @value = {success: true, result: @value} - end - end - def representation Representation end @@ -43,25 +47,25 @@ def representation def represent(metadata, attributes) resource = @resource.with(path: attributes[:id]) - representation.new(resource, metadata: metadata, value: attributes) + representation.new(resource, metadata: metadata, value: { + success: true, result: attributes + }) end def represent_message(message) represent(message.headers, message.result) end - def to_hash - if value.is_a?(Hash) - return value - end - end - def result value[:result] end - def read - value[:result] + def to_hash + result + end + + def to_id + {id: result[:id]} end def results diff --git a/lib/cloudflare/rspec/connection.rb b/lib/cloudflare/rspec/connection.rb deleted file mode 100644 index 0c1a180..0000000 --- a/lib/cloudflare/rspec/connection.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -# Released under the MIT License. -# Copyright, 2017-2024, by Samuel Williams. -# Copyright, 2018, by Leonhardt Wille. - -require "async/rspec" -require "async/http/proxy" - -require_relative "../../cloudflare" - -module Cloudflare - module RSpec - module Connection - end - - RSpec.shared_context Connection do - include_context Async::RSpec::Reactor - - # You must specify these in order for the tests to run. - let(:email) {ENV["CLOUDFLARE_EMAIL"]} - let(:key) {ENV["CLOUDFLARE_KEY"]} - - let(:connection) do - if proxy_url = ENV["CLOUDFLARE_PROXY"] - proxy_endpoint = Async::HTTP::Endpoint.parse(proxy_url) - @client = Async::HTTP::Client.new(proxy_endpoint) - @connection = Cloudflare.connect(@client.proxied_endpoint(DEFAULT_ENDPOINT), key: key, email: email) - else - @client = nil - @connection = Cloudflare.connect(key: key, email: email) - end - end - - after do - @connection&.close - @client&.close - end - end - end -end diff --git a/lib/cloudflare/user.rb b/lib/cloudflare/user.rb index dde3d18..a02ea1e 100644 --- a/lib/cloudflare/user.rb +++ b/lib/cloudflare/user.rb @@ -9,11 +9,11 @@ module Cloudflare class User < Representation def id - value[:id] + result[:id] end def email - value[:email] + result[:email] end end end diff --git a/lib/cloudflare/zones.rb b/lib/cloudflare/zones.rb index 73a12a3..419502f 100644 --- a/lib/cloudflare/zones.rb +++ b/lib/cloudflare/zones.rb @@ -22,34 +22,38 @@ module Cloudflare class Zone < Representation + include Async::REST::Representation::Mutable + def custom_hostnames - self.with(CustomHostnames, path: "custom_hostnames/") + self.with(CustomHostnames, path: "custom_hostnames") end def dns_records - self.with(DNS::Records, path: "dns_records/") + self.with(DNS::Records, path: "dns_records") end def firewall_rules - self.with(Firewall::Rules, path: "firewall/access_rules/rules/") + self.with(Firewall::Rules, path: "firewall/access_rules/rules") end def logs - self.with(Logs::Received, path: "logs/received/") + self.with(Logs::Received, path: "logs/received") end - DEFAULT_PURGE_CACHE_PARAMS = { + DEFAULT_PURGE_CACHE_PARAMETERS = { purge_everything: true }.freeze - def purge_cache(parameters = DEFAULT_PURGE_CACHE_PARAMS) - self.with(Zone, path: "purge_cache").post(parameters) + def purge_cache(**options) + if options.empty? + options = DEFAULT_PURGE_CACHE_PARAMETERS + end - return self + self.class.post(@resource.with(path: "purge_cache"), options) end def name - value[:name] + result[:name] end alias to_s name @@ -62,12 +66,24 @@ def representation Zone end - def create(name, account, jump_start = false) - represent_message(self.post(name: name, account: account.to_hash, jump_start: jump_start)) + def create(name, account, jump_start: false, **options) + payload = {name: name, account: account.to_id, jump_start: jump_start, **options} + + Zone.post(@resource, payload) do |resource, response| + value = response.read + result = value[:result] + metadata = response.headers + + if id = result[:id] + resource = resource.with(path: id) + end + + Zone.new(resource, value: value, metadata: metadata) + end end - + def find_by_name(name) - each(name: name).first + each(name: name).find{|zone| zone.name == name} end end end diff --git a/spec/cloudflare/accounts_spec.rb b/spec/cloudflare/accounts_spec.rb deleted file mode 100644 index c2700ff..0000000 --- a/spec/cloudflare/accounts_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true - -# Released under the MIT License. -# Copyright, 2019, by Rob Widmer. -# Copyright, 2024, by Samuel Williams. - -RSpec.describe Cloudflare::Accounts, order: :defined, timeout: 30 do - include_context Cloudflare::Account - - before do - account.id # Force a fetch if it hasn't happened yet - end - - it "can list existing accounts" do - accounts = connection.accounts.to_a - expect(accounts.any? {|a| a.id == account.id }).to be true - end - - it "can get a specific account" do - expect(connection.accounts.find_by_id(account.id).id).to eq account.id - end - - it "can generate a representation for the KV namespace endpoint" do - ns = connection.accounts.find_by_id(account.id).kv_namespaces - expect(ns).to be_kind_of(Cloudflare::KV::Namespaces) - expect(ns.resource.reference.path).to end_with("/#{account.id}/storage/kv/namespaces") - end -end diff --git a/spec/cloudflare/custom_hostname/ssl_attribute/settings_spec.rb b/spec/cloudflare/custom_hostname/ssl_attribute/settings_spec.rb deleted file mode 100644 index 7297a00..0000000 --- a/spec/cloudflare/custom_hostname/ssl_attribute/settings_spec.rb +++ /dev/null @@ -1,60 +0,0 @@ -# frozen_string_literal: true - -# Released under the MIT License. -# Copyright, 2019, by Rob Widmer. -# Copyright, 2024, by Samuel Williams. - -RSpec.describe Cloudflare::CustomHostname::SSLAttribute::Settings do - - subject { described_class.new({}) } - - it "has an accessor for ciphers" do - ciphers = double - expect(subject.ciphers).to be_nil - subject.ciphers = ciphers - expect(subject.ciphers).to be ciphers - end - - it "has a boolean accessor for http2" do - expect(subject.http2).to be_nil - expect(subject.http2?).to be false - subject.http2 = true - expect(subject.http2).to eq "on" - expect(subject.http2?).to be true - subject.http2 = false - expect(subject.http2).to eq "off" - expect(subject.http2?).to be false - subject.http2 = "on" - expect(subject.http2).to eq "on" - expect(subject.http2?).to be true - subject.http2 = "off" - expect(subject.http2).to eq "off" - expect(subject.http2?).to be false - end - - it "has an accessor for min_tls_version" do - tls_version = double - expect(subject.min_tls_version).to be_nil - subject.min_tls_version = tls_version - expect(subject.min_tls_version).to be tls_version - end - - it "has a boolean accessor for tls_1_3" do - expect(subject.tls_1_3).to be_nil - expect(subject.tls_1_3?).to be false - subject.tls_1_3 = true - expect(subject.tls_1_3).to eq "on" - expect(subject.tls_1_3?).to be true - subject.tls_1_3 = false - expect(subject.tls_1_3).to eq "off" - expect(subject.tls_1_3?).to be false - subject.tls_1_3 = "on" - expect(subject.tls_1_3).to eq "on" - expect(subject.tls_1_3?).to be true - subject.tls_1_3 = "off" - expect(subject.tls_1_3).to eq "off" - expect(subject.tls_1_3?).to be false - end - - -end diff --git a/spec/cloudflare/custom_hostname/ssl_attribute_spec.rb b/spec/cloudflare/custom_hostname/ssl_attribute_spec.rb deleted file mode 100644 index 84653bb..0000000 --- a/spec/cloudflare/custom_hostname/ssl_attribute_spec.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true - -# Released under the MIT License. -# Copyright, 2019, by Rob Widmer. -# Copyright, 2024, by Samuel Williams. - -RSpec.describe Cloudflare::CustomHostname::SSLAttribute do - - accessors = [:cname, :cname_target, :http_body, :http_url, :method, :status, :type, :validation_errors] - - let(:original_hash) { {} } - - subject { described_class.new(original_hash) } - - accessors.each do |key| - - it "has an accessor for the #{key} value" do - test_value = double - expect(subject.send(key)).to be_nil - original_hash[key] = test_value - expect(subject.send(key)).to be test_value - end - - end - - it '#active? returns true when the status is "active" and false otherwise' do - expect(subject.active?).to be false - original_hash[:status] = "initializing" - expect(subject.active?).to be false - original_hash[:status] = "pending_validation" - expect(subject.active?).to be false - original_hash[:status] = "pending_deployment" - expect(subject.active?).to be false - original_hash[:status] = "active" - expect(subject.active?).to be true - end - - it '#pending_validation? returns true when the status is "pending_validation" and false otherwise' do - expect(subject.pending_validation?).to be false - original_hash[:status] = "initializing" - expect(subject.pending_validation?).to be false - original_hash[:status] = "active" - expect(subject.pending_validation?).to be false - original_hash[:status] = "pending_deployment" - expect(subject.pending_validation?).to be false - original_hash[:status] = "pending_validation" - expect(subject.pending_validation?).to be true - end - - describe "#settings" do - - it "should return a Settings object" do - expect(subject.settings).to be_kind_of Cloudflare::CustomHostname::SSLAttribute::Settings - end - - it "initailizes the settings object with the value from the settings key" do - settings = { min_tls_version: double } - original_hash[:settings] = settings - expect(subject.settings.min_tls_version).to be settings[:min_tls_version] - end - - it "initializes the settings object with a new hash if the settings key does not exist" do - expected_value = double - expect(original_hash[:settings]).to be_nil - expect(subject.settings.min_tls_version).to be_nil - expect(original_hash[:settings]).not_to be_nil - original_hash[:settings][:min_tls_version] = expected_value - expect(subject.settings.min_tls_version).to be expected_value - end - - it "updates the stored hash with values set on the settings object" do - expected_value = double - expect(subject.settings.min_tls_version).to be_nil - subject.settings.min_tls_version = expected_value - expect(original_hash[:settings][:min_tls_version]).to be expected_value - end - end - -end diff --git a/spec/cloudflare/custom_hostnames_spec.rb b/spec/cloudflare/custom_hostnames_spec.rb deleted file mode 100644 index 11e9411..0000000 --- a/spec/cloudflare/custom_hostnames_spec.rb +++ /dev/null @@ -1,216 +0,0 @@ -# frozen_string_literal: true - -# Released under the MIT License. -# Copyright, 2019, by Rob Widmer. -# Copyright, 2019-2024, by Samuel Williams. - -RSpec.xdescribe Cloudflare::CustomHostnames, order: :defined, timeout: 30 do - include_context Cloudflare::Zone - - let(:domain) { "www-#{job_id}.example.com" } - - let(:record) { @record = zone.custom_hostnames.create(domain) } - - let(:custom_origin) do - subdomain = "origin-#{job_id}" - @dns_record = zone.dns_records.create("A", subdomain, "1.2.3.4") # This needs to exist or the calls will fail - "#{subdomain}.#{zone.name}" - end - - after do - if defined? @record - expect(@record.delete).to be_success - end - - if defined? @dns_record - expect(@dns_record.delete).to be_success - end - end - - it "can create a custom hostname record" do - expect(record).to be_kind_of Cloudflare::CustomHostname - expect(record.custom_metadata).to be_nil - expect(record.hostname).to eq domain - expect(record.custom_origin).to be_nil - expect(record.ssl.method).to eq "http" - expect(record.ssl.type).to eq "dv" - end - - it "can create a custom hostname record with a custom origin" do - begin - @record = zone.custom_hostnames.create(domain, origin: custom_origin) - - expect(@record).to be_kind_of Cloudflare::CustomHostname - expect(@record.custom_metadata).to be_nil - expect(@record.hostname).to eq domain - expect(@record.custom_origin).to eq custom_origin - expect(@record.ssl.method).to eq "http" - expect(@record.ssl.type).to eq "dv" - rescue Cloudflare::RequestError => e - if e.message.include?("custom origin server has not been granted") - skip(e.message) # This currently doesn't work but might start eventually: https://github.com/socketry/async-rspec/issues/7 - else - raise - end - end - end - - it "can create a custom hostname record with different ssl options" do - @record = zone.custom_hostnames.create(domain, ssl: { method: "cname" }) - - expect(@record).to be_kind_of Cloudflare::CustomHostname - expect(@record.custom_metadata).to be_nil - expect(@record.hostname).to eq domain - expect(@record.custom_origin).to be_nil - expect(@record.ssl.method).to eq "cname" - expect(@record.ssl.type).to eq "dv" - end - - it "can create a custom hostname record with additional metadata" do - metadata = { a: rand(1..10) } - - begin - @record = zone.custom_hostnames.create(domain, metadata: metadata) - - expect(@record).to be_kind_of Cloudflare::CustomHostname - expect(@record.custom_metadata).to eq metadata - expect(@record.hostname).to eq domain - expect(@record.custom_origin).to be_nil - expect(@record.ssl.method).to eq "http" - expect(@record.ssl.type).to eq "dv" - rescue Cloudflare::RequestError => e - if e.message.include?("No custom metadata access has been allocated for this zone") - skip(e.message) # This currently doesn't work but might start eventually: https://github.com/socketry/async-rspec/issues/7 - else - raise - end - end - end - - it "can look up an existing custom hostname by the hostname or id" do - expect(zone.custom_hostnames.find_by_hostname(record.hostname).id).to eq record.id - expect(zone.custom_hostnames.find_by_id(record.id).id).to eq record.id - end - - context "with existing record" do - - it "returns the hostname when calling #to_s" do - expect(record.to_s).to eq domain - end - - it "can update metadata" do - metadata = { c: rand(1..10) } - - expect(record.custom_metadata).to be_nil - - begin - record.update_settings(metadata: metadata) - - # Make sure the existing object is updated - expect(record.custom_metadata).to eq metadata - - # Verify that the server has the changes - found_record = zone.custom_hostnames.find_by_id(record.id) - - expect(found_record.custom_metadata).to eq metadata - expect(found_record.hostname).to eq domain - expect(found_record.custom_origin).to be_nil - rescue Cloudflare::RequestError => e - if e.message.include?("No custom metadata access has been allocated for this zone") - skip(e.message) # This currently doesn't work but might start eventually: https://github.com/socketry/async-rspec/issues/7 - else - raise - end - end - end - - it "can update the custom origin" do - expect(record.custom_origin).to be_nil - - begin - record.update_settings(origin: custom_origin) - - # Make sure the existing object is updated - expect(record.custom_origin).to eq custom_origin - - # Verify that the server has the changes - found_record = zone.custom_hostnames.find_by_id(record.id) - - expect(found_record.custom_metadata).to be_nil - expect(found_record.hostname).to eq domain - expect(found_record.custom_origin).to eq custom_origin - rescue Cloudflare::RequestError => e - if e.message.include?("custom origin server has not been granted") - skip(e.message) # This currently doesn't work but might start eventually: https://github.com/socketry/async-rspec/issues/7 - else - raise - end - end - end - - it "can update ssl information" do - expect(record.ssl.method).to eq "http" - - record.update_settings(ssl: { method: "cname", type: "dv" }) - - # Make sure the existing object is updated - expect(record.ssl.method).to eq "cname" - - # Verify that the server has the changes - found_record = zone.custom_hostnames.find_by_id(record.id) - - expect(found_record.custom_metadata).to be_nil - expect(found_record.hostname).to eq domain - expect(found_record.custom_origin).to be_nil - expect(found_record.ssl.method).to eq "cname" - end - - context "has an ssl section" do - - it "wraps it in an SSLAttributes object" do - expect(record.ssl).to be_kind_of Cloudflare::CustomHostname::SSLAttribute - end - - it "has some helpers for commonly used keys" do - # Make sure our values exist before we check to make sure that they are returned correctly - expect(record.value[:ssl].values_at(:method, :http_body, :http_url).compact).not_to be_empty - expect(record.ssl.method).to be record.value[:ssl][:method] - expect(record.ssl.http_body).to be record.value[:ssl][:http_body] - expect(record.ssl.http_url).to be record.value[:ssl][:http_url] - end - - end - - describe "#ssl_active?" do - - it "returns the result of calling ssl.active?" do - expected_value = double - expect(record.ssl).to receive(:active?).and_return(expected_value) - expect(record).not_to receive(:send_patch) - expect(record.ssl_active?).to be expected_value - end - - it "returns the result of calling ssl.active? without triggering an update if force_update is true and the ssl is not in the pending_validation state" do - expected_value = double - expect(record.ssl).to receive(:active?).and_return(expected_value) - expect(record.ssl.method).not_to be_nil - expect(record.ssl.type).not_to be_nil - expect(record.ssl.pending_validation?).to be false - expect(record).not_to receive(:send_patch).with(ssl: { method: record.ssl.method, type: record.ssl.type }) - expect(record.ssl_active?(true)).to be expected_value - end - - it "returns the result of calling ssl.active? after triggering an update if force_update is true and the ssl is in the pending_validation state" do - expected_value = double - expect(record.ssl).to receive(:active?).and_return(expected_value) - expect(record.ssl.method).not_to be_nil - expect(record.ssl.type).not_to be_nil - record.value[:ssl][:status] = "pending_validation" - expect(record).to receive(:send_patch).with(ssl: { method: record.ssl.method, type: record.ssl.type }) - expect(record.ssl_active?(true)).to be expected_value - end - - end - - end -end diff --git a/spec/cloudflare/dns_spec.rb b/spec/cloudflare/dns_spec.rb deleted file mode 100644 index 28f03a6..0000000 --- a/spec/cloudflare/dns_spec.rb +++ /dev/null @@ -1,55 +0,0 @@ -# frozen_string_literal: true - -# Released under the MIT License. -# Copyright, 2019-2024, by Samuel Williams. -# Copyright, 2019, by David Wegman. - -require "cloudflare/rspec/connection" - -RSpec.describe Cloudflare::DNS, order: :defined, timeout: 30 do - include_context Cloudflare::Zone - - let(:subdomain) {"www-#{job_id}"} - - after do - if defined? @record - expect(@record.delete).to be_success - end - end - - context "new record" do - it "can create dns record" do - @record = zone.dns_records.create("A", subdomain, "1.2.3.4") - expect(@record.type).to be == "A" - expect(@record.name).to be_start_with subdomain - expect(@record.content).to be == "1.2.3.4" - end - - it "can create dns record with proxied option" do - @record = zone.dns_records.create("A", subdomain, "1.2.3.4", proxied: true) - expect(@record.type).to be == "A" - expect(@record.name).to be_start_with subdomain - expect(@record.content).to be == "1.2.3.4" - expect(@record.proxied).to be_truthy - end - end - - context "with existing record" do - let(:record) {@record = zone.dns_records.create("A", subdomain, "1.2.3.4")} - it "can update dns content" do - record.update_content("4.3.2.1") - expect(record.content).to be == "4.3.2.1" - - fetched_record = zone.dns_records.find_by_name(record.name) - expect(fetched_record.content).to be == record.content - end - - it "can update dns content with proxied option" do - record.update_content("4.3.2.1", proxied: true) - expect(record.proxied).to be_truthy - - fetched_record = zone.dns_records.find_by_name(record.name) - expect(fetched_record.proxied).to be_truthy - end - end -end diff --git a/spec/cloudflare/kv/namespaces_spec.rb b/spec/cloudflare/kv/namespaces_spec.rb deleted file mode 100644 index ce69086..0000000 --- a/spec/cloudflare/kv/namespaces_spec.rb +++ /dev/null @@ -1,76 +0,0 @@ -# frozen_string_literal: true - -# Released under the MIT License. -# Copyright, 2019, by Rob Widmer. -# Copyright, 2019-2024, by Samuel Williams. - -RSpec.describe Cloudflare::KV::Namespaces, kv_spec: true, order: :defined, timeout: 30 do - include_context Cloudflare::Account - - let(:namespace) { @namespace = account.kv_namespaces.create(namespace_title) } - let(:namespace_title) { "Test NS ##{rand(1..100)}" } - - after do - if defined? @namespace - expect(@namespace.delete).to be_success - end - end - - it "can create a namespace" do - expect(namespace).to be_kind_of Cloudflare::KV::Namespace - expect(namespace.id).not_to be_nil - expect(namespace.title).to eq namespace_title - end - - it "can find a namespace by title" do - namespace # Call this so that the namespace gets created - expect(account.kv_namespaces.find_by_title(namespace_title).id).to eq namespace.id - end - - it "can rename the namespace" do - new_title = "#{namespace_title}-#{rand(1..100)}" - namespace.rename(new_title) - expect(namespace.title).to eq new_title - expect(account.kv_namespaces.find_by_title(new_title).id).to eq namespace.id - expect(account.kv_namespaces.find_by_title(namespace_title)).to be_nil - end - - it "can store a key/value, read it back" do - key = "key-#{rand(1..100)}" - value = rand(100..999) - namespace.write_value(key, value) - expect(account.kv_namespaces.find_by_id(namespace.id).read_value(key)).to eq value.to_s - end - - it "can read a previously stored key" do - key = "key-#{rand(1..100)}" - value = rand(100..999) - expect(account.kv_namespaces.find_by_id(namespace.id).write_value(key, value)).to be true - expect(namespace.read_value(key)).to eq value.to_s - end - - it "can delete keys" do - key = "key-#{rand(1..100)}" - value = rand(100..999) - expect(namespace.write_value(key, value)).to be true - expect(namespace.read_value(key)).to eq value.to_s - expect(namespace.delete_value(key)).to be true - expect do - account.kv_namespaces.find_by_id(namespace.id).read_value(key) - end.to raise_error(Cloudflare::RequestError) - end - - it "can get the keys that exist in the namespace" do - counter = 0 - keys = Array.new(rand(1..9)) { "key-#{counter += 1}" } # Keep this single digits so ordering works - keys.each_with_index do |key, i| - namespace.write_value(key, i) - end - - saved_keys = account.kv_namespaces.find_by_id(namespace.id).keys.to_a - expect(saved_keys.length).to eq keys.length - saved_keys.each_with_index do |key, i| - expect(key.name).to eq keys[i] - end - end -end diff --git a/spec/cloudflare/zone_spec.rb b/spec/cloudflare/zone_spec.rb deleted file mode 100644 index 3bf4013..0000000 --- a/spec/cloudflare/zone_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -# Released under the MIT License. -# Copyright, 2017-2024, by Samuel Williams. -# Copyright, 2018, by Leonhardt Wille. -# Copyright, 2018, by Michael Kalygin. -# Copyright, 2018, by Sherman Koa. -# Copyright, 2019, by Rob Widmer. - -RSpec.describe Cloudflare::Zones, order: :defined, timeout: 30 do - include_context Cloudflare::Zone - - if ENV["CLOUDFLARE_TEST_ZONE_MANAGEMENT"] == "true" - it "can delete existing domain if exists" do - if zone = zones.find_by_name(name) - expect(zone.delete).to be_success - end - end - - it "can create a zone" do - zone = zones.create(name, account) - expect(zone.value).to include(:id) - end - end - - it "can list zones" do - matching_zones = zones.select{|zone| zone.name == name} - expect(matching_zones).to_not be_empty - end - - it "can get zone by name" do - found_zone = zones.find_by_name(name) - expect(found_zone.name).to be == name - end -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb deleted file mode 100644 index 5586ca8..0000000 --- a/spec/spec_helper.rb +++ /dev/null @@ -1,93 +0,0 @@ -# frozen_string_literal: true - -# Released under the MIT License. -# Copyright, 2017-2024, by Samuel Williams. -# Copyright, 2018, by Leonhardt Wille. -# Copyright, 2018, by Michael Kalygin. -# Copyright, 2018, by Sherman Koa. -# Copyright, 2019, by Rob Widmer. - -AUTH_EMAIL = ENV["CLOUDFLARE_EMAIL"] -AUTH_KEY = ENV["CLOUDFLARE_KEY"] - -if AUTH_EMAIL.nil? || AUTH_EMAIL.empty? || AUTH_KEY.nil? || AUTH_KEY.empty? - puts "Please make sure you have defined CLOUDFLARE_EMAIL and CLOUDFLARE_KEY in your environment" - puts "You can also specify CLOUDFLARE_ZONE_NAME to test with your own zone and" - puts "CLOUDFLARE_ACCOUNT_ID to use a specific account" - exit(1) -end - -ACCOUNT_ID = ENV["CLOUDFLARE_ACCOUNT_ID"] -NAMES = %w{alligator ant bear bee bird camel cat cheetah chicken chimpanzee cow crocodile deer dog dolphin duck eagle elephant fish fly fox frog giraffe goat goldfish hamster hippopotamus horse kangaroo kitten lion lobster monkey octopus owl panda pig puppy rabbit rat scorpion seal shark sheep snail snake spider squirrel tiger turtle wolf zebra} -JOB_ID = ENV.fetch("INVOCATION_ID", "testing").hash -ZONE_NAME = ENV["CLOUDFLARE_ZONE_NAME"] || "#{NAMES[JOB_ID % NAMES.size]}.com" - -$stderr.puts "Using zone name: #{ZONE_NAME}" - -require "covered/rspec" -require "async/rspec" - -require "cloudflare/rspec/connection" -require "cloudflare/zones" - -RSpec.shared_context Cloudflare::Account do - include_context Cloudflare::RSpec::Connection - - let(:account) do - if ACCOUNT_ID - connection.accounts.find_by_id(ACCOUNT_ID) - else - connection.accounts.first - end - end -end - -RSpec.shared_context Cloudflare::Zone do - include_context Cloudflare::Account - - let(:job_id) {JOB_ID} - let(:names) {NAMES.dup} - let(:name) {ZONE_NAME.dup} - - let(:zones) {connection.zones} - - let(:zone) {@zone = zones.find_by_name(name) || zones.create(name, account)} - - # after do - # if defined? @zone - # @zone.delete - # end - # end -end - -RSpec.configure do |config| - # Enable flags like --only-failures and --next-failure - config.example_status_persistence_file_path = ".rspec_status" - - config.expect_with :rspec do |c| - c.syntax = :expect - end - - disabled_specs = {} - - # Check for features the current account has enabled - Cloudflare.connect(key: AUTH_KEY, email: AUTH_EMAIL) do |conn| - begin - account = if ACCOUNT_ID - conn.accounts.find_by_id(ACCOUNT_ID) - else - conn.accounts.first - end - account.kv_namespaces.to_a - rescue Cloudflare::RequestError => e - if e.message.include?("your account is not entitled") - puts "Disabling KV specs due to no access" - disabled_specs[:kv_spec] = true - else - raise - end - end - end - - config.filter_run_excluding disabled_specs unless disabled_specs.empty? -end diff --git a/test/cloudflare/accounts.rb b/test/cloudflare/accounts.rb new file mode 100644 index 0000000..d6064a1 --- /dev/null +++ b/test/cloudflare/accounts.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019, by Rob Widmer. +# Copyright, 2024, by Samuel Williams. + +require "cloudflare/accounts" +require "cloudflare/a_connection" + +describe Cloudflare::Accounts do + include_context Cloudflare::AConnection + + before do + # Force a fetch if it hasn't happened yet: + account.id + end + + it "can list existing accounts" do + accounts = connection.accounts.to_a + + expect(accounts).to have_value(have_attributes( + id: be == account.id + )) + end + + it "can get a specific account" do + fetched_account = connection.accounts.find_by_id(account.id) + + expect(fetched_account.id).to be == account.id + end + + it "can generate a representation for the KV namespace endpoint" do + namespace = connection.accounts.find_by_id(account.id).kv_namespaces + + expect(namespace).to be_a(Cloudflare::KV::Namespaces) + + expect(namespace.resource.reference.path).to be(:end_with?, "/#{account.id}/storage/kv/namespaces") + end +end diff --git a/test/cloudflare/connection.rb b/test/cloudflare/connection.rb new file mode 100644 index 0000000..163e821 --- /dev/null +++ b/test/cloudflare/connection.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2024, by Samuel Williams. + +require "cloudflare/accounts" +require "cloudflare/a_connection" + +describe Cloudflare::Connection do + include_context Cloudflare::AConnection + + with "#user" do + it "can get the current user" do + user = connection.user + + expect(user).to have_attributes( + id: be =~ /\A[a-f0-9]{32}\z/, + ) + end + end +end diff --git a/test/cloudflare/custom_hostname/ssl_attribute.rb b/test/cloudflare/custom_hostname/ssl_attribute.rb new file mode 100644 index 0000000..c091344 --- /dev/null +++ b/test/cloudflare/custom_hostname/ssl_attribute.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019, by Rob Widmer. +# Copyright, 2024, by Samuel Williams. + +require "cloudflare/custom_hostname/ssl_attribute" + +ACCESSORS = [:cname, :cname_target, :http_body, :http_url, :method, :status, :type, :validation_errors] + +describe Cloudflare::CustomHostname::SSLAttribute do + let(:original_hash) {Hash.new} + let(:attribute) {subject.new(original_hash)} + + ACCESSORS.each do |key| + it "has an accessor for the #{key} value", unique: key do + test_value = Object.new + expect(attribute.send(key)).to be_nil + + original_hash[key] = test_value + expect(attribute.send(key)).to be == test_value + end + end + + it '#active? returns true when the status is "active" and false otherwise' do + expect(attribute.active?).to be == false + original_hash[:status] = "initializing" + expect(attribute.active?).to be == false + original_hash[:status] = "pending_validation" + expect(attribute.active?).to be == false + original_hash[:status] = "pending_deployment" + expect(attribute.active?).to be == false + original_hash[:status] = "active" + expect(attribute.active?).to be == true + end + + it '#pending_validation? returns true when the status is "pending_validation" and false otherwise' do + expect(attribute.pending_validation?).to be == false + original_hash[:status] = "initializing" + expect(attribute.pending_validation?).to be == false + original_hash[:status] = "active" + expect(attribute.pending_validation?).to be == false + original_hash[:status] = "pending_deployment" + expect(attribute.pending_validation?).to be == false + original_hash[:status] = "pending_validation" + expect(attribute.pending_validation?).to be == true + end + + with "#settings" do + it "should return a Settings object" do + expect(attribute.settings).to be_a Cloudflare::CustomHostname::SSLAttribute::Settings + end + + it "initailizes the settings object with the value from the settings key" do + settings = {min_tls_version: Object.new} + + original_hash[:settings] = settings + + expect(attribute.settings.min_tls_version).to be == settings[:min_tls_version] + end + + it "initializes the settings object with a new hash if the settings key does not exist" do + expected_value = Object.new + + expect(original_hash[:settings]).to be_nil + expect(attribute.settings.min_tls_version).to be_nil + expect(original_hash[:settings]).not.to be_nil + original_hash[:settings][:min_tls_version] = expected_value + expect(attribute.settings.min_tls_version).to be == expected_value + end + + it "updates the stored hash with values set on the settings object" do + expected_value = Object.new + + expect(attribute.settings.min_tls_version).to be_nil + attribute.settings.min_tls_version = expected_value + expect(original_hash[:settings][:min_tls_version]).to be == expected_value + end + end +end diff --git a/test/cloudflare/custom_hostname/ssl_attribute/settings.rb b/test/cloudflare/custom_hostname/ssl_attribute/settings.rb new file mode 100644 index 0000000..893fc84 --- /dev/null +++ b/test/cloudflare/custom_hostname/ssl_attribute/settings.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019, by Rob Widmer. +# Copyright, 2024, by Samuel Williams. + +require "cloudflare/custom_hostname/ssl_attribute/settings" + +describe Cloudflare::CustomHostname::SSLAttribute::Settings do + let(:settings) {subject.new} + + it "has an accessor for ciphers" do + ciphers = Object.new + expect(settings.ciphers).to be_nil + settings.ciphers = ciphers + expect(settings.ciphers).to be == ciphers + end + + it "has a boolean accessor for http2" do + expect(settings.http2).to be_nil + expect(settings.http2?).to be == false + settings.http2 = true + expect(settings.http2).to be == "on" + expect(settings.http2?).to be == true + settings.http2 = false + expect(settings.http2).to be == "off" + expect(settings.http2?).to be == false + settings.http2 = "on" + expect(settings.http2).to be == "on" + expect(settings.http2?).to be == true + settings.http2 = "off" + expect(settings.http2).to be == "off" + expect(settings.http2?).to be == false + end + + it "has an accessor for min_tls_version" do + tls_version = Object.new + expect(settings.min_tls_version).to be_nil + settings.min_tls_version = tls_version + expect(settings.min_tls_version).to be == tls_version + end + + it "has a boolean accessor for tls_1_3" do + expect(settings.tls_1_3).to be_nil + expect(settings.tls_1_3?).to be == false + settings.tls_1_3 = true + expect(settings.tls_1_3).to be == "on" + expect(settings.tls_1_3?).to be == true + settings.tls_1_3 = false + expect(settings.tls_1_3).to be == "off" + expect(settings.tls_1_3?).to be == false + settings.tls_1_3 = "on" + expect(settings.tls_1_3).to be == "on" + expect(settings.tls_1_3?).to be == true + settings.tls_1_3 = "off" + expect(settings.tls_1_3).to be == "off" + expect(settings.tls_1_3?).to be == false + end +end diff --git a/test/cloudflare/custom_hostnames.rb b/test/cloudflare/custom_hostnames.rb new file mode 100644 index 0000000..86a51c6 --- /dev/null +++ b/test/cloudflare/custom_hostnames.rb @@ -0,0 +1,210 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019, by Rob Widmer. +# Copyright, 2019-2024, by Samuel Williams. + +require "cloudflare/custom_hostnames" +require "cloudflare/a_connection" + +describe Cloudflare::CustomHostnames do + include_context Cloudflare::AConnection + + let(:domain) {"www-#{job_id}.#{zone_name}"} + let(:record) {zone.custom_hostnames.create(domain)} + + let(:subdomain) {"origin-#{job_id}"} + let(:dns_record) {zone.dns_records.create("A", subdomain, "1.2.3.4")} + + let(:custom_origin) {"#{subdomain}.#{zone.name}"} + + after do + @record&.delete + @dns_record&.delete + end + + # it "can create a custom hostname record" do + # expect(record).to be_a Cloudflare::CustomHostname + # expect(record.custom_metadata).to be_nil + # expect(record.hostname).to be == domain + # expect(record.custom_origin).to be_nil + # expect(record.ssl.method).to be == "http" + # expect(record.ssl.type).to be == "dv" + # end + + # it "can create a custom hostname record with a custom origin" do + # begin + # @record = zone.custom_hostnames.create(domain, origin: custom_origin) + + # expect(@record).to be_kind_of Cloudflare::CustomHostname + # expect(@record.custom_metadata).to be_nil + # expect(@record.hostname).to eq domain + # expect(@record.custom_origin).to eq custom_origin + # expect(@record.ssl.method).to eq "http" + # expect(@record.ssl.type).to eq "dv" + # rescue Cloudflare::RequestError => e + # if e.message.include?("custom origin server has not been granted") + # skip(e.message) # This currently doesn't work but might start eventually: https://github.com/socketry/async-rspec/issues/7 + # else + # raise + # end + # end + # end + + # it "can create a custom hostname record with different ssl options" do + # @record = zone.custom_hostnames.create(domain, ssl: { method: "cname" }) + + # expect(@record).to be_kind_of Cloudflare::CustomHostname + # expect(@record.custom_metadata).to be_nil + # expect(@record.hostname).to eq domain + # expect(@record.custom_origin).to be_nil + # expect(@record.ssl.method).to eq "cname" + # expect(@record.ssl.type).to eq "dv" + # end + + # it "can create a custom hostname record with additional metadata" do + # metadata = { a: rand(1..10) } + + # begin + # @record = zone.custom_hostnames.create(domain, metadata: metadata) + + # expect(@record).to be_kind_of Cloudflare::CustomHostname + # expect(@record.custom_metadata).to eq metadata + # expect(@record.hostname).to eq domain + # expect(@record.custom_origin).to be_nil + # expect(@record.ssl.method).to eq "http" + # expect(@record.ssl.type).to eq "dv" + # rescue Cloudflare::RequestError => e + # if e.message.include?("No custom metadata access has been allocated for this zone") + # skip(e.message) # This currently doesn't work but might start eventually: https://github.com/socketry/async-rspec/issues/7 + # else + # raise + # end + # end + # end + + # it "can look up an existing custom hostname by the hostname or id" do + # expect(zone.custom_hostnames.find_by_hostname(record.hostname).id).to eq record.id + # expect(zone.custom_hostnames.find_by_id(record.id).id).to eq record.id + # end + + # context "with existing record" do + + # it "returns the hostname when calling #to_s" do + # expect(record.to_s).to eq domain + # end + + # it "can update metadata" do + # metadata = { c: rand(1..10) } + + # expect(record.custom_metadata).to be_nil + + # begin + # record.update_settings(metadata: metadata) + + # # Make sure the existing object is updated + # expect(record.custom_metadata).to eq metadata + + # # Verify that the server has the changes + # found_record = zone.custom_hostnames.find_by_id(record.id) + + # expect(found_record.custom_metadata).to eq metadata + # expect(found_record.hostname).to eq domain + # expect(found_record.custom_origin).to be_nil + # rescue Cloudflare::RequestError => e + # if e.message.include?("No custom metadata access has been allocated for this zone") + # skip(e.message) # This currently doesn't work but might start eventually: https://github.com/socketry/async-rspec/issues/7 + # else + # raise + # end + # end + # end + + # it "can update the custom origin" do + # expect(record.custom_origin).to be_nil + + # begin + # record.update_settings(origin: custom_origin) + + # # Make sure the existing object is updated + # expect(record.custom_origin).to eq custom_origin + + # # Verify that the server has the changes + # found_record = zone.custom_hostnames.find_by_id(record.id) + + # expect(found_record.custom_metadata).to be_nil + # expect(found_record.hostname).to eq domain + # expect(found_record.custom_origin).to eq custom_origin + # rescue Cloudflare::RequestError => e + # if e.message.include?("custom origin server has not been granted") + # skip(e.message) # This currently doesn't work but might start eventually: https://github.com/socketry/async-rspec/issues/7 + # else + # raise + # end + # end + # end + + # it "can update ssl information" do + # expect(record.ssl.method).to eq "http" + + # record.update_settings(ssl: { method: "cname", type: "dv" }) + + # # Make sure the existing object is updated + # expect(record.ssl.method).to eq "cname" + + # # Verify that the server has the changes + # found_record = zone.custom_hostnames.find_by_id(record.id) + + # expect(found_record.custom_metadata).to be_nil + # expect(found_record.hostname).to eq domain + # expect(found_record.custom_origin).to be_nil + # expect(found_record.ssl.method).to eq "cname" + # end + + # context "has an ssl section" do + + # it "wraps it in an SSLAttributes object" do + # expect(record.ssl).to be_kind_of Cloudflare::CustomHostname::SSLAttribute + # end + + # it "has some helpers for commonly used keys" do + # # Make sure our values exist before we check to make sure that they are returned correctly + # expect(record.result[:ssl].values_at(:method, :http_body, :http_url).compact).not_to be_empty + # expect(record.ssl.method).to be record.result[:ssl][:method] + # expect(record.ssl.http_body).to be record.result[:ssl][:http_body] + # expect(record.ssl.http_url).to be record.result[:ssl][:http_url] + # end + + # end + + # describe "#ssl_active?" do + + # it "returns the result of calling ssl.active?" do + # expected_value = double + # expect(record.ssl).to receive(:active?).and_return(expected_value) + # expect(record).not_to receive(:send_patch) + # expect(record.ssl_active?).to be expected_value + # end + + # it "returns the result of calling ssl.active? without triggering an update if force_update is true and the ssl is not in the pending_validation state" do + # expected_value = double + # expect(record.ssl).to receive(:active?).and_return(expected_value) + # expect(record.ssl.method).not_to be_nil + # expect(record.ssl.type).not_to be_nil + # expect(record.ssl.pending_validation?).to be false + # expect(record).not_to receive(:send_patch).with(ssl: { method: record.ssl.method, type: record.ssl.type }) + # expect(record.ssl_active?(true)).to be expected_value + # end + + # it "returns the result of calling ssl.active? after triggering an update if force_update is true and the ssl is in the pending_validation state" do + # expected_value = double + # expect(record.ssl).to receive(:active?).and_return(expected_value) + # expect(record.ssl.method).not_to be_nil + # expect(record.ssl.type).not_to be_nil + # record.result[:ssl][:status] = "pending_validation" + # expect(record).to receive(:send_patch).with(ssl: { method: record.ssl.method, type: record.ssl.type }) + # expect(record.ssl_active?(true)).to be expected_value + # end + # end + # end +end diff --git a/test/cloudflare/dns.rb b/test/cloudflare/dns.rb new file mode 100644 index 0000000..7f3bf45 --- /dev/null +++ b/test/cloudflare/dns.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2024, by Samuel Williams. +# Copyright, 2019, by David Wegman. + +require "cloudflare/dns" +require "cloudflare/a_connection" + +describe Cloudflare::DNS do + include_context Cloudflare::AConnection + + let(:subdomain) {"www-#{job_id || SecureRandom.hex(4)}"} + + with "new record" do + it "can create dns record" do + record = zone.dns_records.create("A", subdomain, "1.2.3.4") + + expect(record.type).to be == "A" + expect(record.name).to be(:start_with?, subdomain) + expect(record.content).to be == "1.2.3.4" + ensure + record&.delete + end + + it "can create dns record with proxied option" do + record = zone.dns_records.create("A", subdomain, "1.2.3.4", proxied: true) + + expect(record.type).to be == "A" + expect(record.name).to be(:start_with?, subdomain) + expect(record.content).to be == "1.2.3.4" + expect(record.proxied).to be_truthy + ensure + record&.delete + end + end + + with "existing record" do + let(:record) {zone.dns_records.create("A", subdomain, "1.2.3.4")} + + after do + @record&.delete + end + + it "can update dns content" do + record.update_content("4.3.2.1") + expect(record.content).to be == "4.3.2.1" + + fetched_record = zone.dns_records.find_by_name(record.name) + expect(fetched_record.content).to be == record.content + end + + it "can update dns content with proxied option" do + record.update_content("4.3.2.1", proxied: true) + expect(record).to be(:proxied?) + + fetched_record = zone.dns_records.find_by_name(record.name) + expect(fetched_record).to be(:proxied?) + end + end +end diff --git a/spec/cloudflare/firewall_spec.rb b/test/cloudflare/firewall.rb similarity index 82% rename from spec/cloudflare/firewall_spec.rb rename to test/cloudflare/firewall.rb index 3296c49..15f59d7 100644 --- a/spec/cloudflare/firewall_spec.rb +++ b/test/cloudflare/firewall.rb @@ -3,14 +3,15 @@ # Released under the MIT License. # Copyright, 2019-2024, by Samuel Williams. -require "cloudflare/rspec/connection" +require "cloudflare/firewall" +require "cloudflare/a_connection" -RSpec.describe Cloudflare::Firewall, order: :defined, timeout: 30 do - include_context Cloudflare::Zone +describe Cloudflare::Firewall do + include_context Cloudflare::AConnection let(:notes) {"gemtest"} - context "with several rules" do + with "several rules" do let(:allow_ip) {"123.123.123.123"} let(:block_ip) {"123.123.123.124"} @@ -37,7 +38,7 @@ end %w[block challenge whitelist].each_with_index do |mode, index| - it "should create a #{mode} rule" do + it "should create a #{mode} rule", unique: mode do value = "1.2.3.#{index}" rule = zone.firewall_rules.set(mode, value, notes: notes) diff --git a/test/cloudflare/kv/namespaces.rb b/test/cloudflare/kv/namespaces.rb new file mode 100644 index 0000000..ea11eaa --- /dev/null +++ b/test/cloudflare/kv/namespaces.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019, by Rob Widmer. +# Copyright, 2019-2024, by Samuel Williams. + +require "cloudflare/kv/namespaces" +require "cloudflare/a_connection" + +describe Cloudflare::KV::Namespaces do + include_context Cloudflare::AConnection + + let(:namespace_title) {"Test Worker #{SecureRandom.hex(4)}"} + let(:namespace) {account.kv_namespaces.create(namespace_title) } + + after do + @namespace&.delete + end + + it "can create a namespace" do + expect(namespace).to be_a(Cloudflare::KV::Namespace) + expect(namespace.id).to be =~ /\A[a-f0-9]{32}\z/ + expect(namespace.title).to be == namespace_title + + fetched_namespace = account.kv_namespaces.find_by_title(namespace_title) + expect(fetched_namespace).to have_attributes( + id: be == namespace.id + ) + end + + it "can rename the namespace" do + new_title = namespace_title + " Renamed" + + namespace.rename(new_title) + + expect(namespace.title).to be == new_title + + fetched_namespace = account.kv_namespaces.find_by_title(new_title) + expect(fetched_namespace).to have_attributes( + id: be == namespace.id + ) + end + + it "can store a key/value, read it back" do + key = "key-#{rand(1..100)}" + value = rand(100..999) + + expect(namespace.write_value(key, value)).to be == true + + fetched_namespace = account.kv_namespaces.find_by_id(namespace.id) + expect(fetched_namespace.read_value(key)).to be == value.to_s + end + + it "can delete keys" do + key = "key-#{SecureRandom.hex(8)}" + value = SecureRandom.hex(32) + + namespace.write_value(key, value) + expect(namespace.read_value(key)).to be == value.to_s + expect(namespace.delete_value(key)).to be == true + + fetched_namespace = account.kv_namespaces.find_by_id(namespace.id) + + expect do + fetched_namespace.read_value(key) + end.to raise_exception(Cloudflare::RequestError) + end + + it "can get the keys that exist in the namespace" do + keys = 10.times.map{|index| "key-#{index}"} + + keys.each do |key| + namespace.write_value(key, key) + end + + fetched_keys = account.kv_namespaces.find_by_id(namespace.id).keys.map(&:name) + + expect(fetched_keys.sort).to be == keys.sort + end +end diff --git a/test/cloudflare/logs.rb b/test/cloudflare/logs.rb new file mode 100644 index 0000000..9886974 --- /dev/null +++ b/test/cloudflare/logs.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2024, by Samuel Williams. + +require "cloudflare/logs" +require "cloudflare/a_connection" + +describe Cloudflare::Logs do + include_context Cloudflare::AConnection + + # it "can list logs" do + # logs = zone.logs.first(10) + + # expect(logs).not.to be(:empty?) + # end +end diff --git a/test/cloudflare/zones.rb b/test/cloudflare/zones.rb new file mode 100644 index 0000000..2e23529 --- /dev/null +++ b/test/cloudflare/zones.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2017-2024, by Samuel Williams. +# Copyright, 2018, by Leonhardt Wille. +# Copyright, 2018, by Michael Kalygin. +# Copyright, 2018, by Sherman Koa. +# Copyright, 2019, by Rob Widmer. + +require "cloudflare/zones" +require "cloudflare/a_connection" + +describe Cloudflare::Zones do + include_context Cloudflare::AConnection + + with "temporary zone" do + let(:temporary_zone_name) {"#{SecureRandom.hex(8)}-testing.com"} + + it "can create and destroy zone" do + temporary_zone = zones.create(temporary_zone_name, account) + + fetched_zone = zones.find_by_name(temporary_zone_name) + expect(fetched_zone.name).to be == temporary_zone_name + + fetched_zone.delete + end + end + + with "test zone" do + before do + # Ensure the zone exists: + self.zone + end + + it "can list zones" do + matching_zones = zones.select{|zone| zone.name == zone_name} + + expect(matching_zones).not.to be(:empty?) + end + + it "can get zone by name" do + found_zone = zones.find_by_name(zone_name) + + expect(found_zone).to have_attributes( + name: be == zone_name + ) + end + end +end From 0c93700ce653bad4c5de85a287c21ce8d8c53d5f Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Fri, 13 Sep 2024 12:59:29 +1200 Subject: [PATCH 4/9] Fix test matrix. --- .github/workflows/test-coverage.yaml | 4 +- .github/workflows/test-external.yaml | 3 ++ .github/workflows/test-proxy.yaml | 59 ++++++++++++++++++++++++++++ .github/workflows/test.yaml | 3 ++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/test-proxy.yaml diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index ffa0927..c41019f 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -18,7 +18,6 @@ jobs: matrix: os: - ubuntu - - macos ruby: - "3.3" @@ -32,6 +31,9 @@ jobs: - name: Run tests timeout-minutes: 5 + env: + CLOUDFLARE_EMAIL: ${{secrets.CLOUDFLARE_EMAIL}} + CLOUDFLARE_KEY: ${{secrets.CLOUDFLARE_KEY}} run: bundle exec bake test - uses: actions/upload-artifact@v4 diff --git a/.github/workflows/test-external.yaml b/.github/workflows/test-external.yaml index 21898f5..fd8acc1 100644 --- a/.github/workflows/test-external.yaml +++ b/.github/workflows/test-external.yaml @@ -33,4 +33,7 @@ jobs: - name: Run tests timeout-minutes: 10 + env: + CLOUDFLARE_EMAIL: ${{secrets.CLOUDFLARE_EMAIL}} + CLOUDFLARE_KEY: ${{secrets.CLOUDFLARE_KEY}} run: bundle exec bake test:external diff --git a/.github/workflows/test-proxy.yaml b/.github/workflows/test-proxy.yaml new file mode 100644 index 0000000..199f673 --- /dev/null +++ b/.github/workflows/test-proxy.yaml @@ -0,0 +1,59 @@ +name: Test + +on: [push, pull_request] + +permissions: + contents: read + +env: + CONSOLE_OUTPUT: XTerm + +jobs: + test: + name: ${{matrix.ruby}} on ${{matrix.os}} + runs-on: ${{matrix.os}}-latest + continue-on-error: ${{matrix.experimental}} + + strategy: + matrix: + os: + - ubuntu + - macos + + ruby: + - "3.1" + - "3.2" + - "3.3" + + experimental: [false] + + include: + - os: ubuntu + ruby: truffleruby + experimental: true + - os: ubuntu + ruby: jruby + experimental: true + - os: ubuntu + ruby: head + experimental: true + + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{matrix.ruby}} + bundler-cache: true + + - name: Prepare squid + run: | + sudo apt-get install squid + sudo systemctl start squid + + - name: Run tests + timeout-minutes: 10 + env: + CLOUDFLARE_PROXY: http://localhost:3128 + CLOUDFLARE_EMAIL: ${{secrets.CLOUDFLARE_EMAIL}} + CLOUDFLARE_KEY: ${{secrets.CLOUDFLARE_KEY}} + run: bundle exec bake test diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 0769a98..fc60b8e 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -47,4 +47,7 @@ jobs: - name: Run tests timeout-minutes: 10 + env: + CLOUDFLARE_EMAIL: ${{secrets.CLOUDFLARE_EMAIL}} + CLOUDFLARE_KEY: ${{secrets.CLOUDFLARE_KEY}} run: bundle exec bake test From 708f4f7a051d3dfd731f02fbdb4a3b011e93be54 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Fri, 13 Sep 2024 13:06:20 +1200 Subject: [PATCH 5/9] Reduce requests/second for CI. --- .github/workflows/test-external.yaml | 1 - .github/workflows/test-proxy.yaml | 3 --- .github/workflows/test.yaml | 1 - 3 files changed, 5 deletions(-) diff --git a/.github/workflows/test-external.yaml b/.github/workflows/test-external.yaml index fd8acc1..7416970 100644 --- a/.github/workflows/test-external.yaml +++ b/.github/workflows/test-external.yaml @@ -17,7 +17,6 @@ jobs: matrix: os: - ubuntu - - macos ruby: - "3.1" diff --git a/.github/workflows/test-proxy.yaml b/.github/workflows/test-proxy.yaml index 199f673..e22f5ed 100644 --- a/.github/workflows/test-proxy.yaml +++ b/.github/workflows/test-proxy.yaml @@ -18,11 +18,8 @@ jobs: matrix: os: - ubuntu - - macos ruby: - - "3.1" - - "3.2" - "3.3" experimental: [false] diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index fc60b8e..1d873ab 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -18,7 +18,6 @@ jobs: matrix: os: - ubuntu - - macos ruby: - "3.1" From ef5f5b34e2b26718e71831ea25975e40e6b4fe4e Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Fri, 13 Sep 2024 13:11:57 +1200 Subject: [PATCH 6/9] Print result if it doesn't raise. --- test/cloudflare/kv/namespaces.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cloudflare/kv/namespaces.rb b/test/cloudflare/kv/namespaces.rb index ea11eaa..9fec619 100644 --- a/test/cloudflare/kv/namespaces.rb +++ b/test/cloudflare/kv/namespaces.rb @@ -62,7 +62,7 @@ fetched_namespace = account.kv_namespaces.find_by_id(namespace.id) expect do - fetched_namespace.read_value(key) + pp key: key, value: value, fetched_value: fetched_namespace.read_value(key) end.to raise_exception(Cloudflare::RequestError) end From fc4b130199638999d3492c61966070bb8832cf7f Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Fri, 13 Sep 2024 13:15:39 +1200 Subject: [PATCH 7/9] Key deletion does not seem atomic. --- test/cloudflare/kv/namespaces.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/test/cloudflare/kv/namespaces.rb b/test/cloudflare/kv/namespaces.rb index 9fec619..61f9bf0 100644 --- a/test/cloudflare/kv/namespaces.rb +++ b/test/cloudflare/kv/namespaces.rb @@ -59,11 +59,13 @@ expect(namespace.read_value(key)).to be == value.to_s expect(namespace.delete_value(key)).to be == true - fetched_namespace = account.kv_namespaces.find_by_id(namespace.id) - - expect do - pp key: key, value: value, fetched_value: fetched_namespace.read_value(key) - end.to raise_exception(Cloudflare::RequestError) + # This doesn't always reliably fail, so we can't test it: + # + # fetched_namespace = account.kv_namespaces.find_by_id(namespace.id) + # + # expect do + # fetched_namespace.read_value(key) + # end.to raise_exception(Cloudflare::RequestError) end it "can get the keys that exist in the namespace" do From 6eb609ea13ab3683e24980a97c278044ccfb07ca Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Fri, 13 Sep 2024 13:33:02 +1200 Subject: [PATCH 8/9] Fix proxy connection setup. --- fixtures/cloudflare/a_connection.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fixtures/cloudflare/a_connection.rb b/fixtures/cloudflare/a_connection.rb index cb26a51..833bd91 100644 --- a/fixtures/cloudflare/a_connection.rb +++ b/fixtures/cloudflare/a_connection.rb @@ -5,6 +5,7 @@ require "cloudflare" require "sus/fixtures/async/reactor_context" +require "async/http/proxy" module Cloudflare AUTH_EMAIL = ENV["CLOUDFLARE_EMAIL"] @@ -32,7 +33,7 @@ module Cloudflare if proxy_url = PROXY_URL proxy_endpoint = Async::HTTP::Endpoint.parse(proxy_url) @client = Async::HTTP::Client.new(proxy_endpoint) - @connection = Cloudflare.connect(@client.proxied_endpoint(ENDPOINT), email: AUTH_EMAIL, key: AUTH_KEY) + @connection = Cloudflare.connect(@client.proxied_endpoint(Connection::ENDPOINT), email: AUTH_EMAIL, key: AUTH_KEY) else @client = nil @connection = Cloudflare.connect(email: AUTH_EMAIL, key: AUTH_KEY) From 2963337d151415c2336b0674c57502b567c6f2c8 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Fri, 13 Sep 2024 13:45:13 +1200 Subject: [PATCH 9/9] Don't clobber existing ENV. --- .github/workflows/test-coverage.yaml | 5 ++--- .github/workflows/test-external.yaml | 5 ++--- .github/workflows/test-proxy.yaml | 7 +++---- .github/workflows/test.yaml | 5 ++--- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index c41019f..eb155e1 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -8,6 +8,8 @@ permissions: env: CONSOLE_OUTPUT: XTerm COVERAGE: PartialSummary + CLOUDFLARE_EMAIL: ${{secrets.CLOUDFLARE_EMAIL}} + CLOUDFLARE_KEY: ${{secrets.CLOUDFLARE_KEY}} jobs: test: @@ -31,9 +33,6 @@ jobs: - name: Run tests timeout-minutes: 5 - env: - CLOUDFLARE_EMAIL: ${{secrets.CLOUDFLARE_EMAIL}} - CLOUDFLARE_KEY: ${{secrets.CLOUDFLARE_KEY}} run: bundle exec bake test - uses: actions/upload-artifact@v4 diff --git a/.github/workflows/test-external.yaml b/.github/workflows/test-external.yaml index 7416970..4606575 100644 --- a/.github/workflows/test-external.yaml +++ b/.github/workflows/test-external.yaml @@ -7,6 +7,8 @@ permissions: env: CONSOLE_OUTPUT: XTerm + CLOUDFLARE_EMAIL: ${{secrets.CLOUDFLARE_EMAIL}} + CLOUDFLARE_KEY: ${{secrets.CLOUDFLARE_KEY}} jobs: test: @@ -32,7 +34,4 @@ jobs: - name: Run tests timeout-minutes: 10 - env: - CLOUDFLARE_EMAIL: ${{secrets.CLOUDFLARE_EMAIL}} - CLOUDFLARE_KEY: ${{secrets.CLOUDFLARE_KEY}} run: bundle exec bake test:external diff --git a/.github/workflows/test-proxy.yaml b/.github/workflows/test-proxy.yaml index e22f5ed..28d1a75 100644 --- a/.github/workflows/test-proxy.yaml +++ b/.github/workflows/test-proxy.yaml @@ -7,6 +7,9 @@ permissions: env: CONSOLE_OUTPUT: XTerm + CLOUDFLARE_PROXY: http://localhost:3128 + CLOUDFLARE_EMAIL: ${{secrets.CLOUDFLARE_EMAIL}} + CLOUDFLARE_KEY: ${{secrets.CLOUDFLARE_KEY}} jobs: test: @@ -49,8 +52,4 @@ jobs: - name: Run tests timeout-minutes: 10 - env: - CLOUDFLARE_PROXY: http://localhost:3128 - CLOUDFLARE_EMAIL: ${{secrets.CLOUDFLARE_EMAIL}} - CLOUDFLARE_KEY: ${{secrets.CLOUDFLARE_KEY}} run: bundle exec bake test diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 1d873ab..012ee57 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -7,6 +7,8 @@ permissions: env: CONSOLE_OUTPUT: XTerm + CLOUDFLARE_EMAIL: ${{secrets.CLOUDFLARE_EMAIL}} + CLOUDFLARE_KEY: ${{secrets.CLOUDFLARE_KEY}} jobs: test: @@ -46,7 +48,4 @@ jobs: - name: Run tests timeout-minutes: 10 - env: - CLOUDFLARE_EMAIL: ${{secrets.CLOUDFLARE_EMAIL}} - CLOUDFLARE_KEY: ${{secrets.CLOUDFLARE_KEY}} run: bundle exec bake test