├── .ruby-version ├── service.yml ├── sorbet ├── config ├── rbi │ ├── shims │ │ ├── fakefs.rbi │ │ ├── hash.rb │ │ └── openssl.rb │ ├── todo.rbi │ └── gems │ │ ├── pry@0.14.1.rbi │ │ ├── coderay@1.1.3.rbi │ │ ├── securerandom@0.1.1.rbi │ │ ├── method_source@1.0.0.rbi │ │ ├── rubocop-shopify@2.4.0.rbi │ │ ├── hash_diff@1.0.0.rbi │ │ ├── unicode-display_width@2.1.0.rbi │ │ ├── multi_xml@0.6.0.rbi │ │ ├── crack@0.4.5.rbi │ │ ├── ast@2.4.2.rbi │ │ ├── i18n@1.8.11.rbi │ │ └── mime-types-data@3.2022.0105.rbi └── tapioca │ ├── config.yml │ └── require.rb ├── shipit.rubygems.yml ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── FEATURE_REQUEST.md │ ├── ENHANCEMENT.md │ └── BUG_REPORT.md ├── workflows │ ├── remove-labels-on-activity.yml │ ├── api_update_reminder.yml │ ├── api_update_reminder_on_release.yml │ ├── cla.yml │ ├── close-waiting-for-response-issues.yml │ └── build.yml ├── api_update_reminder_on_release.md ├── dependabot.yml ├── pull_request_template.md └── api_update_reminder.md ├── lib ├── shopify_api │ ├── version.rb │ ├── errors │ │ ├── no_webhook_handler.rb │ │ ├── private_app_error.rb │ │ ├── invalid_oauth_error.rb │ │ ├── invalid_webhook_error.rb │ │ ├── session_storage_error.rb │ │ ├── context_not_setup_error.rb │ │ ├── cookie_not_found_error.rb │ │ ├── invalid_jwt_token_error.rb │ │ ├── missing_jwt_token_error.rb │ │ ├── no_active_session_error.rb │ │ ├── no_session_cookie_error.rb │ │ ├── session_not_found_error.rb │ │ ├── unsupported_oauth_error.rb │ │ ├── feature_deprecated_error.rb │ │ ├── invalid_http_request_error.rb │ │ ├── log_level_not_found_error.rb │ │ ├── request_access_token_error.rb │ │ ├── unsupported_version_error.rb │ │ ├── webhook_registration_error.rb │ │ ├── invalid_graphql_request_error.rb │ │ ├── missing_required_argument_error.rb │ │ ├── invalid_webhook_registration_error.rb │ │ ├── max_http_retries_exceeded_error.rb │ │ └── http_response_error.rb │ ├── webhooks │ │ ├── register_result.rb │ │ ├── handler.rb │ │ └── request.rb │ ├── utils │ │ ├── verifiable_query.rb │ │ ├── http_utils.rb │ │ ├── hmac_validator.rb │ │ └── graphql_proxy.rb │ ├── inflector.rb │ ├── clients │ │ ├── graphql │ │ │ ├── admin.rb │ │ │ └── client.rb │ │ └── http_request.rb │ ├── admin_versions.rb │ ├── auth.rb │ ├── rest │ │ ├── base_errors.rb │ │ └── resources │ │ │ ├── 2022_04 │ │ │ ├── balance.rb │ │ │ └── currency.rb │ │ │ ├── 2022_07 │ │ │ ├── balance.rb │ │ │ └── currency.rb │ │ │ ├── 2022_10 │ │ │ ├── balance.rb │ │ │ └── currency.rb │ │ │ ├── 2023_01 │ │ │ ├── balance.rb │ │ │ └── currency.rb │ │ │ ├── 2023_04 │ │ │ ├── balance.rb │ │ │ └── currency.rb │ │ │ ├── 2023_07 │ │ │ ├── balance.rb │ │ │ └── currency.rb │ │ │ ├── 2023_10 │ │ │ ├── balance.rb │ │ │ └── currency.rb │ │ │ ├── 2024_01 │ │ │ ├── balance.rb │ │ │ └── currency.rb │ │ │ ├── 2024_04 │ │ │ ├── balance.rb │ │ │ └── currency.rb │ │ │ ├── 2024_07 │ │ │ ├── balance.rb │ │ │ └── currency.rb │ │ │ └── 2024_10 │ │ │ ├── balance.rb │ │ │ └── currency.rb │ └── auth │ │ ├── oauth │ │ ├── session_cookie.rb │ │ ├── access_token_response.rb │ │ └── auth_query.rb │ │ ├── associated_user.rb │ │ └── auth_scopes.rb └── shopify_api.rb ├── test ├── test_helpers │ ├── new_fake_webhook_handler.rb │ ├── fake_webhook_handler.rb │ ├── constants.rb │ └── fake_resource_with_custom_prefix.rb ├── utils │ ├── http_utils_test.rb │ └── hmac_validator_test.rb ├── admin_versions_test.rb ├── auth │ └── oauth │ │ ├── auth_query_test.rb │ │ └── access_token_response_test.rb ├── auth_test.rb ├── rest │ ├── base_errors_test.rb │ ├── 2022_04 │ │ ├── balance_test.rb │ │ ├── access_scope_test.rb │ │ └── policy_test.rb │ ├── 2022_07 │ │ ├── balance_test.rb │ │ ├── access_scope_test.rb │ │ └── policy_test.rb │ ├── 2022_10 │ │ ├── balance_test.rb │ │ ├── access_scope_test.rb │ │ └── policy_test.rb │ ├── 2023_01 │ │ ├── balance_test.rb │ │ ├── access_scope_test.rb │ │ └── policy_test.rb │ ├── 2023_04 │ │ ├── balance_test.rb │ │ ├── access_scope_test.rb │ │ └── policy_test.rb │ ├── 2023_07 │ │ ├── balance_test.rb │ │ ├── access_scope_test.rb │ │ └── policy_test.rb │ ├── 2023_10 │ │ ├── balance_test.rb │ │ ├── access_scope_test.rb │ │ └── policy_test.rb │ ├── 2024_01 │ │ ├── balance_test.rb │ │ ├── access_scope_test.rb │ │ └── policy_test.rb │ ├── 2024_04 │ │ ├── balance_test.rb │ │ ├── access_scope_test.rb │ │ └── policy_test.rb │ ├── 2024_07 │ │ ├── balance_test.rb │ │ ├── access_scope_test.rb │ │ └── policy_test.rb │ └── 2024_10 │ │ ├── balance_test.rb │ │ ├── access_scope_test.rb │ │ └── policy_test.rb ├── clients │ ├── graphql │ │ └── admin_test.rb │ └── http_request_test.rb └── webhooks │ └── request_test.rb ├── Gemfile ├── docs ├── README.md └── usage │ └── graphql_storefront.md ├── ROADMAP.md ├── Rakefile ├── bin └── tapioca ├── dev.yml ├── RELEASING.md ├── LICENSE ├── .rubocop.yml ├── shopify_api.gemspec ├── BREAKING_CHANGES_FOR_V15.md └── CODE_OF_CONDUCT.md /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.0.6 2 | -------------------------------------------------------------------------------- /service.yml: -------------------------------------------------------------------------------- 1 | audience: partner 2 | -------------------------------------------------------------------------------- /sorbet/config: -------------------------------------------------------------------------------- 1 | --dir 2 | . 3 | --ignore=test/rest -------------------------------------------------------------------------------- /shipit.rubygems.yml: -------------------------------------------------------------------------------- 1 | # using the default shipit config -------------------------------------------------------------------------------- /sorbet/rbi/shims/fakefs.rbi: -------------------------------------------------------------------------------- 1 | module FakeFS; end 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @Shopify/client-libraries-app-templates 2 | -------------------------------------------------------------------------------- /sorbet/rbi/shims/hash.rb: -------------------------------------------------------------------------------- 1 | class Hash 2 | def with_indifferent_access; end 3 | end 4 | -------------------------------------------------------------------------------- /sorbet/tapioca/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | gem: 3 | typed_overrides: 4 | "openssl": "false" 5 | -------------------------------------------------------------------------------- /sorbet/rbi/shims/openssl.rb: -------------------------------------------------------------------------------- 1 | module OpenSSL 2 | def self.secure_compare(a, b); end 3 | end 4 | -------------------------------------------------------------------------------- /lib/shopify_api/version.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | VERSION = "14.6.0" 6 | end 7 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/no_webhook_handler.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class NoWebhookHandler < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/private_app_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class PrivateAppError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/invalid_oauth_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class InvalidOauthError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/invalid_webhook_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class InvalidWebhookError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/session_storage_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class SessionStorageError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/context_not_setup_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class ContextNotSetupError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/cookie_not_found_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class CookieNotFoundError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/invalid_jwt_token_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class InvalidJwtTokenError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/missing_jwt_token_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class MissingJwtTokenError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/no_active_session_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class NoActiveSessionError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/no_session_cookie_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class NoSessionCookieError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/session_not_found_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class SessionNotFoundError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/unsupported_oauth_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class UnsupportedOauthError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/feature_deprecated_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class FeatureDeprecatedError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/invalid_http_request_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class InvalidHttpRequestError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/log_level_not_found_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class LogLevelNotFoundError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/request_access_token_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class RequestAccessTokenError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/unsupported_version_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class UnsupportedVersionError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/webhook_registration_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class WebhookRegistrationError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/invalid_graphql_request_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class InvalidGraphqlRequestError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/missing_required_argument_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class MissingRequiredArgumentError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/invalid_webhook_registration_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class InvalidWebhookRegistrationError < StandardError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/max_http_retries_exceeded_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class MaxHttpRetriesExceededError < HttpResponseError 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /sorbet/rbi/todo.rbi: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT MANUALLY 2 | # This is an autogenerated file for unresolved constants. 3 | # Please instead update this file by running `bin/tapioca todo`. 4 | 5 | # typed: false 6 | 7 | module Rack::Request::Env; end 8 | module Rack::Request::Helpers; end 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "🙌 Feature Request" 3 | about: Suggest a new feature, or changes to an existing one 4 | labels: "Type: Feature Request :raised_hands:" 5 | --- 6 | 7 | ## Overview 8 | 9 | 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/ENHANCEMENT.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: '📈 Enhancement' 3 | about: Enhancement to our codebase that isn't a adding or changing a feature 4 | labels: 'Type: Enhancement 📈' 5 | --- 6 | 7 | ## Overview/summary 8 | 9 | 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/pry@0.14.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `pry` gem. 5 | # Please instead update this file by running `bin/tapioca gem pry`. 6 | 7 | # THIS IS AN EMPTY RBI FILE. 8 | # see https://github.com/Shopify/tapioca/wiki/Manual-Gem-Requires 9 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/coderay@1.1.3.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `coderay` gem. 5 | # Please instead update this file by running `bin/tapioca gem coderay`. 6 | 7 | # THIS IS AN EMPTY RBI FILE. 8 | # see https://github.com/Shopify/tapioca/wiki/Manual-Gem-Requires 9 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/securerandom@0.1.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `securerandom` gem. 5 | # Please instead update this file by running `bin/tapioca gem securerandom`. 6 | 7 | class Random 8 | include ::Random::Formatter 9 | extend ::Random::Formatter 10 | end 11 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/method_source@1.0.0.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `method_source` gem. 5 | # Please instead update this file by running `bin/tapioca gem method_source`. 6 | 7 | # THIS IS AN EMPTY RBI FILE. 8 | # see https://github.com/Shopify/tapioca/wiki/Manual-Gem-Requires 9 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/rubocop-shopify@2.4.0.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `rubocop-shopify` gem. 5 | # Please instead update this file by running `bin/tapioca gem rubocop-shopify`. 6 | 7 | # THIS IS AN EMPTY RBI FILE. 8 | # see https://github.com/Shopify/tapioca/wiki/Manual-Gem-Requires 9 | -------------------------------------------------------------------------------- /lib/shopify_api/webhooks/register_result.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Webhooks 6 | class RegisterResult < T::Struct 7 | extend T::Sig 8 | 9 | const :topic, String 10 | const :success, T::Boolean 11 | const :body, T.nilable(T.any(T::Hash[String, T.untyped], String)) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/shopify_api/utils/verifiable_query.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Utils 6 | module VerifiableQuery 7 | extend T::Sig 8 | extend T::Helpers 9 | interface! 10 | 11 | sig { abstract.returns(T.nilable(String)) } 12 | def hmac; end 13 | 14 | sig { abstract.returns(String) } 15 | def to_signable_string; end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/test_helpers/new_fake_webhook_handler.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | require_relative "../../lib/shopify_api/webhooks/handler" 5 | module TestHelpers 6 | class NewFakeWebhookHandler 7 | include ShopifyAPI::Webhooks::WebhookHandler 8 | 9 | def initialize(handler) 10 | @handler = handler 11 | end 12 | 13 | def handle(data:) 14 | @handler.call(data) 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/test_helpers/fake_webhook_handler.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | require_relative "../../lib/shopify_api/webhooks/handler" 5 | 6 | module TestHelpers 7 | class FakeWebhookHandler 8 | include ShopifyAPI::Webhooks::Handler 9 | 10 | def initialize(handler) 11 | @handler = handler 12 | end 13 | 14 | def handle(topic:, shop:, body:) 15 | @handler.call(topic, shop, body) 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | gemspec 6 | 7 | group :test, :development do 8 | gem "pry-byebug" 9 | end 10 | 11 | group :development do 12 | gem "rake" 13 | gem "rubocop" 14 | gem "rubocop-shopify" 15 | gem "rubocop-sorbet" 16 | gem "sorbet" 17 | gem "tapioca" 18 | end 19 | 20 | group :test do 21 | gem "minitest" 22 | gem "fakefs", require: false 23 | gem "webmock" 24 | gem "mocha" 25 | end 26 | -------------------------------------------------------------------------------- /test/test_helpers/constants.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module TestHelpers 5 | module Constants 6 | DEFAULT_CLIENT_HEADERS = T.let({ 7 | "Content-Type": "application/json", 8 | "Accept": "application/json", 9 | "User-Agent": "Shopify API Library v#{ShopifyAPI::VERSION} | Ruby #{RUBY_VERSION}", 10 | "Accept-Encoding": "gzip;q=1.0,deflate;q=0.6,identity;q=0.3", 11 | }, T::Hash[Symbol, String]) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Getting started 4 | 5 | You can follow our getting started guide to learn how to use this library. 6 | 7 | - [Getting started](getting_started.md) 8 | - [Performing OAuth](usage/oauth.md) 9 | - [REST Admin API](usage/rest.md) 10 | - [Make a GraphQL API call](usage/graphql.md) 11 | - [Make a Storefront API call](usage/graphql_storefront.md) 12 | - [Webhooks](usage/webhooks.md) 13 | -------------------------------------------------------------------------------- /lib/shopify_api/inflector.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | class Inflector < Zeitwerk::GemInflector 6 | extend T::Sig 7 | 8 | sig { params(basename: String, abspath: String).returns(String) } 9 | def camelize(basename, abspath) 10 | if basename =~ /\Ashopify_api(.*)/ 11 | "ShopifyAPI" + super(Regexp.last_match(1), abspath) 12 | else 13 | super 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/utils/http_utils_test.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | require_relative "../test_helper" 5 | 6 | module ShopifyAPITest 7 | module Utils 8 | class HttpUtilsTest < Minitest::Test 9 | def test_normalize_headers 10 | normalized_headers = ShopifyAPI::Utils::HttpUtils.normalize_headers({ "HTTP_AUTHORIZATION" => "jwt" }) 11 | assert_equal({ "authorization" => "jwt" }, normalized_headers) 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/shopify_api/clients/graphql/admin.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Clients 6 | module Graphql 7 | class Admin < Client 8 | sig { params(session: T.nilable(Auth::Session), api_version: T.nilable(String)).void } 9 | def initialize(session:, api_version: nil) 10 | super(session: session, base_path: "/admin/api", api_version: api_version) 11 | end 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/shopify_api/utils/http_utils.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Utils 6 | module HttpUtils 7 | class << self 8 | extend T::Sig 9 | 10 | sig { params(headers: T::Hash[String, T.untyped]).returns(T::Hash[String, T.untyped]) } 11 | def normalize_headers(headers) 12 | headers.to_h { |k, v| [k.downcase.sub("http_", "").gsub("_", "-"), v] } 13 | end 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /.github/workflows/remove-labels-on-activity.yml: -------------------------------------------------------------------------------- 1 | name: Remove Stale or Waiting Labels 2 | on: 3 | issue_comment: 4 | types: [created] 5 | workflow_dispatch: 6 | jobs: 7 | remove-labels-on-activity: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions-ecosystem/action-remove-labels@v1 12 | if: contains(github.event.issue.labels.*.name, 'Waiting for Response') 13 | with: 14 | labels: | 15 | Waiting for Response 16 | 17 | -------------------------------------------------------------------------------- /.github/workflows/api_update_reminder.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: ~ 3 | schedule: 4 | - cron: "0 0 1 3,6,9,12 *" # At 00:00 on 1st of March, June, September, and December 5 | 6 | name: API update reminder 7 | jobs: 8 | reminder: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: JasonEtco/create-an-issue@v2.4.0 13 | env: 14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 15 | with: 16 | filename: .github/api_update_reminder.md 17 | -------------------------------------------------------------------------------- /test/admin_versions_test.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | require_relative "test_helper" 5 | 6 | module ShopifyAPITest 7 | class AdminVersionsTest < Minitest::Test 8 | def test_supported_admin_versions 9 | assert_instance_of(Array, ShopifyAPI::AdminVersions::SUPPORTED_ADMIN_VERSIONS) 10 | end 11 | 12 | def test_supported_latest_supported_admin_version 13 | assert_instance_of(String, ShopifyAPI::AdminVersions::LATEST_SUPPORTED_ADMIN_VERSION) 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /sorbet/tapioca/require.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | require "active_support/core_ext/string/inflections" 5 | require "active_support/inflector" 6 | require "cgi" 7 | require "concurrent" 8 | require "fakefs/safe" 9 | require "hash_diff" 10 | require "httparty" 11 | require "jwt" 12 | require "minitest/autorun" 13 | require "mocha" 14 | require "mocha/minitest" 15 | require "oj" 16 | require "securerandom" 17 | require "sorbet-runtime" 18 | require "uri" 19 | require "webmock/minitest" 20 | require "zeitwerk" 21 | -------------------------------------------------------------------------------- /.github/workflows/api_update_reminder_on_release.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: ~ 3 | schedule: 4 | - cron: "0 0 1 1,4,7,10 *" # At 00:00 on 1st of January, April, July, and October 5 | 6 | name: API update reminder on release 7 | jobs: 8 | reminder: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: JasonEtco/create-an-issue@v2.4.0 13 | env: 14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 15 | with: 16 | filename: .github/api_update_reminder_on_release.md 17 | -------------------------------------------------------------------------------- /ROADMAP.md: -------------------------------------------------------------------------------- 1 | # Roadmap 2 | 3 | |Priority|Description|Delivery Time frame| 4 | |---|---|---| 5 | |P0|Respond timely to open issues/Pull Requests|Ongoing| 6 | |P1|Minor API release with support for 10-23 API version|Oct 6 - 13| 7 | |P2|Restore dot notation access to GraphQL responses|Oct 26 - Dec 7| 8 | |P2|Restrospection GQL queries to define types for GQL resources|October 26- Dec 7| 9 | |P2|New token exchange authentication via optional feature flag|October 26- Dec 7| 10 | |P3|[Extract REST resources into their own gem](https://github.com/Shopify/shopify-api-ruby/issues/1194)|Oct 26 - Dec 7| 11 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | require "rake/testtask" 5 | require "bundler/gem_tasks" 6 | 7 | namespace :test do 8 | Rake::TestTask.new(:library) do |t| 9 | t.test_files = FileList["test/**/*_test.rb"].exclude("test/rest/**/*.rb") 10 | end 11 | 12 | Rake::TestTask.new(:rest_wrappers) do |t| 13 | pattern = if ENV.key?("API_VERSION") 14 | "test/rest/**/#{ENV.fetch("API_VERSION")}/*.rb" 15 | else 16 | "test/rest/**/*.rb" 17 | end 18 | 19 | t.pattern = pattern 20 | end 21 | end 22 | 23 | task test: ["test:library"] 24 | -------------------------------------------------------------------------------- /lib/shopify_api/errors/http_response_error.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Errors 6 | class HttpResponseError < StandardError 7 | extend T::Sig 8 | 9 | sig { returns(Integer) } 10 | attr_reader :code 11 | 12 | sig { returns(ShopifyAPI::Clients::HttpResponse) } 13 | attr_reader :response 14 | 15 | sig { params(response: ShopifyAPI::Clients::HttpResponse).void } 16 | def initialize(response:) 17 | super 18 | @code = T.let(response.code, Integer) 19 | @response = response 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /.github/api_update_reminder_on_release.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: A new release of the Shopify API occurred 3 | labels: automated 4 | --- 5 | 6 | This is an automated reminder for the maintainers that a new Stable release of the Shopify API is scheduled for today 7 | at 12pm Eastern Time, so a new release of the library is now due. 8 | 9 | A draft PR should already exist for this. 10 | 11 | Review the changelog again and consider if anything in the library needs to change: 12 | 13 | https://shopify.dev/changelog 14 | 15 | Test against the new release by using the Stable version just released. 16 | 17 | Aim to release an update of the library within one week. 18 | 19 | Thank you! 20 | -------------------------------------------------------------------------------- /lib/shopify_api.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 5 | 6 | require "oj" 7 | require "sorbet-runtime" 8 | require "securerandom" 9 | require "cgi" 10 | require "uri" 11 | require "openssl" 12 | require "httparty" 13 | require "zeitwerk" 14 | require "jwt" 15 | require "concurrent" 16 | 17 | require_relative "shopify_api/inflector" 18 | require_relative "shopify_api/admin_versions" 19 | require_relative "shopify_api/webhooks/handler" 20 | 21 | loader = Zeitwerk::Loader.for_gem 22 | loader.inflector = ShopifyAPI::Inflector.new(__FILE__) 23 | loader.ignore("#{__dir__}/shopify_api/rest/resources") 24 | loader.setup 25 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | registries: 3 | rubygems-server-pkgs-shopify-io: 4 | type: rubygems-server 5 | url: https://pkgs.shopify.io 6 | username: ${{secrets.RUBYGEMS_SERVER_PKGS_SHOPIFY_IO_USERNAME}} 7 | password: ${{secrets.RUBYGEMS_SERVER_PKGS_SHOPIFY_IO_PASSWORD}} 8 | github-com: 9 | type: git 10 | url: https://github.com 11 | username: ${{secrets.DEPENDENCIES_GITHUB_USER}} 12 | password: ${{secrets.DEPENDENCIES_GITHUB_TOKEN}} 13 | updates: 14 | - package-ecosystem: bundler 15 | directory: "/" 16 | schedule: 17 | interval: weekly 18 | open-pull-requests-limit: 100 19 | insecure-external-code-execution: allow 20 | registries: '*' 21 | -------------------------------------------------------------------------------- /.github/workflows/cla.yml: -------------------------------------------------------------------------------- 1 | name: Contributor License Agreement (CLA) 2 | 3 | on: 4 | pull_request_target: 5 | types: [opened, synchronize] 6 | issue_comment: 7 | types: [created] 8 | 9 | jobs: 10 | cla: 11 | runs-on: ubuntu-latest 12 | if: | 13 | (github.event.issue.pull_request 14 | && !github.event.issue.pull_request.merged_at 15 | && contains(github.event.comment.body, 'signed') 16 | ) 17 | || (github.event.pull_request && !github.event.pull_request.merged) 18 | steps: 19 | - uses: Shopify/shopify-cla-action@v1 20 | with: 21 | github-token: ${{ secrets.GITHUB_TOKEN }} 22 | cla-token: ${{ secrets.CLA_TOKEN }} 23 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Fixes # 4 | 5 | Please, include a summary of what the PR is for: 6 | - What is the problem it is solving? 7 | - What is the context of these changes? 8 | - What is the impact of this PR? 9 | 10 | ## How has this been tested? 11 | 12 | Please, describe the tests that you ran to verify your changes. 13 | 14 | ## Checklist: 15 | 16 | - [ ] My commit message follow the pattern described in [here](https://chris.beams.io/posts/git-commit/) 17 | - [ ] I have performed a self-review of my own code. 18 | - [ ] I have added tests that prove my fix is effective or that my feature works. 19 | - [ ] I have updated the project documentation. 20 | - [ ] I have added a changelog line. 21 | -------------------------------------------------------------------------------- /lib/shopify_api/admin_versions.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module AdminVersions 6 | SUPPORTED_ADMIN_VERSIONS = T.let([ 7 | "unstable", 8 | "2024-10", 9 | "2024-07", 10 | "2024-04", 11 | "2024-01", 12 | "2023-10", 13 | "2023-07", 14 | "2023-04", 15 | "2023-01", 16 | "2022-10", 17 | "2022-07", 18 | "2022-04", 19 | "2022-01", 20 | ], T::Array[String]) 21 | 22 | LATEST_SUPPORTED_ADMIN_VERSION = T.let("2024-10", String) 23 | end 24 | 25 | SUPPORTED_ADMIN_VERSIONS = ShopifyAPI::AdminVersions::SUPPORTED_ADMIN_VERSIONS 26 | LATEST_SUPPORTED_ADMIN_VERSION = ShopifyAPI::AdminVersions::LATEST_SUPPORTED_ADMIN_VERSION 27 | end 28 | -------------------------------------------------------------------------------- /lib/shopify_api/auth.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Auth 6 | extend T::Sig 7 | 8 | class << self 9 | extend T::Sig 10 | 11 | sig { params(host: T.nilable(String)).returns(String) } 12 | def embedded_app_url(host) 13 | unless Context.setup? 14 | raise Errors::ContextNotSetupError, "ShopifyAPI::Context not setup, please call ShopifyAPI::Context.setup" 15 | end 16 | 17 | unless host 18 | raise Errors::MissingRequiredArgumentError, "host argument is required" 19 | end 20 | 21 | decoded_host = Base64.decode64(host) 22 | "https://#{decoded_host}/apps/#{Context.api_key}" 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/base_errors.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Rest 6 | class BaseErrors 7 | extend T::Sig 8 | 9 | sig { returns(T::Array[ShopifyAPI::Errors::HttpResponseError]) } 10 | attr_accessor :errors 11 | 12 | sig { void } 13 | def initialize 14 | @errors = T.let([], T::Array[ShopifyAPI::Errors::HttpResponseError]) 15 | end 16 | 17 | sig { returns(String) } 18 | def full_messages 19 | @errors.join("\n") 20 | end 21 | 22 | sig { returns(T::Array[T.untyped]) } 23 | def codes 24 | codes = [] 25 | @errors.each do |error| 26 | codes << error.code 27 | end 28 | codes 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/shopify_api/auth/oauth/session_cookie.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Auth 6 | module Oauth 7 | class SessionCookie < T::Struct 8 | extend T::Sig 9 | 10 | SESSION_COOKIE_NAME = "shopify_app_session" 11 | 12 | const :name, String, default: SESSION_COOKIE_NAME 13 | const :value, String 14 | const :expires, T.nilable(Time) 15 | 16 | alias_method :eql?, :== 17 | sig { params(other: T.nilable(SessionCookie)).returns(T::Boolean) } 18 | def ==(other) 19 | return false unless other 20 | 21 | name == other.name && 22 | value == other.value && 23 | expires == other.expires 24 | end 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /.github/workflows/close-waiting-for-response-issues.yml: -------------------------------------------------------------------------------- 1 | name: Close Waiting for Response Issues 2 | on: 3 | schedule: 4 | - cron: "30 1 * * *" 5 | workflow_dispatch: 6 | jobs: 7 | check-need-info: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: close-issues 11 | uses: actions-cool/issues-helper@v3 12 | with: 13 | actions: 'close-issues' 14 | token: ${{ secrets.GITHUB_TOKEN }} 15 | labels: 'Waiting for Response' 16 | inactive-day: 7 17 | body: | 18 | We are closing this issue because we did not hear back regarding additional details we needed to resolve this issue. If the issue persists and you are able to provide the missing clarification we need, feel free to respond and reopen this issue. 19 | 20 | We appreciate your understanding as we try to manage our number of open issues. 21 | -------------------------------------------------------------------------------- /bin/tapioca: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'tapioca' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | require "pathname" 12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 13 | Pathname.new(__FILE__).realpath) 14 | 15 | bundle_binstub = File.expand_path("../bundle", __FILE__) 16 | 17 | if File.file?(bundle_binstub) 18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/ 19 | load(bundle_binstub) 20 | else 21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 23 | end 24 | end 25 | 26 | require "rubygems" 27 | require "bundler/setup" 28 | 29 | load Gem.bin_path("tapioca", "tapioca") 30 | -------------------------------------------------------------------------------- /test/auth/oauth/auth_query_test.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPITest 5 | module Auth 6 | module Oauth 7 | class AuthQueryTest < Minitest::Test 8 | def test_auth_query_is_alphabetical 9 | query = ShopifyAPI::Auth::Oauth::AuthQuery.new( 10 | code: "somecode", 11 | shop: "some-shop.myshopify.com", 12 | state: "1234", 13 | timestamp: "123456", 14 | host: "host", 15 | hmac: "hmac", 16 | ) 17 | 18 | alphabetical_encoded_string = URI.encode_www_form({ 19 | code: "somecode", 20 | host: "host", 21 | shop: "some-shop.myshopify.com", 22 | state: "1234", 23 | timestamp: "123456", 24 | }) 25 | 26 | assert_equal(alphabetical_encoded_string, query.to_signable_string) 27 | end 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/shopify_api/webhooks/handler.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Webhooks 6 | class WebhookMetadata < T::Struct 7 | const :topic, String 8 | const :shop, String 9 | const :body, T::Hash[String, T.untyped] 10 | const :api_version, String 11 | const :webhook_id, String 12 | end 13 | 14 | module Handler 15 | include Kernel 16 | extend T::Sig 17 | extend T::Helpers 18 | interface! 19 | 20 | sig do 21 | abstract.params(topic: String, shop: String, body: T::Hash[String, T.untyped]).void 22 | end 23 | def handle(topic:, shop:, body:); end 24 | end 25 | 26 | module WebhookHandler 27 | include Kernel 28 | extend T::Sig 29 | extend T::Helpers 30 | interface! 31 | 32 | sig do 33 | abstract.params(data: WebhookMetadata).void 34 | end 35 | def handle(data:); end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /dev.yml: -------------------------------------------------------------------------------- 1 | name: shopify-api 2 | 3 | type: ruby 4 | 5 | up: 6 | - ruby 7 | - bundler: 8 | gemfile: Gemfile 9 | 10 | commands: 11 | console: 12 | desc: 'start a ruby shell with ShopifyAPI already loaded' 13 | aliases: ['c'] 14 | run: bundle exec irb -Ilib -rshopify_api 15 | style: 16 | desc: 'run rubocop' 17 | run: bundle exec rubocop 18 | typecheck: 19 | desc: 'run Sorbet typechecking' 20 | aliases: ['tc'] 21 | run: bundle exec srb tc 22 | test: 23 | run: bundle exec rake test 24 | sanity: 25 | desc: 'run all static checks against the codebase' 26 | run: bundle exec rubocop && bundle exec srb tc && bundle exec rake test 27 | subcommands: 28 | all: 29 | desc: 'run all checks regardless of the success states of previous checks' 30 | run: bundle exec rubocop; bundle exec srb tc; bundle exec rake test 31 | rbi: 32 | desc: 'generate .rbi files for specified gem to the project' 33 | run: bin/tapioca gem 34 | -------------------------------------------------------------------------------- /test/auth_test.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | require_relative "test_helper" 5 | 6 | module ShopifyAPITest 7 | class AuthTest < Test::Unit::TestCase 8 | def setup 9 | super 10 | @host = "some-shopify-store.myshopify.com/admin" 11 | @encoded_host = Base64.strict_encode64(@host) 12 | end 13 | 14 | def test_valid_host 15 | assert_equal( 16 | ShopifyAPI::Auth.embedded_app_url(@encoded_host), 17 | "https://#{@host}/apps/#{ShopifyAPI::Context.api_key}", 18 | ) 19 | end 20 | 21 | def test_no_host 22 | assert_raises(ShopifyAPI::Errors::MissingRequiredArgumentError) do 23 | ShopifyAPI::Auth.embedded_app_url(nil) 24 | end 25 | end 26 | 27 | def test_context_not_setup 28 | modify_context(api_key: "", api_secret_key: "", host: "") 29 | 30 | assert_raises(ShopifyAPI::Errors::ContextNotSetupError) do 31 | ShopifyAPI::Auth.embedded_app_url(@encoded_host) 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /.github/api_update_reminder.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: A new release of the Shopify API is due soon 3 | labels: automated 4 | --- 5 | 6 | This is an automated reminder for the maintainers that a new Stable release of the Shopify API is due soon, so the library needs to be updated. 7 | 8 | A new library release should be prepared to: 9 | * add the upcoming Stable release 10 | * remove the oldest Stable release which will no longer be supported. 11 | 12 | The PR should be created as a **draft** but not yet be merged. 13 | 14 | The release schedule can be found at https://shopify.dev/concepts/about-apis/versioning 15 | 16 | Review the changelog and consider if anything in the library needs to change: 17 | 18 | https://shopify.dev/changelog 19 | 20 | Test against the upcoming release by using the Release Candidate. 21 | 22 | Another reminder issue will be created on the date of the next release. 23 | When that happens, test again using the now Stable API version, and aim to release an update of the library within one week. 24 | 25 | Thank you! 26 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | # Releasing ShopifyAPI 2 | 3 | 1. Before releasing, make sure `sorbet` and related gems are up to date: 4 | `bundle update sorbet sorbet-runtime sorbet-static tapioca --conservative` 5 | 1. Check the Semantic Versioning page for info on how to version the new release: http://semver.org 6 | 1. Update the version of ShopifyAPI in lib/shopify_api/version.rb 7 | 1. Run `bundle` 8 | 1. Add a CHANGELOG entry for the new release 9 | 1. Commit the changes with a commit message like "Packaging for release X.Y.Z" 10 | 1. Tag the release with the version (Leave REV blank for HEAD or provide a SHA) 11 | $ git tag vX.Y.Z REV 12 | 1. Push out the changes 13 | $ git push 14 | 1. Push out the tags 15 | $ git push --tags 16 | 1. Publish the gem using Shipit 17 | 1. Consider if the dependency in Shopify/shopify needs updated. It's used only by the tests so is a low risk change. 18 | Also consider Shopify/shopify_app whose gemspec depends on this. 19 | We don't need to do this for every release, but we should try to keep them relatively up to date. 20 | -------------------------------------------------------------------------------- /test/rest/base_errors_test.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | require_relative "../test_helper" 5 | module ShopifyAPITest 6 | class BaseErrorsTest < Minitest::Test 7 | 8 | def test_outputs_error_message 9 | errors = ShopifyAPI::Rest::BaseErrors.new 10 | errors.errors << StandardError.new("Something went wrong") 11 | errors.errors << StandardError.new("Another thing went wrong") 12 | 13 | assert_equal(errors.full_messages, "Something went wrong\nAnother thing went wrong") 14 | end 15 | 16 | def test_outputs_error_code 17 | errors = ShopifyAPI::Rest::BaseErrors.new 18 | 19 | response = ShopifyAPI::Clients::HttpResponse.new(code: 404, body: {}, headers: {}) 20 | errors.errors << ShopifyAPI::Errors::HttpResponseError.new(response: response) 21 | 22 | response = ShopifyAPI::Clients::HttpResponse.new(code: 405, body: {}, headers: {}) 23 | errors.errors << ShopifyAPI::Errors::HttpResponseError.new(response: response) 24 | 25 | assert_equal(errors.codes, [404, 405]) 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/clients/graphql/admin_test.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | require_relative "../../test_helper.rb" 5 | 6 | module ShopifyAPI 7 | module Clients 8 | module Graphql 9 | class AdminTest < Test::Unit::TestCase 10 | include TestHelpers::GraphQLClient 11 | 12 | def setup 13 | super 14 | @session = ShopifyAPI::Auth::Session.new(shop: "test-shop.myshopify.com", 15 | access_token: SecureRandom.alphanumeric(10)) 16 | @client = build_client 17 | @path = "admin/api" 18 | @expected_headers = TestHelpers::Constants::DEFAULT_CLIENT_HEADERS.merge({ 19 | "X-Shopify-Access-Token": @session.access_token, 20 | }) 21 | end 22 | 23 | def build_client 24 | if !defined?("@api_version") 25 | ShopifyAPI::Clients::Graphql::Admin.new(session: @session) 26 | else 27 | ShopifyAPI::Clients::Graphql::Admin.new(session: @session, api_version: @api_version) 28 | end 29 | end 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Shopify Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /lib/shopify_api/auth/associated_user.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Auth 6 | class AssociatedUser < T::Struct 7 | extend T::Sig 8 | 9 | prop :id, Integer 10 | prop :first_name, String 11 | prop :last_name, String 12 | prop :email, String 13 | prop :email_verified, T::Boolean 14 | prop :account_owner, T::Boolean 15 | prop :locale, String 16 | prop :collaborator, T::Boolean 17 | 18 | alias_method :eql?, :== 19 | sig { params(other: T.nilable(AssociatedUser)).returns(T::Boolean) } 20 | def ==(other) 21 | if other 22 | id == other.id && 23 | first_name == other.first_name && 24 | last_name == other.last_name && 25 | email == other.email && 26 | email_verified == other.email_verified && 27 | account_owner == other.account_owner && 28 | locale == other.locale && 29 | collaborator == other.collaborator 30 | else 31 | false 32 | end 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/hash_diff@1.0.0.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `hash_diff` gem. 5 | # Please instead update this file by running `bin/tapioca gem hash_diff`. 6 | 7 | module HashDiff 8 | private 9 | 10 | def diff(*args); end 11 | def left_diff(*args); end 12 | def right_diff(*args); end 13 | 14 | class << self 15 | def diff(*args); end 16 | def left_diff(*args); end 17 | def patch!; end 18 | def right_diff(*args); end 19 | end 20 | end 21 | 22 | class HashDiff::Comparison 23 | def initialize(left, right); end 24 | 25 | def diff; end 26 | def left; end 27 | def left_diff; end 28 | def right; end 29 | def right_diff; end 30 | 31 | protected 32 | 33 | def find_differences(&reporter); end 34 | 35 | private 36 | 37 | def combined_keys; end 38 | def comparable?(key); end 39 | def comparison_strategy(reporter); end 40 | def equal?(key); end 41 | def hash?(value); end 42 | def report_difference(key, reporter); end 43 | def value_with_default(obj, key); end 44 | end 45 | 46 | class HashDiff::NO_VALUE; end 47 | HashDiff::VERSION = T.let(T.unsafe(nil), String) 48 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/unicode-display_width@2.1.0.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `unicode-display_width` gem. 5 | # Please instead update this file by running `bin/tapioca gem unicode-display_width`. 6 | 7 | module Unicode; end 8 | 9 | class Unicode::DisplayWidth 10 | def initialize(ambiguous: T.unsafe(nil), overwrite: T.unsafe(nil), emoji: T.unsafe(nil)); end 11 | 12 | def get_config(**kwargs); end 13 | def of(string, **kwargs); end 14 | 15 | class << self 16 | def emoji_extra_width_of(string, ambiguous = T.unsafe(nil), overwrite = T.unsafe(nil), _ = T.unsafe(nil)); end 17 | def of(string, ambiguous = T.unsafe(nil), overwrite = T.unsafe(nil), options = T.unsafe(nil)); end 18 | end 19 | end 20 | 21 | Unicode::DisplayWidth::DATA_DIRECTORY = T.let(T.unsafe(nil), String) 22 | Unicode::DisplayWidth::DEPTHS = T.let(T.unsafe(nil), Array) 23 | Unicode::DisplayWidth::INDEX = T.let(T.unsafe(nil), Array) 24 | Unicode::DisplayWidth::INDEX_FILENAME = T.let(T.unsafe(nil), String) 25 | Unicode::DisplayWidth::UNICODE_VERSION = T.let(T.unsafe(nil), String) 26 | Unicode::DisplayWidth::VERSION = T.let(T.unsafe(nil), String) 27 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | build: 9 | name: Ruby ${{ matrix.version }} 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | version: 14 | - 3.0 15 | - 3.1 16 | - 3.2 17 | - 3.3 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Set up Ruby ${{ matrix.version }} 21 | uses: ruby/setup-ruby@v1 22 | with: 23 | ruby-version: ${{ matrix.version }} 24 | - name: Install OpenSSL 25 | run: | 26 | sudo apt-get update 27 | sudo apt-get install -y libssl-dev 28 | - name: Run Bundle Commands 29 | run: | 30 | bundle config set --with docs 31 | bundle config set ignore_messages true 32 | bundle 33 | - name: Run RuboCop 34 | run: | 35 | bundle exec rubocop 36 | - name: Run Typecheck 37 | run: | 38 | bundle exec srb tc 39 | - name: Run tests 40 | run: | 41 | bundle exec rake test:library 42 | - name: Run REST wrapper tests 43 | run: | 44 | bundle exec rake test:rest_wrappers 45 | -------------------------------------------------------------------------------- /lib/shopify_api/auth/oauth/access_token_response.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Auth 6 | module Oauth 7 | class AccessTokenResponse < T::Struct 8 | extend T::Sig 9 | 10 | const :access_token, String 11 | const :scope, String 12 | const :session, T.nilable(String) 13 | const :expires_in, T.nilable(Integer) 14 | const :associated_user, T.nilable(AssociatedUser) 15 | const :associated_user_scope, T.nilable(String) 16 | 17 | sig { returns(T::Boolean) } 18 | def online_token? 19 | !associated_user.nil? 20 | end 21 | 22 | alias_method :eql?, :== 23 | sig { params(other: T.nilable(AccessTokenResponse)).returns(T::Boolean) } 24 | def ==(other) 25 | return false unless other 26 | 27 | access_token == other.access_token && 28 | scope == other.scope && 29 | session == other.session && 30 | expires_in == other.expires_in && 31 | associated_user == other.associated_user && 32 | associated_user_scope == other.associated_user_scope 33 | end 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /test/webhooks/request_test.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | require_relative "../test_helper" 5 | 6 | module ShopifyAPITest 7 | module Webhooks 8 | class WebhookRequestTest < Minitest::Test 9 | def test_create_webhook_request 10 | headers = { 11 | "HTTP_X_SHOPIFY_TOPIC" => "some/topic", 12 | "HTTP_X_SHOPIFY_HMAC_SHA256" => "some_hmac", 13 | "HTTP_X_SHOPIFY_SHOP_DOMAIN" => "shop.myshopify.com", 14 | } 15 | 16 | assert(ShopifyAPI::Webhooks::Request.new(raw_body: "{}", headers: headers)) 17 | end 18 | 19 | def test_error_when_headers_missing 20 | assert_raises(ShopifyAPI::Errors::InvalidWebhookError) do 21 | ShopifyAPI::Webhooks::Request.new(raw_body: "{}", headers: {}) 22 | end 23 | end 24 | 25 | def test_with_symbol_headers 26 | headers = { 27 | "HTTP_X_SHOPIFY_TOPIC" => "some/topic", 28 | "HTTP_X_SHOPIFY_HMAC_SHA256" => "some_hmac", 29 | "HTTP_X_SHOPIFY_SHOP_DOMAIN" => "shop.myshopify.com", 30 | :clearance => "session", 31 | } 32 | 33 | assert(ShopifyAPI::Webhooks::Request.new(raw_body: "{}", headers: headers)) 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/shopify_api/auth/oauth/auth_query.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Auth 6 | module Oauth 7 | class AuthQuery 8 | extend T::Sig 9 | include Utils::VerifiableQuery 10 | 11 | sig { returns(String) } 12 | attr_reader :code, :host, :hmac, :shop, :state, :timestamp 13 | 14 | sig do 15 | params( 16 | code: String, 17 | shop: String, 18 | timestamp: String, 19 | state: String, 20 | host: String, 21 | hmac: String, 22 | ).void 23 | end 24 | def initialize(code:, shop:, timestamp:, state:, host:, hmac:) 25 | @code = code 26 | @shop = shop 27 | @timestamp = timestamp 28 | @state = state 29 | @host = host 30 | @hmac = hmac 31 | end 32 | 33 | sig { override.returns(String) } 34 | def to_signable_string 35 | params = { 36 | code: code, 37 | host: host, 38 | shop: shop, 39 | state: state, 40 | timestamp: timestamp, 41 | } 42 | URI.encode_www_form(params) 43 | end 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/shopify_api/clients/http_request.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Clients 6 | class HttpRequest < T::Struct 7 | extend T::Sig 8 | 9 | prop :http_method, Symbol 10 | prop :path, String 11 | prop :body, T.nilable(T.any(T::Hash[T.any(Symbol, String), T.untyped], String)) 12 | prop :body_type, T.nilable(String) 13 | prop :query, T.nilable(T::Hash[T.any(Symbol, String), T.untyped]) 14 | prop :extra_headers, T.nilable(T::Hash[T.any(Symbol, String), T.untyped]) 15 | prop :tries, Integer, default: 1 16 | 17 | sig { void } 18 | def verify 19 | unless [:get, :delete, :put, :post].include?(http_method) 20 | raise ShopifyAPI::Errors::InvalidHttpRequestError, "Invalid Http method #{http_method}." 21 | end 22 | 23 | if body && !body_type 24 | raise ShopifyAPI::Errors::InvalidHttpRequestError, "Cannot set a body without also setting body_type." 25 | end 26 | 27 | return unless [:put, :post].include?(http_method) 28 | 29 | unless body 30 | raise ShopifyAPI::Errors::InvalidHttpRequestError, "Cannot use #{http_method} without specifying data." 31 | end 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/multi_xml@0.6.0.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `multi_xml` gem. 5 | # Please instead update this file by running `bin/tapioca gem multi_xml`. 6 | 7 | module MultiXml 8 | class << self 9 | def default_parser; end 10 | def parse(xml, options = T.unsafe(nil)); end 11 | def parser; end 12 | def parser=(new_parser); end 13 | 14 | private 15 | 16 | def parse_binary(binary, entity); end 17 | def parse_file(file, entity); end 18 | def symbolize_keys(params); end 19 | def typecast_xml_value(value, disallowed_types = T.unsafe(nil)); end 20 | def undasherize_keys(params); end 21 | end 22 | end 23 | 24 | MultiXml::CONTENT_ROOT = T.let(T.unsafe(nil), String) 25 | MultiXml::DEFAULT_OPTIONS = T.let(T.unsafe(nil), Hash) 26 | MultiXml::DISALLOWED_XML_TYPES = T.let(T.unsafe(nil), Array) 27 | 28 | class MultiXml::DisallowedTypeError < ::StandardError 29 | def initialize(type); end 30 | end 31 | 32 | class MultiXml::NoParserError < ::StandardError; end 33 | MultiXml::PARSING = T.let(T.unsafe(nil), Hash) 34 | class MultiXml::ParseError < ::StandardError; end 35 | MultiXml::REQUIREMENT_MAP = T.let(T.unsafe(nil), Array) 36 | MultiXml::TYPE_NAMES = T.let(T.unsafe(nil), Hash) 37 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/crack@0.4.5.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `crack` gem. 5 | # Please instead update this file by running `bin/tapioca gem crack`. 6 | 7 | module Crack; end 8 | 9 | class Crack::REXMLParser 10 | class << self 11 | def parse(xml); end 12 | end 13 | end 14 | 15 | class Crack::XML 16 | class << self 17 | def parse(xml); end 18 | def parser; end 19 | def parser=(parser); end 20 | end 21 | end 22 | 23 | class REXMLUtilityNode 24 | def initialize(name, normalized_attributes = T.unsafe(nil)); end 25 | 26 | def add_node(node); end 27 | def attributes; end 28 | def attributes=(_arg0); end 29 | def children; end 30 | def children=(_arg0); end 31 | def inner_html; end 32 | def name; end 33 | def name=(_arg0); end 34 | def to_hash; end 35 | def to_html; end 36 | def to_s; end 37 | def type; end 38 | def type=(_arg0); end 39 | def typecast_value(value); end 40 | def undasherize_keys(params); end 41 | 42 | private 43 | 44 | def unnormalize_xml_entities(value); end 45 | 46 | class << self 47 | def available_typecasts; end 48 | def available_typecasts=(obj); end 49 | def typecasts; end 50 | def typecasts=(obj); end 51 | end 52 | end 53 | 54 | class REXMLUtiliyNodeString < ::String 55 | def attributes; end 56 | def attributes=(_arg0); end 57 | end 58 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/ast@2.4.2.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `ast` gem. 5 | # Please instead update this file by running `bin/tapioca gem ast`. 6 | 7 | module AST; end 8 | 9 | class AST::Node 10 | def initialize(type, children = T.unsafe(nil), properties = T.unsafe(nil)); end 11 | 12 | def +(array); end 13 | def <<(element); end 14 | def ==(other); end 15 | def append(element); end 16 | def children; end 17 | def clone; end 18 | def concat(array); end 19 | def deconstruct; end 20 | def dup; end 21 | def eql?(other); end 22 | def hash; end 23 | def inspect(indent = T.unsafe(nil)); end 24 | def to_a; end 25 | def to_ast; end 26 | def to_s(indent = T.unsafe(nil)); end 27 | def to_sexp(indent = T.unsafe(nil)); end 28 | def to_sexp_array; end 29 | def type; end 30 | def updated(type = T.unsafe(nil), children = T.unsafe(nil), properties = T.unsafe(nil)); end 31 | 32 | protected 33 | 34 | def assign_properties(properties); end 35 | def fancy_type; end 36 | 37 | private 38 | 39 | def original_dup; end 40 | end 41 | 42 | class AST::Processor 43 | include ::AST::Processor::Mixin 44 | end 45 | 46 | module AST::Processor::Mixin 47 | def handler_missing(node); end 48 | def process(node); end 49 | def process_all(nodes); end 50 | end 51 | 52 | module AST::Sexp 53 | def s(type, *children); end 54 | end 55 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_gem: 2 | rubocop-shopify: rubocop.yml 3 | 4 | require: 5 | - rubocop-sorbet 6 | 7 | AllCops: 8 | NewCops: enable 9 | Exclude: 10 | - "sorbet/**/*" 11 | - "bin/**/*" 12 | - "lib/shopify_api/rest/resources/**/*" 13 | - "test/rest/**/*" 14 | 15 | Sorbet: 16 | Enabled: true 17 | Sorbet/HasSigil: 18 | Enabled: true 19 | Exclude: 20 | - Gemfile* 21 | Sorbet/StrictSigil: 22 | Enabled: true 23 | Exclude: 24 | - Gemfile* 25 | - "lib/shopify_api/auth/session.rb" 26 | - Rakefile 27 | - "test/**/*" 28 | Sorbet/TrueSigil: 29 | Enabled: true 30 | Exclude: 31 | - Gemfile* 32 | - Rakefile 33 | - "test/**/*" 34 | Sorbet/ValidSigil: 35 | Enabled: true 36 | Sorbet/EnforceSigilOrder: 37 | Enabled: true 38 | Sorbet/AllowIncompatibleOverride: 39 | Enabled: true 40 | Sorbet/CheckedTrueInSignature: 41 | Enabled: true 42 | Sorbet/KeywordArgumentOrdering: 43 | Enabled: true 44 | Sorbet/SignatureBuildOrder: 45 | Enabled: true 46 | Sorbet/BindingConstantWithoutTypeAlias: 47 | Enabled: true 48 | Sorbet/ConstantsFromStrings: 49 | Enabled: true 50 | Sorbet/ForbidIncludeConstLiteral: 51 | Enabled: true 52 | Sorbet/ForbidSuperclassConstLiteral: 53 | Enabled: true 54 | 55 | Layout/EmptyLineAfterGuardClause: 56 | Enabled: true 57 | Style/GlobalStdStream: 58 | Enabled: true 59 | Style/OpenStructUse: 60 | Enabled: false 61 | Layout/LineLength: 62 | Exclude: 63 | - "test/clients/*" 64 | -------------------------------------------------------------------------------- /test/auth/oauth/access_token_response_test.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | require_relative "../../test_helper" 5 | 6 | module ShopifyAPITest 7 | module Auth 8 | module Oauth 9 | class AccessTokenResponseTest < Test::Unit::TestCase 10 | def test_online_token_is_false_when_no_associated_user 11 | token_response = ShopifyAPI::Auth::Oauth::AccessTokenResponse.new( 12 | access_token: "token", 13 | scope: "scope1, scope2", 14 | ) 15 | 16 | assert(!token_response.online_token?) 17 | end 18 | 19 | def test_online_token_is_true_when_associated_user_is_present 20 | associated_user = ShopifyAPI::Auth::AssociatedUser.new( 21 | id: 902541635, 22 | first_name: "first", 23 | last_name: "last", 24 | email: "firstlast@example.com", 25 | email_verified: true, 26 | account_owner: true, 27 | locale: "en", 28 | collaborator: false, 29 | ) 30 | token_response = ShopifyAPI::Auth::Oauth::AccessTokenResponse.new( 31 | access_token: "token", 32 | scope: "scope1, scope2", 33 | expires_in: 1000, 34 | associated_user_scope: "scope1", 35 | associated_user: associated_user, 36 | ) 37 | 38 | assert(token_response.online_token?) 39 | end 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /shopify_api.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $LOAD_PATH.push(File.expand_path("../lib", __FILE__)) 4 | require "shopify_api/version" 5 | 6 | Gem::Specification.new do |s| 7 | s.name = "shopify_api" 8 | s.version = ShopifyAPI::VERSION 9 | s.author = "Shopify" 10 | 11 | s.summary = "The gem for accessing the Shopify API" 12 | s.description = <<~HERE 13 | This gem allows Ruby developers to programmatically access the admin 14 | section of Shopify stores. 15 | HERE 16 | s.email = "developers@shopify.com" 17 | s.homepage = "https://shopify.dev/docs/apps" 18 | 19 | s.metadata["allowed_push_host"] = "https://rubygems.org" 20 | 21 | s.extra_rdoc_files = [ 22 | "LICENSE", 23 | "README.md", 24 | ] 25 | s.files = Dir.chdir(File.expand_path("..", __FILE__)) do 26 | %x(git ls-files -z).split("\x0").reject { |f| f.match(%r{^(test)/}) } 27 | end 28 | 29 | s.rdoc_options = ["--charset=UTF-8"] 30 | 31 | s.license = "MIT" 32 | 33 | s.required_ruby_version = ">= 3.0" 34 | 35 | s.add_runtime_dependency("activesupport") 36 | s.add_runtime_dependency("concurrent-ruby") 37 | s.add_runtime_dependency("hash_diff") 38 | s.add_runtime_dependency("httparty") 39 | s.add_runtime_dependency("jwt") 40 | s.add_runtime_dependency("oj") 41 | s.add_runtime_dependency("openssl") 42 | s.add_runtime_dependency("securerandom") 43 | s.add_runtime_dependency("sorbet-runtime") 44 | s.add_runtime_dependency("zeitwerk", "~> 2.5") 45 | end 46 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG_REPORT.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "🐛 Bug Report" 3 | about: Something isn't working 4 | labels: "Type: Bug 🐛" 5 | --- 6 | 7 | # Issue summary 8 | 9 | Before opening this issue, I have: 10 | 11 | - [ ] Upgraded to the latest version of the package 12 | - `shopify_api` version: 13 | - Ruby version: 14 | - Operating system: 15 | - [ ] Set `log_level: :debug` [in my configuration](https://github.com/Shopify/shopify-api-ruby#setup-shopify-context), if applicable 16 | - [ ] Found a reliable way to reproduce the problem that indicates it's a problem with the package 17 | - [ ] Looked for similar issues in this repository 18 | - [ ] Checked that this isn't an issue with a Shopify API 19 | - If it is, please create a post in the [Shopify community forums](https://community.shopify.com/c/partners-and-developers/ct-p/appdev) or report it to [Shopify Partner Support](https://help.shopify.com/en/support/partners/org-select) 20 | 21 | 27 | 28 | ## Expected behavior 29 | 30 | What do you think should happen? 31 | 32 | ## Actual behavior 33 | 34 | What actually happens? 35 | 36 | ## Steps to reproduce the problem 37 | 38 | 1. 39 | 1. 40 | 1. 41 | 42 | ## Debug logs 43 | 44 | ``` 45 | // Paste any relevant logs here 46 | ``` 47 | -------------------------------------------------------------------------------- /lib/shopify_api/utils/hmac_validator.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Utils 6 | module HmacValidator 7 | extend T::Sig 8 | 9 | class << self 10 | extend T::Sig 11 | 12 | sig { params(verifiable_query: VerifiableQuery).returns(T::Boolean) } 13 | def validate(verifiable_query) 14 | return false unless verifiable_query.hmac 15 | 16 | result = validate_signature(verifiable_query, Context.api_secret_key) 17 | if result || Context.old_api_secret_key.nil? || T.must(Context.old_api_secret_key).empty? 18 | result 19 | else 20 | validate_signature(verifiable_query, T.must(Context.old_api_secret_key)) 21 | end 22 | end 23 | 24 | private 25 | 26 | sig { params(verifiable_query: VerifiableQuery, secret: String).returns(T::Boolean) } 27 | def validate_signature(verifiable_query, secret) 28 | received_signature = verifiable_query.hmac 29 | computed_signature = compute_signature(verifiable_query.to_signable_string, secret) 30 | OpenSSL.secure_compare(computed_signature, T.must(received_signature)) 31 | end 32 | 33 | sig { params(signable_string: String, secret: String).returns(String) } 34 | def compute_signature(signable_string, secret) 35 | OpenSSL::HMAC.hexdigest( 36 | OpenSSL::Digest.new("sha256"), 37 | secret, 38 | signable_string, 39 | ) 40 | end 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /test/clients/http_request_test.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | require_relative "../test_helper.rb" 5 | 6 | module ShopifyAPITest 7 | module Clients 8 | class HttpRequestTest < Test::Unit::TestCase 9 | def test_valid_request 10 | ShopifyAPI::Clients::HttpRequest.new( 11 | http_method: :get, 12 | path: "path", 13 | ).verify 14 | end 15 | 16 | def test_invalid_http_method 17 | assert_raises(ShopifyAPI::Errors::InvalidHttpRequestError) do 18 | ShopifyAPI::Clients::HttpRequest.new( 19 | http_method: :bad, 20 | path: "path", 21 | ).verify 22 | end 23 | end 24 | 25 | def test_body_with_no_type 26 | assert_raises(ShopifyAPI::Errors::InvalidHttpRequestError) do 27 | ShopifyAPI::Clients::HttpRequest.new( 28 | http_method: :get, 29 | path: "path", 30 | body: {}, 31 | ).verify 32 | end 33 | end 34 | 35 | def test_post_no_body 36 | assert_raises(ShopifyAPI::Errors::InvalidHttpRequestError) do 37 | ShopifyAPI::Clients::HttpRequest.new( 38 | http_method: :post, 39 | path: "path", 40 | ).verify 41 | end 42 | end 43 | 44 | def test_put_no_body 45 | assert_raises(ShopifyAPI::Errors::InvalidHttpRequestError) do 46 | ShopifyAPI::Clients::HttpRequest.new( 47 | http_method: :put, 48 | path: "path", 49 | ).verify 50 | end 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/i18n@1.8.11.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `i18n` gem. 5 | # Please instead update this file by running `bin/tapioca gem i18n`. 6 | 7 | GetText::PoParser::Racc_arg = T.let(T.unsafe(nil), Array) 8 | GetText::PoParser::Racc_debug_parser = T.let(T.unsafe(nil), TrueClass) 9 | GetText::PoParser::Racc_token_to_s_table = T.let(T.unsafe(nil), Array) 10 | I18n::Backend::Flatten::FLATTEN_SEPARATOR = T.let(T.unsafe(nil), String) 11 | I18n::Backend::Flatten::SEPARATOR_ESCAPE_CHAR = T.let(T.unsafe(nil), String) 12 | I18n::Backend::InterpolationCompiler::Compiler::INTERPOLATION_SYNTAX_PATTERN = T.let(T.unsafe(nil), Regexp) 13 | I18n::Backend::InterpolationCompiler::Compiler::TOKENIZER = T.let(T.unsafe(nil), Regexp) 14 | I18n::Backend::Transliterator::DEFAULT_REPLACEMENT_CHAR = T.let(T.unsafe(nil), String) 15 | I18n::Backend::Transliterator::HashTransliterator::DEFAULT_APPROXIMATIONS = T.let(T.unsafe(nil), Hash) 16 | I18n::DEFAULT_INTERPOLATION_PATTERNS = T.let(T.unsafe(nil), Array) 17 | I18n::EMPTY_HASH = T.let(T.unsafe(nil), Hash) 18 | I18n::Gettext::CONTEXT_SEPARATOR = T.let(T.unsafe(nil), String) 19 | I18n::Gettext::PLURAL_SEPARATOR = T.let(T.unsafe(nil), String) 20 | I18n::INTERPOLATION_PATTERN = T.let(T.unsafe(nil), Regexp) 21 | I18n::Locale::Tag::RFC4646_FORMATS = T.let(T.unsafe(nil), Hash) 22 | I18n::Locale::Tag::RFC4646_SUBTAGS = T.let(T.unsafe(nil), Array) 23 | I18n::Locale::Tag::Rfc4646::Parser::PATTERN = T.let(T.unsafe(nil), Regexp) 24 | I18n::RESERVED_KEYS = T.let(T.unsafe(nil), Array) 25 | I18n::VERSION = T.let(T.unsafe(nil), String) 26 | -------------------------------------------------------------------------------- /BREAKING_CHANGES_FOR_V15.md: -------------------------------------------------------------------------------- 1 | # Breaking change notice for version 15.0.0 2 | 3 | ## Removal of `ShopifyAPI::Webhooks::Handler` 4 | 5 | The `ShopifyAPI::Webhooks::Handler` class has been removed in favor of `ShopifyAPI::Webhooks::WebhookHandler`. The `ShopifyAPI::Webhooks::WebhookHandler` class is now the recommended way to handle webhooks. 6 | 7 | Make a module or class that includes or extends `ShopifyAPI::Webhooks::WebhookHandler` and implement the `handle` method which accepts the following named parameters: data: `WebhookMetadata`. 8 | 9 | In v14, adding new fields to the callback would become a breaking change. To make this code more flexible, handlers will now receive an object that can be typed and extended. 10 | 11 | `data` will have the following keys 12 | - `topic`, `String` - The topic of the webhook 13 | - `shop`, `String` - The shop domain of the webhook 14 | - `body`, `T::Hash[String, T.untyped]`- The body of the webhook 15 | - `webhook_id`, `String` - The id of the webhook event to [avoid duplicates](https://shopify.dev/docs/apps/webhooks/best-practices#ignore-duplicates) 16 | - `api_version`, `String` - The api version of the webhook 17 | 18 | ### New implementation 19 | ```ruby 20 | module WebhookHandler 21 | extend ShopifyAPI::Webhooks::WebhookHandler 22 | 23 | class << self 24 | def handle_webhook(data:) 25 | puts "Received webhook! topic: #{data.topic} shop: #{data.shop} body: #{data.body} webhook_id: #{data.webhook_id} api_version: #{data.api_version" 26 | end 27 | end 28 | end 29 | ``` 30 | 31 | ### Previous implementation 32 | ```ruby 33 | module WebhookHandler 34 | include ShopifyAPI::Webhooks::Handler 35 | 36 | class << self 37 | def handle(topic:, shop:, body:) 38 | puts "Received webhook! topic: #{topic} shop: #{shop} body: #{body}" 39 | end 40 | end 41 | end 42 | ``` 43 | -------------------------------------------------------------------------------- /lib/shopify_api/utils/graphql_proxy.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Utils 6 | module GraphqlProxy 7 | class << self 8 | extend T::Sig 9 | 10 | sig do 11 | params( 12 | session: Auth::Session, 13 | headers: T::Hash[String, T.untyped], 14 | body: String, 15 | cookies: T.nilable(T::Hash[String, String]), 16 | tries: Integer, 17 | ).returns(Clients::HttpResponse) 18 | end 19 | def proxy_query(session:, headers:, body:, cookies: nil, tries: 1) 20 | raise Errors::PrivateAppError, "GraphQL proxing is unsupported for private apps." if Context.private? 21 | 22 | normalized_headers = HttpUtils.normalize_headers(headers) 23 | 24 | unless session.online? 25 | raise Errors::SessionNotFoundError, 26 | "Failed to load an online session from the provided parameters." 27 | end 28 | 29 | client = Clients::Graphql::Admin.new(session: session) 30 | 31 | case normalized_headers["content-type"] 32 | when "application/graphql" 33 | return client.query(query: body, tries: tries) 34 | when "application/json" 35 | parsed_body = JSON.parse(body) 36 | 37 | query = parsed_body["query"] 38 | raise Errors::InvalidGraphqlRequestError, 39 | "Request missing 'query' field in GraphQL proxy request." if query.nil? 40 | 41 | return client.query(query: query, variables: parsed_body["variables"], tries: tries) 42 | end 43 | 44 | raise Errors::InvalidGraphqlRequestError, "Unsupported Content-Type #{normalized_headers["content-type"]}." 45 | end 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2022_04/balance.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Balance < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | super(session: session, from_hash: from_hash) 22 | end 23 | 24 | @has_one = T.let({}, T::Hash[Symbol, Class]) 25 | @has_many = T.let({}, T::Hash[Symbol, Class]) 26 | @paths = T.let([ 27 | {http_method: :get, operation: :get, ids: [], path: "shopify_payments/balance.json"} 28 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 29 | 30 | class << self 31 | sig do 32 | params( 33 | session: Auth::Session, 34 | kwargs: T.untyped 35 | ).returns(T::Array[Balance]) 36 | end 37 | def all( 38 | session: ShopifyAPI::Context.active_session, 39 | **kwargs 40 | ) 41 | response = base_find( 42 | session: session, 43 | ids: {}, 44 | params: {}.merge(kwargs).compact, 45 | ) 46 | 47 | T.cast(response, T::Array[Balance]) 48 | end 49 | 50 | end 51 | 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/shopify_api/webhooks/request.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Webhooks 6 | class Request 7 | extend T::Sig 8 | include Utils::VerifiableQuery 9 | 10 | sig { override.returns(String) } 11 | def hmac 12 | Digest.hexencode(Base64.decode64(T.cast(@headers["x-shopify-hmac-sha256"], String))) 13 | end 14 | 15 | sig { returns(String) } 16 | def topic 17 | T.cast(@headers["x-shopify-topic"], String) 18 | end 19 | 20 | sig { returns(String) } 21 | def shop 22 | T.cast(@headers["x-shopify-shop-domain"], String) 23 | end 24 | 25 | sig { returns(String) } 26 | def api_version 27 | T.cast(@headers["x-shopify-api-version"], String) 28 | end 29 | 30 | sig { returns(String) } 31 | def webhook_id 32 | T.cast(@headers["x-shopify-webhook-id"], String) 33 | end 34 | 35 | sig { override.returns(String) } 36 | def to_signable_string 37 | @raw_body 38 | end 39 | 40 | sig { returns(T::Hash[String, T.untyped]) } 41 | def parsed_body 42 | JSON.parse(@raw_body) 43 | end 44 | 45 | sig { params(raw_body: String, headers: T::Hash[String, T.untyped]).void } 46 | def initialize(raw_body:, headers:) 47 | # normalize the headers by forcing lowercase, removing any prepended "http"s, and changing underscores to dashes 48 | headers = headers.to_h { |k, v| [k.to_s.downcase.sub("http_", "").gsub("_", "-"), v] } 49 | 50 | missing_headers = [] 51 | [ 52 | "x-shopify-topic", 53 | "x-shopify-hmac-sha256", 54 | "x-shopify-shop-domain", 55 | ].each { |header| missing_headers << header unless headers.include?(header) } 56 | unless missing_headers.empty? 57 | raise Errors::InvalidWebhookError, 58 | "Missing one or more of the required HTTP headers to process webhooks: #{missing_headers}" 59 | end 60 | 61 | @headers = headers 62 | @raw_body = raw_body 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/mime-types-data@3.2022.0105.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `mime-types-data` gem. 5 | # Please instead update this file by running `bin/tapioca gem mime-types-data`. 6 | 7 | module MIME; end 8 | 9 | class MIME::Types 10 | include ::Enumerable 11 | extend ::Enumerable 12 | 13 | def initialize; end 14 | 15 | def [](type_id, complete: T.unsafe(nil), registered: T.unsafe(nil)); end 16 | def add(*types); end 17 | def add_type(type, quiet = T.unsafe(nil)); end 18 | def count; end 19 | def each; end 20 | def inspect; end 21 | def of(filename); end 22 | def type_for(filename); end 23 | 24 | private 25 | 26 | def add_type_variant!(mime_type); end 27 | def index_extensions!(mime_type); end 28 | def match(pattern); end 29 | def prune_matches(matches, complete, registered); end 30 | def reindex_extensions!(mime_type); end 31 | 32 | class << self 33 | def [](type_id, complete: T.unsafe(nil), registered: T.unsafe(nil)); end 34 | def add(*types); end 35 | def count; end 36 | def each; end 37 | def logger; end 38 | def logger=(_arg0); end 39 | def new(*_arg0); end 40 | def of(filename); end 41 | def type_for(filename); end 42 | 43 | private 44 | 45 | def __instances__; end 46 | def __types__; end 47 | def lazy_load?; end 48 | def load_default_mime_types(mode = T.unsafe(nil)); end 49 | def load_mode; end 50 | def reindex_extensions(type); end 51 | end 52 | end 53 | 54 | class MIME::Types::Cache < ::Struct 55 | def data; end 56 | def data=(_); end 57 | def version; end 58 | def version=(_); end 59 | 60 | class << self 61 | def [](*_arg0); end 62 | def inspect; end 63 | def load(cache_file = T.unsafe(nil)); end 64 | def members; end 65 | def new(*_arg0); end 66 | def save(types = T.unsafe(nil), cache_file = T.unsafe(nil)); end 67 | end 68 | end 69 | 70 | module MIME::Types::Data; end 71 | MIME::Types::Data::PATH = T.let(T.unsafe(nil), String) 72 | MIME::Types::Data::VERSION = T.let(T.unsafe(nil), String) 73 | MIME::Types::VERSION = T.let(T.unsafe(nil), String) 74 | -------------------------------------------------------------------------------- /test/utils/hmac_validator_test.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | require_relative "../test_helper" 5 | 6 | module ShopifyAPITest 7 | module Utils 8 | class HmacValidatorTest < Test::Unit::TestCase 9 | def setup 10 | super 11 | 12 | @query = { 13 | code: "somecode", 14 | host: "host", 15 | shop: "some-shop.myshopify.com", 16 | state: "1234", 17 | timestamp: "123456", 18 | } 19 | end 20 | 21 | def test_invalid_signature 22 | auth_query = ShopifyAPI::Auth::Oauth::AuthQuery.new( 23 | code: @query[:code], 24 | shop: @query[:shop], 25 | timestamp: @query[:timestamp], 26 | state: @query[:state], 27 | host: @query[:host], 28 | hmac: "invalid", 29 | ) 30 | refute(ShopifyAPI::Utils::HmacValidator.validate(auth_query)) 31 | end 32 | 33 | def test_valid_signature 34 | auth_query = ShopifyAPI::Auth::Oauth::AuthQuery.new( 35 | code: @query[:code], 36 | shop: @query[:shop], 37 | timestamp: @query[:timestamp], 38 | state: @query[:state], 39 | host: @query[:host], 40 | hmac: hmac(@query, ShopifyAPI::Context.api_secret_key), 41 | ) 42 | assert(ShopifyAPI::Utils::HmacValidator.validate(auth_query)) 43 | end 44 | 45 | def test_valid_signature_signed_with_old_api_secret_key 46 | modify_context(old_api_secret_key: "OLD_API_SECRET_KEY") 47 | auth_query = ShopifyAPI::Auth::Oauth::AuthQuery.new( 48 | code: @query[:code], 49 | shop: @query[:shop], 50 | timestamp: @query[:timestamp], 51 | state: @query[:state], 52 | host: @query[:host], 53 | hmac: hmac(@query, ShopifyAPI::Context.old_api_secret_key), 54 | ) 55 | assert(ShopifyAPI::Utils::HmacValidator.validate(auth_query)) 56 | end 57 | 58 | private 59 | 60 | def hmac(query_to_sign, secret) 61 | OpenSSL::HMAC.hexdigest( 62 | OpenSSL::Digest.new("sha256"), 63 | secret, 64 | URI.encode_www_form(query_to_sign), 65 | ) 66 | end 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /test/rest/2022_04/balance_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Balance202204Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2022-04") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2022-04/shopify_payments/balance.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"balance" => [{"amount" => "53.99", "currency" => "USD"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Balance.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2022-04/shopify_payments/balance.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2022_07/balance_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Balance202207Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2022-07") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2022-07/shopify_payments/balance.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"balance" => [{"amount" => "53.99", "currency" => "USD"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Balance.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2022-07/shopify_payments/balance.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2022_10/balance_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Balance202210Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2022-10") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2022-10/shopify_payments/balance.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"balance" => [{"amount" => "53.99", "currency" => "USD"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Balance.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2022-10/shopify_payments/balance.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2023_01/balance_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Balance202301Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2023-01") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2023-01/shopify_payments/balance.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"balance" => [{"amount" => "53.99", "currency" => "USD"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Balance.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2023-01/shopify_payments/balance.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2023_04/balance_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Balance202304Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2023-04") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2023-04/shopify_payments/balance.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"balance" => [{"amount" => "53.99", "currency" => "USD"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Balance.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2023-04/shopify_payments/balance.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2023_07/balance_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Balance202307Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2023-07") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2023-07/shopify_payments/balance.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"balance" => [{"amount" => "53.99", "currency" => "USD"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Balance.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2023-07/shopify_payments/balance.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2023_10/balance_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Balance202310Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2023-10") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2023-10/shopify_payments/balance.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"balance" => [{"amount" => "53.99", "currency" => "USD"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Balance.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2023-10/shopify_payments/balance.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2024_01/balance_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Balance202401Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2024-01") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2024-01/shopify_payments/balance.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"balance" => [{"amount" => "53.99", "currency" => "USD"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Balance.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2024-01/shopify_payments/balance.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2024_04/balance_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Balance202404Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2024-04") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2024-04/shopify_payments/balance.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"balance" => [{"amount" => "53.99", "currency" => "USD"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Balance.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2024-04/shopify_payments/balance.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2024_07/balance_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Balance202404Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2024-07") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2024-07/shopify_payments/balance.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"balance" => [{"amount" => "53.99", "currency" => "USD"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Balance.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2024-07/shopify_payments/balance.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2024_10/balance_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Balance202410Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2024-10") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2024-10/shopify_payments/balance.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"balance" => [{"amount" => "53.99", "currency" => "USD"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Balance.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2024-10/shopify_payments/balance.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2022_07/balance.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Balance < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @balance = T.let(nil, T.nilable(T::Array[T.untyped])) 22 | 23 | super(session: session, from_hash: from_hash) 24 | end 25 | 26 | @has_one = T.let({}, T::Hash[Symbol, Class]) 27 | @has_many = T.let({}, T::Hash[Symbol, Class]) 28 | @paths = T.let([ 29 | {http_method: :get, operation: :get, ids: [], path: "shopify_payments/balance.json"} 30 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 31 | 32 | sig { returns(T.nilable(T::Array[T::Hash[T.untyped, T.untyped]])) } 33 | attr_reader :balance 34 | 35 | class << self 36 | sig do 37 | params( 38 | session: Auth::Session, 39 | kwargs: T.untyped 40 | ).returns(T::Array[Balance]) 41 | end 42 | def all( 43 | session: ShopifyAPI::Context.active_session, 44 | **kwargs 45 | ) 46 | response = base_find( 47 | session: session, 48 | ids: {}, 49 | params: {}.merge(kwargs).compact, 50 | ) 51 | 52 | T.cast(response, T::Array[Balance]) 53 | end 54 | 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2022_10/balance.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Balance < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @balance = T.let(nil, T.nilable(T::Array[T.untyped])) 22 | 23 | super(session: session, from_hash: from_hash) 24 | end 25 | 26 | @has_one = T.let({}, T::Hash[Symbol, Class]) 27 | @has_many = T.let({}, T::Hash[Symbol, Class]) 28 | @paths = T.let([ 29 | {http_method: :get, operation: :get, ids: [], path: "shopify_payments/balance.json"} 30 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 31 | 32 | sig { returns(T.nilable(T::Array[T::Hash[T.untyped, T.untyped]])) } 33 | attr_reader :balance 34 | 35 | class << self 36 | sig do 37 | params( 38 | session: Auth::Session, 39 | kwargs: T.untyped 40 | ).returns(T::Array[Balance]) 41 | end 42 | def all( 43 | session: ShopifyAPI::Context.active_session, 44 | **kwargs 45 | ) 46 | response = base_find( 47 | session: session, 48 | ids: {}, 49 | params: {}.merge(kwargs).compact, 50 | ) 51 | 52 | T.cast(response, T::Array[Balance]) 53 | end 54 | 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2023_01/balance.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Balance < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @balance = T.let(nil, T.nilable(T::Array[T.untyped])) 22 | 23 | super(session: session, from_hash: from_hash) 24 | end 25 | 26 | @has_one = T.let({}, T::Hash[Symbol, Class]) 27 | @has_many = T.let({}, T::Hash[Symbol, Class]) 28 | @paths = T.let([ 29 | {http_method: :get, operation: :get, ids: [], path: "shopify_payments/balance.json"} 30 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 31 | 32 | sig { returns(T.nilable(T::Array[T::Hash[T.untyped, T.untyped]])) } 33 | attr_reader :balance 34 | 35 | class << self 36 | sig do 37 | params( 38 | session: Auth::Session, 39 | kwargs: T.untyped 40 | ).returns(T::Array[Balance]) 41 | end 42 | def all( 43 | session: ShopifyAPI::Context.active_session, 44 | **kwargs 45 | ) 46 | response = base_find( 47 | session: session, 48 | ids: {}, 49 | params: {}.merge(kwargs).compact, 50 | ) 51 | 52 | T.cast(response, T::Array[Balance]) 53 | end 54 | 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2023_04/balance.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Balance < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @balance = T.let(nil, T.nilable(T::Array[T.untyped])) 22 | 23 | super(session: session, from_hash: from_hash) 24 | end 25 | 26 | @has_one = T.let({}, T::Hash[Symbol, Class]) 27 | @has_many = T.let({}, T::Hash[Symbol, Class]) 28 | @paths = T.let([ 29 | {http_method: :get, operation: :get, ids: [], path: "shopify_payments/balance.json"} 30 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 31 | 32 | sig { returns(T.nilable(T::Array[T::Hash[T.untyped, T.untyped]])) } 33 | attr_reader :balance 34 | 35 | class << self 36 | sig do 37 | params( 38 | session: Auth::Session, 39 | kwargs: T.untyped 40 | ).returns(T::Array[Balance]) 41 | end 42 | def all( 43 | session: ShopifyAPI::Context.active_session, 44 | **kwargs 45 | ) 46 | response = base_find( 47 | session: session, 48 | ids: {}, 49 | params: {}.merge(kwargs).compact, 50 | ) 51 | 52 | T.cast(response, T::Array[Balance]) 53 | end 54 | 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2023_07/balance.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Balance < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @balance = T.let(nil, T.nilable(T::Array[T.untyped])) 22 | 23 | super(session: session, from_hash: from_hash) 24 | end 25 | 26 | @has_one = T.let({}, T::Hash[Symbol, Class]) 27 | @has_many = T.let({}, T::Hash[Symbol, Class]) 28 | @paths = T.let([ 29 | {http_method: :get, operation: :get, ids: [], path: "shopify_payments/balance.json"} 30 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 31 | 32 | sig { returns(T.nilable(T::Array[T::Hash[T.untyped, T.untyped]])) } 33 | attr_reader :balance 34 | 35 | class << self 36 | sig do 37 | params( 38 | session: Auth::Session, 39 | kwargs: T.untyped 40 | ).returns(T::Array[Balance]) 41 | end 42 | def all( 43 | session: ShopifyAPI::Context.active_session, 44 | **kwargs 45 | ) 46 | response = base_find( 47 | session: session, 48 | ids: {}, 49 | params: {}.merge(kwargs).compact, 50 | ) 51 | 52 | T.cast(response, T::Array[Balance]) 53 | end 54 | 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2023_10/balance.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Balance < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @balance = T.let(nil, T.nilable(T::Array[T.untyped])) 22 | 23 | super(session: session, from_hash: from_hash) 24 | end 25 | 26 | @has_one = T.let({}, T::Hash[Symbol, Class]) 27 | @has_many = T.let({}, T::Hash[Symbol, Class]) 28 | @paths = T.let([ 29 | {http_method: :get, operation: :get, ids: [], path: "shopify_payments/balance.json"} 30 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 31 | 32 | sig { returns(T.nilable(T::Array[T::Hash[T.untyped, T.untyped]])) } 33 | attr_reader :balance 34 | 35 | class << self 36 | sig do 37 | params( 38 | session: Auth::Session, 39 | kwargs: T.untyped 40 | ).returns(T::Array[Balance]) 41 | end 42 | def all( 43 | session: ShopifyAPI::Context.active_session, 44 | **kwargs 45 | ) 46 | response = base_find( 47 | session: session, 48 | ids: {}, 49 | params: {}.merge(kwargs).compact, 50 | ) 51 | 52 | T.cast(response, T::Array[Balance]) 53 | end 54 | 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2024_01/balance.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Balance < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @balance = T.let(nil, T.nilable(T::Array[T.untyped])) 22 | 23 | super(session: session, from_hash: from_hash) 24 | end 25 | 26 | @has_one = T.let({}, T::Hash[Symbol, Class]) 27 | @has_many = T.let({}, T::Hash[Symbol, Class]) 28 | @paths = T.let([ 29 | {http_method: :get, operation: :get, ids: [], path: "shopify_payments/balance.json"} 30 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 31 | 32 | sig { returns(T.nilable(T::Array[T::Hash[T.untyped, T.untyped]])) } 33 | attr_reader :balance 34 | 35 | class << self 36 | sig do 37 | params( 38 | session: Auth::Session, 39 | kwargs: T.untyped 40 | ).returns(T::Array[Balance]) 41 | end 42 | def all( 43 | session: ShopifyAPI::Context.active_session, 44 | **kwargs 45 | ) 46 | response = base_find( 47 | session: session, 48 | ids: {}, 49 | params: {}.merge(kwargs).compact, 50 | ) 51 | 52 | T.cast(response, T::Array[Balance]) 53 | end 54 | 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2024_04/balance.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Balance < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @balance = T.let(nil, T.nilable(T::Array[T.untyped])) 22 | 23 | super(session: session, from_hash: from_hash) 24 | end 25 | 26 | @has_one = T.let({}, T::Hash[Symbol, Class]) 27 | @has_many = T.let({}, T::Hash[Symbol, Class]) 28 | @paths = T.let([ 29 | {http_method: :get, operation: :get, ids: [], path: "shopify_payments/balance.json"} 30 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 31 | 32 | sig { returns(T.nilable(T::Array[T::Hash[T.untyped, T.untyped]])) } 33 | attr_reader :balance 34 | 35 | class << self 36 | sig do 37 | params( 38 | session: Auth::Session, 39 | kwargs: T.untyped 40 | ).returns(T::Array[Balance]) 41 | end 42 | def all( 43 | session: ShopifyAPI::Context.active_session, 44 | **kwargs 45 | ) 46 | response = base_find( 47 | session: session, 48 | ids: {}, 49 | params: {}.merge(kwargs).compact, 50 | ) 51 | 52 | T.cast(response, T::Array[Balance]) 53 | end 54 | 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2024_07/balance.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Balance < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @balance = T.let(nil, T.nilable(T::Array[T.untyped])) 22 | 23 | super(session: session, from_hash: from_hash) 24 | end 25 | 26 | @has_one = T.let({}, T::Hash[Symbol, Class]) 27 | @has_many = T.let({}, T::Hash[Symbol, Class]) 28 | @paths = T.let([ 29 | {http_method: :get, operation: :get, ids: [], path: "shopify_payments/balance.json"} 30 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 31 | 32 | sig { returns(T.nilable(T::Array[T::Hash[T.untyped, T.untyped]])) } 33 | attr_reader :balance 34 | 35 | class << self 36 | sig do 37 | params( 38 | session: Auth::Session, 39 | kwargs: T.untyped 40 | ).returns(T::Array[Balance]) 41 | end 42 | def all( 43 | session: ShopifyAPI::Context.active_session, 44 | **kwargs 45 | ) 46 | response = base_find( 47 | session: session, 48 | ids: {}, 49 | params: {}.merge(kwargs).compact, 50 | ) 51 | 52 | T.cast(response, T::Array[Balance]) 53 | end 54 | 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2024_10/balance.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Balance < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @balance = T.let(nil, T.nilable(T::Array[T.untyped])) 22 | 23 | super(session: session, from_hash: from_hash) 24 | end 25 | 26 | @has_one = T.let({}, T::Hash[Symbol, Class]) 27 | @has_many = T.let({}, T::Hash[Symbol, Class]) 28 | @paths = T.let([ 29 | {http_method: :get, operation: :get, ids: [], path: "shopify_payments/balance.json"} 30 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 31 | 32 | sig { returns(T.nilable(T::Array[T::Hash[T.untyped, T.untyped]])) } 33 | attr_reader :balance 34 | 35 | class << self 36 | sig do 37 | params( 38 | session: Auth::Session, 39 | kwargs: T.untyped 40 | ).returns(T::Array[Balance]) 41 | end 42 | def all( 43 | session: ShopifyAPI::Context.active_session, 44 | **kwargs 45 | ) 46 | response = base_find( 47 | session: session, 48 | ids: {}, 49 | params: {}.merge(kwargs).compact, 50 | ) 51 | 52 | T.cast(response, T::Array[Balance]) 53 | end 54 | 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /test/rest/2022_04/access_scope_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class AccessScope202204Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2022-04") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"access_scopes" => [{"handle" => "read_products"}, {"handle" => "write_orders"}, {"handle" => "read_orders"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::AccessScope.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2022_07/access_scope_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class AccessScope202207Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2022-07") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"access_scopes" => [{"handle" => "read_products"}, {"handle" => "write_orders"}, {"handle" => "read_orders"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::AccessScope.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2022_10/access_scope_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class AccessScope202210Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2022-10") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"access_scopes" => [{"handle" => "read_products"}, {"handle" => "write_orders"}, {"handle" => "read_orders"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::AccessScope.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2023_01/access_scope_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class AccessScope202301Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2023-01") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"access_scopes" => [{"handle" => "read_products"}, {"handle" => "write_orders"}, {"handle" => "read_orders"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::AccessScope.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2023_04/access_scope_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class AccessScope202304Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2023-04") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"access_scopes" => [{"handle" => "read_products"}, {"handle" => "write_orders"}, {"handle" => "read_orders"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::AccessScope.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2023_07/access_scope_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class AccessScope202307Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2023-07") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"access_scopes" => [{"handle" => "read_products"}, {"handle" => "write_orders"}, {"handle" => "read_orders"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::AccessScope.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2023_10/access_scope_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class AccessScope202310Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2023-10") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"access_scopes" => [{"handle" => "read_products"}, {"handle" => "write_orders"}, {"handle" => "read_orders"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::AccessScope.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2024_01/access_scope_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class AccessScope202401Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2024-01") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"access_scopes" => [{"handle" => "read_products"}, {"handle" => "write_orders"}, {"handle" => "read_orders"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::AccessScope.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2024_04/access_scope_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class AccessScope202404Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2024-04") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"access_scopes" => [{"handle" => "read_products"}, {"handle" => "write_orders"}, {"handle" => "read_orders"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::AccessScope.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2024_07/access_scope_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class AccessScope202404Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2024-07") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"access_scopes" => [{"handle" => "read_products"}, {"handle" => "write_orders"}, {"handle" => "read_orders"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::AccessScope.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2024_10/access_scope_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class AccessScope202410Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2024-10") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"access_scopes" => [{"handle" => "read_products"}, {"handle" => "write_orders"}, {"handle" => "read_orders"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::AccessScope.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/oauth/access_scopes.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /lib/shopify_api/auth/auth_scopes.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Auth 6 | class AuthScopes 7 | extend T::Sig 8 | 9 | SCOPE_DELIMITER = "," 10 | 11 | sig { params(scope_names: T.any(String, T::Array[String])).void } 12 | def initialize(scope_names = []) 13 | @compressed_scopes = T.let([].to_set, T::Set[String]) 14 | @expanded_scopes = T.let([].to_set, T::Set[String]) 15 | 16 | if scope_names.is_a?(String) 17 | scope_names = scope_names.to_s.split(SCOPE_DELIMITER) 18 | end 19 | 20 | store_scopes(scope_names) 21 | end 22 | 23 | sig { params(auth_scopes: AuthScopes).returns(T::Boolean) } 24 | def covers?(auth_scopes) 25 | auth_scopes.compressed_scopes <= expanded_scopes 26 | end 27 | 28 | sig { returns(String) } 29 | def to_s 30 | to_a.join(SCOPE_DELIMITER) 31 | end 32 | 33 | sig { returns(T::Array[String]) } 34 | def to_a 35 | compressed_scopes.to_a 36 | end 37 | 38 | sig { params(other: T.nilable(AuthScopes)).returns(T::Boolean) } 39 | def ==(other) 40 | !other.nil? && 41 | other.class == self.class && 42 | compressed_scopes == other.compressed_scopes 43 | end 44 | 45 | alias_method :eql?, :== 46 | 47 | sig { returns(Integer) } 48 | def hash 49 | compressed_scopes.hash 50 | end 51 | 52 | protected 53 | 54 | sig { returns(T::Set[String]) } 55 | attr_reader :compressed_scopes, :expanded_scopes 56 | 57 | private 58 | 59 | sig { params(scope_names: T::Array[String]).void } 60 | def store_scopes(scope_names) 61 | scopes = scope_names.map(&:strip).reject(&:empty?).to_set 62 | implied_scopes = scopes.map { |scope| implied_scope(scope) }.compact 63 | 64 | @compressed_scopes = scopes - implied_scopes 65 | @expanded_scopes = scopes + implied_scopes 66 | end 67 | 68 | sig { params(scope: String).returns(T.nilable(String)) } 69 | def implied_scope(scope) 70 | is_write_scope = scope =~ /\A(unauthenticated_)?write_(.*)\z/ 71 | "#{Regexp.last_match(1)}read_#{Regexp.last_match(2)}" if is_write_scope 72 | end 73 | end 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /test/test_helpers/fake_resource_with_custom_prefix.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module TestHelpers 5 | class FakeResourceWithCustomPrefix < ShopifyAPI::Rest::Base 6 | extend T::Sig 7 | 8 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 9 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 10 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 11 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 12 | 13 | @has_one = T.let({}, T::Hash[Symbol, T::Class[T.anything]]) 14 | @has_many = T.let({}, T::Hash[Symbol, T::Class[T.anything]]) 15 | @paths = T.let([ 16 | { http_method: :get, operation: :get, ids: [:id], path: "fake_resource_with_custom_prefix/.json" }, 17 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 18 | 19 | @custom_prefix = T.let("/admin/custom_prefix", String) 20 | 21 | sig { returns(T.nilable(Integer)) } 22 | attr_reader :id 23 | 24 | sig { returns(T.nilable(String)) } 25 | attr_reader :attribute 26 | 27 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session)).void } 28 | def initialize(session: nil) 29 | super(session: session) 30 | @id = T.let(nil, T.nilable(Integer)) 31 | @attribute = T.let(nil, T.nilable(String)) 32 | end 33 | 34 | class << self 35 | sig do 36 | params(id: T.any(Integer, String), session: ShopifyAPI::Auth::Session, param: T.untyped, 37 | kwargs: T.untyped).returns(FakeResourceWithCustomPrefix) 38 | end 39 | def find(id:, session:, param: nil, **kwargs) 40 | T.cast( 41 | base_find(params: { param: param }.merge(kwargs), session: session, ids: { id: id })[0], 42 | FakeResourceWithCustomPrefix, 43 | ) 44 | end 45 | 46 | sig do 47 | params(session: ShopifyAPI::Auth::Session, kwargs: T.untyped).returns(T::Array[FakeResourceWithCustomPrefix]) 48 | end 49 | def all(session:, **kwargs) 50 | T.cast( 51 | base_find(session: session, params: kwargs), 52 | T::Array[FakeResourceWithCustomPrefix], 53 | ) 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/shopify_api/clients/graphql/client.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | module ShopifyAPI 5 | module Clients 6 | module Graphql 7 | class Client 8 | extend T::Sig 9 | 10 | sig { params(session: T.nilable(Auth::Session), base_path: String, api_version: T.nilable(String)).void } 11 | def initialize(session:, base_path:, api_version: nil) 12 | @http_client = T.let(HttpClient.new(session: session, base_path: base_path), HttpClient) 13 | @api_version = T.let(api_version || Context.api_version, String) 14 | if api_version 15 | if api_version == Context.api_version 16 | Context.logger.debug("Graphql client has a redundant API version override "\ 17 | "to the default #{Context.api_version}") 18 | else 19 | Context.logger.debug("Graphql client overriding default API version "\ 20 | "#{Context.api_version} with #{api_version}") 21 | end 22 | end 23 | end 24 | 25 | sig do 26 | params( 27 | query: String, 28 | variables: T.nilable(T::Hash[T.any(Symbol, String), T.untyped]), 29 | headers: T.nilable(T::Hash[T.any(Symbol, String), T.untyped]), 30 | tries: Integer, 31 | response_as_struct: T.nilable(T::Boolean), 32 | debug: T::Boolean, 33 | ).returns(HttpResponse) 34 | end 35 | def query( 36 | query:, 37 | variables: nil, 38 | headers: nil, 39 | tries: 1, 40 | response_as_struct: Context.response_as_struct, 41 | debug: false 42 | ) 43 | body = { query: query, variables: variables } 44 | search_params = debug ? "?debug=true" : "" 45 | 46 | @http_client.request( 47 | HttpRequest.new( 48 | http_method: :post, 49 | path: "#{@api_version}/graphql.json#{search_params}", 50 | body: body, 51 | query: nil, 52 | extra_headers: headers, 53 | body_type: "application/json", 54 | tries: tries, 55 | ), 56 | response_as_struct: response_as_struct || false, 57 | ) 58 | end 59 | end 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2022_04/currency.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Currency < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @currency = T.let(nil, T.nilable(String)) 22 | @rate_updated_at = T.let(nil, T.nilable(String)) 23 | 24 | super(session: session, from_hash: from_hash) 25 | end 26 | 27 | @has_one = T.let({}, T::Hash[Symbol, Class]) 28 | @has_many = T.let({}, T::Hash[Symbol, Class]) 29 | @paths = T.let([ 30 | {http_method: :get, operation: :get, ids: [], path: "currencies.json"} 31 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 32 | 33 | sig { returns(T.nilable(String)) } 34 | attr_reader :currency 35 | sig { returns(T.nilable(String)) } 36 | attr_reader :rate_updated_at 37 | 38 | class << self 39 | sig do 40 | params( 41 | session: Auth::Session, 42 | kwargs: T.untyped 43 | ).returns(T::Array[Currency]) 44 | end 45 | def all( 46 | session: ShopifyAPI::Context.active_session, 47 | **kwargs 48 | ) 49 | response = base_find( 50 | session: session, 51 | ids: {}, 52 | params: {}.merge(kwargs).compact, 53 | ) 54 | 55 | T.cast(response, T::Array[Currency]) 56 | end 57 | 58 | end 59 | 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2022_07/currency.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Currency < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @currency = T.let(nil, T.nilable(String)) 22 | @rate_updated_at = T.let(nil, T.nilable(String)) 23 | 24 | super(session: session, from_hash: from_hash) 25 | end 26 | 27 | @has_one = T.let({}, T::Hash[Symbol, Class]) 28 | @has_many = T.let({}, T::Hash[Symbol, Class]) 29 | @paths = T.let([ 30 | {http_method: :get, operation: :get, ids: [], path: "currencies.json"} 31 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 32 | 33 | sig { returns(T.nilable(String)) } 34 | attr_reader :currency 35 | sig { returns(T.nilable(String)) } 36 | attr_reader :rate_updated_at 37 | 38 | class << self 39 | sig do 40 | params( 41 | session: Auth::Session, 42 | kwargs: T.untyped 43 | ).returns(T::Array[Currency]) 44 | end 45 | def all( 46 | session: ShopifyAPI::Context.active_session, 47 | **kwargs 48 | ) 49 | response = base_find( 50 | session: session, 51 | ids: {}, 52 | params: {}.merge(kwargs).compact, 53 | ) 54 | 55 | T.cast(response, T::Array[Currency]) 56 | end 57 | 58 | end 59 | 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2022_10/currency.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Currency < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @currency = T.let(nil, T.nilable(String)) 22 | @rate_updated_at = T.let(nil, T.nilable(String)) 23 | 24 | super(session: session, from_hash: from_hash) 25 | end 26 | 27 | @has_one = T.let({}, T::Hash[Symbol, Class]) 28 | @has_many = T.let({}, T::Hash[Symbol, Class]) 29 | @paths = T.let([ 30 | {http_method: :get, operation: :get, ids: [], path: "currencies.json"} 31 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 32 | 33 | sig { returns(T.nilable(String)) } 34 | attr_reader :currency 35 | sig { returns(T.nilable(String)) } 36 | attr_reader :rate_updated_at 37 | 38 | class << self 39 | sig do 40 | params( 41 | session: Auth::Session, 42 | kwargs: T.untyped 43 | ).returns(T::Array[Currency]) 44 | end 45 | def all( 46 | session: ShopifyAPI::Context.active_session, 47 | **kwargs 48 | ) 49 | response = base_find( 50 | session: session, 51 | ids: {}, 52 | params: {}.merge(kwargs).compact, 53 | ) 54 | 55 | T.cast(response, T::Array[Currency]) 56 | end 57 | 58 | end 59 | 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2023_01/currency.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Currency < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @currency = T.let(nil, T.nilable(String)) 22 | @rate_updated_at = T.let(nil, T.nilable(String)) 23 | 24 | super(session: session, from_hash: from_hash) 25 | end 26 | 27 | @has_one = T.let({}, T::Hash[Symbol, Class]) 28 | @has_many = T.let({}, T::Hash[Symbol, Class]) 29 | @paths = T.let([ 30 | {http_method: :get, operation: :get, ids: [], path: "currencies.json"} 31 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 32 | 33 | sig { returns(T.nilable(String)) } 34 | attr_reader :currency 35 | sig { returns(T.nilable(String)) } 36 | attr_reader :rate_updated_at 37 | 38 | class << self 39 | sig do 40 | params( 41 | session: Auth::Session, 42 | kwargs: T.untyped 43 | ).returns(T::Array[Currency]) 44 | end 45 | def all( 46 | session: ShopifyAPI::Context.active_session, 47 | **kwargs 48 | ) 49 | response = base_find( 50 | session: session, 51 | ids: {}, 52 | params: {}.merge(kwargs).compact, 53 | ) 54 | 55 | T.cast(response, T::Array[Currency]) 56 | end 57 | 58 | end 59 | 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2023_04/currency.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Currency < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @currency = T.let(nil, T.nilable(String)) 22 | @rate_updated_at = T.let(nil, T.nilable(String)) 23 | 24 | super(session: session, from_hash: from_hash) 25 | end 26 | 27 | @has_one = T.let({}, T::Hash[Symbol, Class]) 28 | @has_many = T.let({}, T::Hash[Symbol, Class]) 29 | @paths = T.let([ 30 | {http_method: :get, operation: :get, ids: [], path: "currencies.json"} 31 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 32 | 33 | sig { returns(T.nilable(String)) } 34 | attr_reader :currency 35 | sig { returns(T.nilable(String)) } 36 | attr_reader :rate_updated_at 37 | 38 | class << self 39 | sig do 40 | params( 41 | session: Auth::Session, 42 | kwargs: T.untyped 43 | ).returns(T::Array[Currency]) 44 | end 45 | def all( 46 | session: ShopifyAPI::Context.active_session, 47 | **kwargs 48 | ) 49 | response = base_find( 50 | session: session, 51 | ids: {}, 52 | params: {}.merge(kwargs).compact, 53 | ) 54 | 55 | T.cast(response, T::Array[Currency]) 56 | end 57 | 58 | end 59 | 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2023_07/currency.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Currency < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @currency = T.let(nil, T.nilable(String)) 22 | @rate_updated_at = T.let(nil, T.nilable(String)) 23 | 24 | super(session: session, from_hash: from_hash) 25 | end 26 | 27 | @has_one = T.let({}, T::Hash[Symbol, Class]) 28 | @has_many = T.let({}, T::Hash[Symbol, Class]) 29 | @paths = T.let([ 30 | {http_method: :get, operation: :get, ids: [], path: "currencies.json"} 31 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 32 | 33 | sig { returns(T.nilable(String)) } 34 | attr_reader :currency 35 | sig { returns(T.nilable(String)) } 36 | attr_reader :rate_updated_at 37 | 38 | class << self 39 | sig do 40 | params( 41 | session: Auth::Session, 42 | kwargs: T.untyped 43 | ).returns(T::Array[Currency]) 44 | end 45 | def all( 46 | session: ShopifyAPI::Context.active_session, 47 | **kwargs 48 | ) 49 | response = base_find( 50 | session: session, 51 | ids: {}, 52 | params: {}.merge(kwargs).compact, 53 | ) 54 | 55 | T.cast(response, T::Array[Currency]) 56 | end 57 | 58 | end 59 | 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2023_10/currency.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Currency < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @currency = T.let(nil, T.nilable(String)) 22 | @rate_updated_at = T.let(nil, T.nilable(String)) 23 | 24 | super(session: session, from_hash: from_hash) 25 | end 26 | 27 | @has_one = T.let({}, T::Hash[Symbol, Class]) 28 | @has_many = T.let({}, T::Hash[Symbol, Class]) 29 | @paths = T.let([ 30 | {http_method: :get, operation: :get, ids: [], path: "currencies.json"} 31 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 32 | 33 | sig { returns(T.nilable(String)) } 34 | attr_reader :currency 35 | sig { returns(T.nilable(String)) } 36 | attr_reader :rate_updated_at 37 | 38 | class << self 39 | sig do 40 | params( 41 | session: Auth::Session, 42 | kwargs: T.untyped 43 | ).returns(T::Array[Currency]) 44 | end 45 | def all( 46 | session: ShopifyAPI::Context.active_session, 47 | **kwargs 48 | ) 49 | response = base_find( 50 | session: session, 51 | ids: {}, 52 | params: {}.merge(kwargs).compact, 53 | ) 54 | 55 | T.cast(response, T::Array[Currency]) 56 | end 57 | 58 | end 59 | 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2024_01/currency.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Currency < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @currency = T.let(nil, T.nilable(String)) 22 | @rate_updated_at = T.let(nil, T.nilable(String)) 23 | 24 | super(session: session, from_hash: from_hash) 25 | end 26 | 27 | @has_one = T.let({}, T::Hash[Symbol, Class]) 28 | @has_many = T.let({}, T::Hash[Symbol, Class]) 29 | @paths = T.let([ 30 | {http_method: :get, operation: :get, ids: [], path: "currencies.json"} 31 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 32 | 33 | sig { returns(T.nilable(String)) } 34 | attr_reader :currency 35 | sig { returns(T.nilable(String)) } 36 | attr_reader :rate_updated_at 37 | 38 | class << self 39 | sig do 40 | params( 41 | session: Auth::Session, 42 | kwargs: T.untyped 43 | ).returns(T::Array[Currency]) 44 | end 45 | def all( 46 | session: ShopifyAPI::Context.active_session, 47 | **kwargs 48 | ) 49 | response = base_find( 50 | session: session, 51 | ids: {}, 52 | params: {}.merge(kwargs).compact, 53 | ) 54 | 55 | T.cast(response, T::Array[Currency]) 56 | end 57 | 58 | end 59 | 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2024_04/currency.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Currency < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @currency = T.let(nil, T.nilable(String)) 22 | @rate_updated_at = T.let(nil, T.nilable(String)) 23 | 24 | super(session: session, from_hash: from_hash) 25 | end 26 | 27 | @has_one = T.let({}, T::Hash[Symbol, Class]) 28 | @has_many = T.let({}, T::Hash[Symbol, Class]) 29 | @paths = T.let([ 30 | {http_method: :get, operation: :get, ids: [], path: "currencies.json"} 31 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 32 | 33 | sig { returns(T.nilable(String)) } 34 | attr_reader :currency 35 | sig { returns(T.nilable(String)) } 36 | attr_reader :rate_updated_at 37 | 38 | class << self 39 | sig do 40 | params( 41 | session: Auth::Session, 42 | kwargs: T.untyped 43 | ).returns(T::Array[Currency]) 44 | end 45 | def all( 46 | session: ShopifyAPI::Context.active_session, 47 | **kwargs 48 | ) 49 | response = base_find( 50 | session: session, 51 | ids: {}, 52 | params: {}.merge(kwargs).compact, 53 | ) 54 | 55 | T.cast(response, T::Array[Currency]) 56 | end 57 | 58 | end 59 | 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2024_07/currency.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Currency < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @currency = T.let(nil, T.nilable(String)) 22 | @rate_updated_at = T.let(nil, T.nilable(String)) 23 | 24 | super(session: session, from_hash: from_hash) 25 | end 26 | 27 | @has_one = T.let({}, T::Hash[Symbol, Class]) 28 | @has_many = T.let({}, T::Hash[Symbol, Class]) 29 | @paths = T.let([ 30 | {http_method: :get, operation: :get, ids: [], path: "currencies.json"} 31 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 32 | 33 | sig { returns(T.nilable(String)) } 34 | attr_reader :currency 35 | sig { returns(T.nilable(String)) } 36 | attr_reader :rate_updated_at 37 | 38 | class << self 39 | sig do 40 | params( 41 | session: Auth::Session, 42 | kwargs: T.untyped 43 | ).returns(T::Array[Currency]) 44 | end 45 | def all( 46 | session: ShopifyAPI::Context.active_session, 47 | **kwargs 48 | ) 49 | response = base_find( 50 | session: session, 51 | ids: {}, 52 | params: {}.merge(kwargs).compact, 53 | ) 54 | 55 | T.cast(response, T::Array[Currency]) 56 | end 57 | 58 | end 59 | 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/shopify_api/rest/resources/2024_10/currency.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | module ShopifyAPI 9 | class Currency < ShopifyAPI::Rest::Base 10 | extend T::Sig 11 | 12 | @prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 13 | @next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 14 | 15 | @api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 16 | @retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar) 17 | 18 | sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void } 19 | def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) 20 | 21 | @currency = T.let(nil, T.nilable(String)) 22 | @rate_updated_at = T.let(nil, T.nilable(String)) 23 | 24 | super(session: session, from_hash: from_hash) 25 | end 26 | 27 | @has_one = T.let({}, T::Hash[Symbol, Class]) 28 | @has_many = T.let({}, T::Hash[Symbol, Class]) 29 | @paths = T.let([ 30 | {http_method: :get, operation: :get, ids: [], path: "currencies.json"} 31 | ], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]]) 32 | 33 | sig { returns(T.nilable(String)) } 34 | attr_reader :currency 35 | sig { returns(T.nilable(String)) } 36 | attr_reader :rate_updated_at 37 | 38 | class << self 39 | sig do 40 | params( 41 | session: Auth::Session, 42 | kwargs: T.untyped 43 | ).returns(T::Array[Currency]) 44 | end 45 | def all( 46 | session: ShopifyAPI::Context.active_session, 47 | **kwargs 48 | ) 49 | response = base_find( 50 | session: session, 51 | ids: {}, 52 | params: {}.merge(kwargs).compact, 53 | ) 54 | 55 | T.cast(response, T::Array[Currency]) 56 | end 57 | 58 | end 59 | 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of 4 | fostering an open and welcoming community, we pledge to respect all 5 | people who contribute through reporting issues, posting feature 6 | requests, updating documentation, submitting pull requests or patches, 7 | and other activities. 8 | 9 | We are committed to making participation in this project a 10 | harassment-free experience for everyone, regardless of level of 11 | experience, gender, gender identity and expression, sexual orientation, 12 | disability, personal appearance, body size, race, ethnicity, age, 13 | religion, or nationality. 14 | 15 | Examples of unacceptable behavior by participants include: 16 | 17 | - The use of sexualized language or imagery 18 | - Personal attacks 19 | - Trolling or insulting/derogatory comments 20 | - Public or private harassment 21 | - Publishing other's private information, such as physical or electronic 22 | addresses, without explicit permission 23 | - Other unethical or unprofessional conduct 24 | 25 | Project maintainers have the right and responsibility to remove, edit, 26 | or reject comments, commits, code, wiki edits, issues, and other 27 | contributions that are not aligned to this Code of Conduct, or to ban 28 | temporarily or permanently any contributor for other behaviors that they 29 | deem inappropriate, threatening, offensive, or harmful. 30 | 31 | By adopting this Code of Conduct, project maintainers commit themselves 32 | to fairly and consistently applying these principles to every aspect of 33 | managing this project. Project maintainers who do not follow or enforce 34 | the Code of Conduct may be permanently removed from the project team. 35 | 36 | This Code of Conduct applies both within project spaces and in public 37 | spaces when an individual is representing the project or its community. 38 | 39 | Instances of abusive, harassing, or otherwise unacceptable behavior may 40 | be reported by contacting a project maintainer at . 41 | All complaints will be reviewed and investigated and will result in a response 42 | that is deemed necessary and appropriate to the circumstances. Maintainers are 43 | obligated to maintain confidentiality with regard to the reporter of an incident. 44 | 45 | This Code of Conduct is adapted from the Contributor Covenant, version 46 | 1.3.0, available from http://contributor-covenant.org/version/1/3/0/ 47 | -------------------------------------------------------------------------------- /docs/usage/graphql_storefront.md: -------------------------------------------------------------------------------- 1 | # Make a Storefront API call 2 | 3 | The library also allows you to send GraphQL requests to the [Shopify Storefront API](https://shopify.dev/docs/api/storefront). To do that, you can use `ShopifyAPI::Clients::Graphql::Storefront` with either a [private or public Storefront access token](https://shopify.dev/docs/api/usage/authentication#access-tokens-for-the-storefront-api). 4 | 5 | You can obtain Storefront API access tokens for both private apps and sales channels. Please read [our documentation](https://shopify.dev/docs/custom-storefronts/building-with-the-storefront-api/getting-started) to learn more about Storefront Access Tokens. 6 | 7 | Below is an example of how you may query the Storefront API: 8 | 9 | ```ruby 10 | # Load the access token as per instructions above 11 | storefront_private_access_token = '' 12 | # your shop domain 13 | shop_url = 'shop.myshopify.com' 14 | 15 | # initialize the client with session and a private Storefront access token 16 | client = ShopifyAPI::Clients::Graphql::Storefront.new(shop_url, private_token: storefront_private_access_token) 17 | # or, alternatively with a public Storefront access token: 18 | # client = ShopifyAPI::Clients::Graphql::Storefront.new(shop_url, public_token: storefront_public_access_token) 19 | 20 | query = <<~QUERY 21 | { 22 | collections(first: 2) { 23 | edges { 24 | node { 25 | id 26 | products(first: 5) { 27 | edges { 28 | node { 29 | id 30 | title 31 | } 32 | } 33 | } 34 | } 35 | } 36 | } 37 | } 38 | QUERY 39 | 40 | # You may not need the "Shopify-Storefront-Buyer-IP" header, see its documentation: 41 | # https://shopify.dev/docs/api/usage/authentication#making-server-side-requests 42 | response = client.query(query: query, headers: { "Shopify-Storefront-Buyer-IP": request.ip }) 43 | # do something with the returned data 44 | ``` 45 | 46 | By default, the client uses the API version configured in `ShopifyAPI`. To use a different API version, set the optional `api_version` parameter. To experiment with prerelease API features, use `"unstable"` for the API version. 47 | 48 | ```ruby 49 | client = ShopifyAPI::Clients::Graphql::Storefront.new(shop_url, 50 | private_token: storefront_private_access_token, 51 | api_version: "unstable" 52 | ) 53 | ``` 54 | 55 | Want to make calls to the Admin API? Click [here](graphql.md) 56 | -------------------------------------------------------------------------------- /test/rest/2022_04/policy_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Policy202204Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2022-04") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2022-04/policies.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"policies" => [{"body" => "You have 30 days to get a refund", "created_at" => "2023-06-14T14:23:27-04:00", "updated_at" => "2023-06-14T14:23:27-04:00", "handle" => "refund-policy", "title" => "Refund policy", "url" => "https://jsmith.myshopify.com/548380009/policies/878590288"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Policy.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2022-04/policies.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2022_07/policy_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Policy202207Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2022-07") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2022-07/policies.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"policies" => [{"body" => "You have 30 days to get a refund", "created_at" => "2023-07-11T18:14:30-04:00", "updated_at" => "2023-07-11T18:14:30-04:00", "handle" => "refund-policy", "title" => "Refund policy", "url" => "https://jsmith.myshopify.com/548380009/policies/878590289"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Policy.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2022-07/policies.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2022_10/policy_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Policy202210Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2022-10") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2022-10/policies.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"policies" => [{"body" => "You have 30 days to get a refund", "created_at" => "2023-10-03T13:18:49-04:00", "updated_at" => "2023-10-03T13:18:49-04:00", "handle" => "refund-policy", "title" => "Refund policy", "url" => "https://jsmith.myshopify.com/548380009/policies/878590288"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Policy.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2022-10/policies.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2023_01/policy_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Policy202301Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2023-01") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2023-01/policies.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"policies" => [{"body" => "You have 30 days to get a refund", "created_at" => "2024-01-02T08:58:20-05:00", "updated_at" => "2024-01-02T08:58:20-05:00", "handle" => "refund-policy", "title" => "Refund policy", "url" => "https://jsmith.myshopify.com/548380009/policies/997884056"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Policy.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2023-01/policies.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2023_04/policy_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Policy202304Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2023-04") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2023-04/policies.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"policies" => [{"body" => "You have 30 days to get a refund", "created_at" => "2024-01-02T08:58:20-05:00", "updated_at" => "2024-01-02T08:58:20-05:00", "handle" => "refund-policy", "title" => "Refund policy", "url" => "https://jsmith.myshopify.com/548380009/policies/997884056"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Policy.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2023-04/policies.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2023_07/policy_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Policy202307Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2023-07") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2023-07/policies.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"policies" => [{"body" => "You have 30 days to get a refund", "created_at" => "2024-01-02T08:58:20-05:00", "updated_at" => "2024-01-02T08:58:20-05:00", "handle" => "refund-policy", "title" => "Refund policy", "url" => "https://jsmith.myshopify.com/548380009/policies/997884056"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Policy.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2023-07/policies.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2023_10/policy_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Policy202310Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2023-10") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2023-10/policies.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"policies" => [{"body" => "You have 30 days to get a refund", "created_at" => "2024-01-02T08:58:20-05:00", "updated_at" => "2024-01-02T08:58:20-05:00", "handle" => "refund-policy", "title" => "Refund policy", "url" => "https://jsmith.myshopify.com/548380009/policies/997884056"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Policy.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2023-10/policies.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2024_01/policy_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Policy202401Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2024-01") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2024-01/policies.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"policies" => [{"body" => "You have 30 days to get a refund", "created_at" => "2024-01-02T08:58:20-05:00", "updated_at" => "2024-01-02T08:58:20-05:00", "handle" => "refund-policy", "title" => "Refund policy", "url" => "https://jsmith.myshopify.com/548380009/policies/997884056"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Policy.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2024-01/policies.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2024_04/policy_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Policy202404Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2024-04") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2024-04/policies.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"policies" => [{"body" => "You have 30 days to get a refund", "created_at" => "2024-04-02T08:58:20-05:00", "updated_at" => "2024-04-02T08:58:20-05:00", "handle" => "refund-policy", "title" => "Refund policy", "url" => "https://jsmith.myshopify.com/548380009/policies/997884056"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Policy.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2024-04/policies.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2024_07/policy_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Policy202404Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2024-07") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2024-07/policies.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"policies" => [{"body" => "You have 30 days to get a refund", "created_at" => "2024-07-02T08:58:20-05:00", "updated_at" => "2024-07-02T08:58:20-05:00", "handle" => "refund-policy", "title" => "Refund policy", "url" => "https://jsmith.myshopify.com/548380009/policies/997884056"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Policy.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2024-07/policies.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /test/rest/2024_10/policy_test.rb: -------------------------------------------------------------------------------- 1 | # typed: strict 2 | # frozen_string_literal: true 3 | 4 | ######################################################################################################################## 5 | # This file is auto-generated. If you have an issue, please create a GitHub issue. # 6 | ######################################################################################################################## 7 | 8 | $VERBOSE = nil 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 10 | 11 | require "minitest/autorun" 12 | require "webmock/minitest" 13 | 14 | require "shopify_api" 15 | require_relative "../../test_helper" 16 | 17 | class Policy202410Test < Test::Unit::TestCase 18 | def setup 19 | super 20 | 21 | test_session = ShopifyAPI::Auth::Session.new(id: "id", shop: "test-shop.myshopify.io", access_token: "this_is_a_test_token") 22 | ShopifyAPI::Context.activate_session(test_session) 23 | modify_context(api_version: "2024-10") 24 | end 25 | 26 | def teardown 27 | super 28 | 29 | ShopifyAPI::Context.deactivate_session 30 | end 31 | 32 | sig do 33 | void 34 | end 35 | def test_1() 36 | stub_request(:get, "https://test-shop.myshopify.io/admin/api/2024-10/policies.json") 37 | .with( 38 | headers: {"X-Shopify-Access-Token"=>"this_is_a_test_token", "Accept"=>"application/json"}, 39 | body: {} 40 | ) 41 | .to_return(status: 200, body: JSON.generate({"policies" => [{"body" => "You have 30 days to get a refund", "created_at" => "2024-10-02T08:58:20-05:00", "updated_at" => "2024-10-02T08:58:20-05:00", "handle" => "refund-policy", "title" => "Refund policy", "url" => "https://jsmith.myshopify.com/548380009/policies/997884056"}]}), headers: {}) 42 | 43 | response = ShopifyAPI::Policy.all 44 | 45 | assert_requested(:get, "https://test-shop.myshopify.io/admin/api/2024-10/policies.json") 46 | 47 | response = response.first if response.respond_to?(:first) 48 | 49 | # Assert attributes are correctly typed preventing Sorbet errors downstream 50 | if response.respond_to?(:original_state) 51 | response&.original_state&.each do |key, value| 52 | begin 53 | response.send(key) 54 | rescue TypeError => error 55 | fail TypeError.new("#{self.class}##{key} is mistyped: #{error.message}") 56 | end 57 | response.send(key) 58 | end 59 | end 60 | end 61 | 62 | end 63 | --------------------------------------------------------------------------------