shopify_api 1.0.4 → 12.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/CODEOWNERS +1 -0
- data/.github/ISSUE_TEMPLATE.md +35 -0
- data/.github/dependabot.yml +20 -0
- data/.github/pull_request_template.md +20 -0
- data/.github/workflows/build.yml +42 -0
- data/.github/workflows/cla.yml +22 -0
- data/.github/workflows/close-waiting-for-response-issues.yml +20 -0
- data/.github/workflows/remove-labels-on-activity.yml +16 -0
- data/.github/workflows/stale.yml +32 -0
- data/.rubocop.yml +59 -0
- data/BREAKING_CHANGES_FOR_OLDER_VERSIONS.md +110 -0
- data/CHANGELOG.md +574 -0
- data/CONTRIBUTING.md +9 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +159 -0
- data/LICENSE +2 -2
- data/README.md +138 -0
- data/RELEASING.md +19 -0
- data/Rakefile +16 -50
- data/SECURITY.md +59 -0
- data/bin/tapioca +29 -0
- data/dev.yml +32 -0
- data/docs/README.md +13 -0
- data/docs/getting_started.md +53 -0
- data/docs/issues.md +39 -0
- data/docs/usage/graphql.md +115 -0
- data/docs/usage/graphql_storefront.md +42 -0
- data/docs/usage/oauth.md +104 -0
- data/docs/usage/rest.md +137 -0
- data/docs/usage/session_storage.md +46 -0
- data/docs/usage/webhooks.md +98 -0
- data/lib/shopify_api/admin_versions.rb +19 -0
- data/lib/shopify_api/auth/associated_user.rb +36 -0
- data/lib/shopify_api/auth/auth_scopes.rb +75 -0
- data/lib/shopify_api/auth/file_session_storage.rb +72 -0
- data/lib/shopify_api/auth/jwt_payload.rb +83 -0
- data/lib/shopify_api/auth/oauth/auth_query.rb +47 -0
- data/lib/shopify_api/auth/oauth/session_cookie.rb +28 -0
- data/lib/shopify_api/auth/oauth.rb +135 -0
- data/lib/shopify_api/auth/session.rb +119 -0
- data/lib/shopify_api/auth/session_storage.rb +30 -0
- data/lib/shopify_api/auth.rb +26 -0
- data/lib/shopify_api/clients/graphql/admin.rb +15 -0
- data/lib/shopify_api/clients/graphql/client.rb +40 -0
- data/lib/shopify_api/clients/graphql/storefront.rb +35 -0
- data/lib/shopify_api/clients/http_client.rb +100 -0
- data/lib/shopify_api/clients/http_request.rb +35 -0
- data/lib/shopify_api/clients/http_response.rb +66 -0
- data/lib/shopify_api/clients/rest/admin.rb +118 -0
- data/lib/shopify_api/context.rb +196 -0
- data/lib/shopify_api/errors/context_not_setup_error.rb +9 -0
- data/lib/shopify_api/errors/cookie_not_found_error.rb +9 -0
- data/lib/shopify_api/errors/feature_deprecated_error.rb +9 -0
- data/lib/shopify_api/errors/http_response_error.rb +23 -0
- data/lib/shopify_api/errors/invalid_graphql_request_error.rb +9 -0
- data/lib/shopify_api/errors/invalid_http_request_error.rb +9 -0
- data/lib/shopify_api/errors/invalid_jwt_token_error.rb +9 -0
- data/lib/shopify_api/errors/invalid_oauth_error.rb +9 -0
- data/lib/shopify_api/errors/invalid_webhook_error.rb +9 -0
- data/lib/shopify_api/errors/invalid_webhook_registration_error.rb +9 -0
- data/lib/shopify_api/errors/log_level_not_found_error.rb +9 -0
- data/lib/shopify_api/errors/max_http_retries_exceeded_error.rb +9 -0
- data/lib/shopify_api/errors/missing_jwt_token_error.rb +9 -0
- data/lib/shopify_api/errors/missing_required_argument_error.rb +9 -0
- data/lib/shopify_api/errors/no_active_session_error.rb +9 -0
- data/lib/shopify_api/errors/no_session_cookie_error.rb +9 -0
- data/lib/shopify_api/errors/no_webhook_handler.rb +9 -0
- data/lib/shopify_api/errors/private_app_error.rb +9 -0
- data/lib/shopify_api/errors/request_access_token_error.rb +9 -0
- data/lib/shopify_api/errors/session_not_found_error.rb +9 -0
- data/lib/shopify_api/errors/session_storage_error.rb +9 -0
- data/lib/shopify_api/errors/unsupported_oauth_error.rb +9 -0
- data/lib/shopify_api/errors/unsupported_version_error.rb +9 -0
- data/lib/shopify_api/errors/webhook_registration_error.rb +9 -0
- data/lib/shopify_api/inflector.rb +17 -0
- data/lib/shopify_api/logger.rb +82 -0
- data/lib/shopify_api/rest/base.rb +387 -0
- data/lib/shopify_api/rest/base_errors.rb +32 -0
- data/lib/shopify_api/rest/resources/2022_01/abandoned_checkout.rb +190 -0
- data/lib/shopify_api/rest/resources/2022_01/access_scope.rb +58 -0
- data/lib/shopify_api/rest/resources/2022_01/android_pay_key.rb +77 -0
- data/lib/shopify_api/rest/resources/2022_01/apple_pay_certificate.rb +105 -0
- data/lib/shopify_api/rest/resources/2022_01/application_charge.rb +104 -0
- data/lib/shopify_api/rest/resources/2022_01/application_credit.rb +87 -0
- data/lib/shopify_api/rest/resources/2022_01/article.rb +265 -0
- data/lib/shopify_api/rest/resources/2022_01/asset.rb +118 -0
- data/lib/shopify_api/rest/resources/2022_01/assigned_fulfillment_order.rb +79 -0
- data/lib/shopify_api/rest/resources/2022_01/balance.rb +50 -0
- data/lib/shopify_api/rest/resources/2022_01/blog.rb +162 -0
- data/lib/shopify_api/rest/resources/2022_01/cancellation_request.rb +83 -0
- data/lib/shopify_api/rest/resources/2022_01/carrier_service.rb +116 -0
- data/lib/shopify_api/rest/resources/2022_01/checkout.rb +209 -0
- data/lib/shopify_api/rest/resources/2022_01/collect.rb +142 -0
- data/lib/shopify_api/rest/resources/2022_01/collection.rb +110 -0
- data/lib/shopify_api/rest/resources/2022_01/collection_listing.rb +155 -0
- data/lib/shopify_api/rest/resources/2022_01/comment.rb +283 -0
- data/lib/shopify_api/rest/resources/2022_01/country.rb +137 -0
- data/lib/shopify_api/rest/resources/2022_01/currency.rb +57 -0
- data/lib/shopify_api/rest/resources/2022_01/custom_collection.rb +187 -0
- data/lib/shopify_api/rest/resources/2022_01/customer.rb +318 -0
- data/lib/shopify_api/rest/resources/2022_01/customer_address.rb +201 -0
- data/lib/shopify_api/rest/resources/2022_01/customer_saved_search.rb +169 -0
- data/lib/shopify_api/rest/resources/2022_01/deprecated_api_call.rb +57 -0
- data/lib/shopify_api/rest/resources/2022_01/discount_code.rb +219 -0
- data/lib/shopify_api/rest/resources/2022_01/dispute.rb +111 -0
- data/lib/shopify_api/rest/resources/2022_01/draft_order.rb +275 -0
- data/lib/shopify_api/rest/resources/2022_01/event.rb +148 -0
- data/lib/shopify_api/rest/resources/2022_01/fulfillment.rb +268 -0
- data/lib/shopify_api/rest/resources/2022_01/fulfillment_event.rb +166 -0
- data/lib/shopify_api/rest/resources/2022_01/fulfillment_order.rb +281 -0
- data/lib/shopify_api/rest/resources/2022_01/fulfillment_request.rb +87 -0
- data/lib/shopify_api/rest/resources/2022_01/fulfillment_service.rb +130 -0
- data/lib/shopify_api/rest/resources/2022_01/gift_card.rb +215 -0
- data/lib/shopify_api/rest/resources/2022_01/gift_card_adjustment.rb +118 -0
- data/lib/shopify_api/rest/resources/2022_01/image.rb +157 -0
- data/lib/shopify_api/rest/resources/2022_01/inventory_item.rb +108 -0
- data/lib/shopify_api/rest/resources/2022_01/inventory_level.rb +179 -0
- data/lib/shopify_api/rest/resources/2022_01/location.rb +167 -0
- data/lib/shopify_api/rest/resources/2022_01/locations_for_move.rb +56 -0
- data/lib/shopify_api/rest/resources/2022_01/marketing_event.rb +209 -0
- data/lib/shopify_api/rest/resources/2022_01/metafield.rb +349 -0
- data/lib/shopify_api/rest/resources/2022_01/mobile_platform_application.rb +110 -0
- data/lib/shopify_api/rest/resources/2022_01/order.rb +473 -0
- data/lib/shopify_api/rest/resources/2022_01/order_risk.rb +135 -0
- data/lib/shopify_api/rest/resources/2022_01/page.rb +194 -0
- data/lib/shopify_api/rest/resources/2022_01/payment.rb +140 -0
- data/lib/shopify_api/rest/resources/2022_01/payment_gateway.rb +143 -0
- data/lib/shopify_api/rest/resources/2022_01/payment_transaction.rb +107 -0
- data/lib/shopify_api/rest/resources/2022_01/payout.rb +97 -0
- data/lib/shopify_api/rest/resources/2022_01/policy.rb +69 -0
- data/lib/shopify_api/rest/resources/2022_01/price_rule.rb +223 -0
- data/lib/shopify_api/rest/resources/2022_01/product.rb +223 -0
- data/lib/shopify_api/rest/resources/2022_01/product_listing.rb +196 -0
- data/lib/shopify_api/rest/resources/2022_01/product_resource_feedback.rb +88 -0
- data/lib/shopify_api/rest/resources/2022_01/province.rb +132 -0
- data/lib/shopify_api/rest/resources/2022_01/recurring_application_charge.rb +167 -0
- data/lib/shopify_api/rest/resources/2022_01/redirect.rb +139 -0
- data/lib/shopify_api/rest/resources/2022_01/refund.rb +151 -0
- data/lib/shopify_api/rest/resources/2022_01/report.rb +121 -0
- data/lib/shopify_api/rest/resources/2022_01/resource_feedback.rb +73 -0
- data/lib/shopify_api/rest/resources/2022_01/script_tag.rb +155 -0
- data/lib/shopify_api/rest/resources/2022_01/shipping_zone.rb +83 -0
- data/lib/shopify_api/rest/resources/2022_01/shop.rb +218 -0
- data/lib/shopify_api/rest/resources/2022_01/smart_collection.rb +216 -0
- data/lib/shopify_api/rest/resources/2022_01/storefront_access_token.rb +87 -0
- data/lib/shopify_api/rest/resources/2022_01/tender_transaction.rb +93 -0
- data/lib/shopify_api/rest/resources/2022_01/theme.rb +120 -0
- data/lib/shopify_api/rest/resources/2022_01/transaction.rb +181 -0
- data/lib/shopify_api/rest/resources/2022_01/usage_charge.rb +97 -0
- data/lib/shopify_api/rest/resources/2022_01/user.rb +138 -0
- data/lib/shopify_api/rest/resources/2022_01/variant.rb +212 -0
- data/lib/shopify_api/rest/resources/2022_01/webhook.rb +168 -0
- data/lib/shopify_api/rest/resources/2022_04/abandoned_checkout.rb +190 -0
- data/lib/shopify_api/rest/resources/2022_04/access_scope.rb +58 -0
- data/lib/shopify_api/rest/resources/2022_04/android_pay_key.rb +77 -0
- data/lib/shopify_api/rest/resources/2022_04/apple_pay_certificate.rb +105 -0
- data/lib/shopify_api/rest/resources/2022_04/application_charge.rb +104 -0
- data/lib/shopify_api/rest/resources/2022_04/application_credit.rb +87 -0
- data/lib/shopify_api/rest/resources/2022_04/article.rb +265 -0
- data/lib/shopify_api/rest/resources/2022_04/asset.rb +118 -0
- data/lib/shopify_api/rest/resources/2022_04/assigned_fulfillment_order.rb +79 -0
- data/lib/shopify_api/rest/resources/2022_04/balance.rb +50 -0
- data/lib/shopify_api/rest/resources/2022_04/blog.rb +162 -0
- data/lib/shopify_api/rest/resources/2022_04/cancellation_request.rb +83 -0
- data/lib/shopify_api/rest/resources/2022_04/carrier_service.rb +116 -0
- data/lib/shopify_api/rest/resources/2022_04/checkout.rb +209 -0
- data/lib/shopify_api/rest/resources/2022_04/collect.rb +142 -0
- data/lib/shopify_api/rest/resources/2022_04/collection.rb +110 -0
- data/lib/shopify_api/rest/resources/2022_04/collection_listing.rb +155 -0
- data/lib/shopify_api/rest/resources/2022_04/comment.rb +283 -0
- data/lib/shopify_api/rest/resources/2022_04/country.rb +137 -0
- data/lib/shopify_api/rest/resources/2022_04/currency.rb +57 -0
- data/lib/shopify_api/rest/resources/2022_04/custom_collection.rb +187 -0
- data/lib/shopify_api/rest/resources/2022_04/customer.rb +321 -0
- data/lib/shopify_api/rest/resources/2022_04/customer_address.rb +201 -0
- data/lib/shopify_api/rest/resources/2022_04/customer_saved_search.rb +169 -0
- data/lib/shopify_api/rest/resources/2022_04/deprecated_api_call.rb +57 -0
- data/lib/shopify_api/rest/resources/2022_04/discount_code.rb +219 -0
- data/lib/shopify_api/rest/resources/2022_04/dispute.rb +111 -0
- data/lib/shopify_api/rest/resources/2022_04/draft_order.rb +275 -0
- data/lib/shopify_api/rest/resources/2022_04/event.rb +148 -0
- data/lib/shopify_api/rest/resources/2022_04/fulfillment.rb +268 -0
- data/lib/shopify_api/rest/resources/2022_04/fulfillment_event.rb +166 -0
- data/lib/shopify_api/rest/resources/2022_04/fulfillment_order.rb +284 -0
- data/lib/shopify_api/rest/resources/2022_04/fulfillment_request.rb +87 -0
- data/lib/shopify_api/rest/resources/2022_04/fulfillment_service.rb +130 -0
- data/lib/shopify_api/rest/resources/2022_04/gift_card.rb +215 -0
- data/lib/shopify_api/rest/resources/2022_04/gift_card_adjustment.rb +118 -0
- data/lib/shopify_api/rest/resources/2022_04/image.rb +157 -0
- data/lib/shopify_api/rest/resources/2022_04/inventory_item.rb +108 -0
- data/lib/shopify_api/rest/resources/2022_04/inventory_level.rb +179 -0
- data/lib/shopify_api/rest/resources/2022_04/location.rb +167 -0
- data/lib/shopify_api/rest/resources/2022_04/locations_for_move.rb +56 -0
- data/lib/shopify_api/rest/resources/2022_04/marketing_event.rb +209 -0
- data/lib/shopify_api/rest/resources/2022_04/metafield.rb +342 -0
- data/lib/shopify_api/rest/resources/2022_04/mobile_platform_application.rb +110 -0
- data/lib/shopify_api/rest/resources/2022_04/order.rb +473 -0
- data/lib/shopify_api/rest/resources/2022_04/order_risk.rb +135 -0
- data/lib/shopify_api/rest/resources/2022_04/page.rb +194 -0
- data/lib/shopify_api/rest/resources/2022_04/payment.rb +140 -0
- data/lib/shopify_api/rest/resources/2022_04/payment_gateway.rb +143 -0
- data/lib/shopify_api/rest/resources/2022_04/payment_transaction.rb +107 -0
- data/lib/shopify_api/rest/resources/2022_04/payout.rb +97 -0
- data/lib/shopify_api/rest/resources/2022_04/policy.rb +69 -0
- data/lib/shopify_api/rest/resources/2022_04/price_rule.rb +223 -0
- data/lib/shopify_api/rest/resources/2022_04/product.rb +223 -0
- data/lib/shopify_api/rest/resources/2022_04/product_listing.rb +196 -0
- data/lib/shopify_api/rest/resources/2022_04/product_resource_feedback.rb +88 -0
- data/lib/shopify_api/rest/resources/2022_04/province.rb +132 -0
- data/lib/shopify_api/rest/resources/2022_04/recurring_application_charge.rb +167 -0
- data/lib/shopify_api/rest/resources/2022_04/redirect.rb +139 -0
- data/lib/shopify_api/rest/resources/2022_04/refund.rb +151 -0
- data/lib/shopify_api/rest/resources/2022_04/report.rb +121 -0
- data/lib/shopify_api/rest/resources/2022_04/resource_feedback.rb +73 -0
- data/lib/shopify_api/rest/resources/2022_04/script_tag.rb +155 -0
- data/lib/shopify_api/rest/resources/2022_04/shipping_zone.rb +83 -0
- data/lib/shopify_api/rest/resources/2022_04/shop.rb +218 -0
- data/lib/shopify_api/rest/resources/2022_04/smart_collection.rb +216 -0
- data/lib/shopify_api/rest/resources/2022_04/storefront_access_token.rb +87 -0
- data/lib/shopify_api/rest/resources/2022_04/tender_transaction.rb +93 -0
- data/lib/shopify_api/rest/resources/2022_04/theme.rb +120 -0
- data/lib/shopify_api/rest/resources/2022_04/transaction.rb +181 -0
- data/lib/shopify_api/rest/resources/2022_04/usage_charge.rb +97 -0
- data/lib/shopify_api/rest/resources/2022_04/user.rb +138 -0
- data/lib/shopify_api/rest/resources/2022_04/variant.rb +212 -0
- data/lib/shopify_api/rest/resources/2022_04/webhook.rb +168 -0
- data/lib/shopify_api/rest/resources/2022_07/abandoned_checkout.rb +190 -0
- data/lib/shopify_api/rest/resources/2022_07/access_scope.rb +58 -0
- data/lib/shopify_api/rest/resources/2022_07/android_pay_key.rb +77 -0
- data/lib/shopify_api/rest/resources/2022_07/apple_pay_certificate.rb +105 -0
- data/lib/shopify_api/rest/resources/2022_07/application_charge.rb +104 -0
- data/lib/shopify_api/rest/resources/2022_07/application_credit.rb +87 -0
- data/lib/shopify_api/rest/resources/2022_07/article.rb +265 -0
- data/lib/shopify_api/rest/resources/2022_07/asset.rb +118 -0
- data/lib/shopify_api/rest/resources/2022_07/assigned_fulfillment_order.rb +79 -0
- data/lib/shopify_api/rest/resources/2022_07/balance.rb +50 -0
- data/lib/shopify_api/rest/resources/2022_07/blog.rb +162 -0
- data/lib/shopify_api/rest/resources/2022_07/cancellation_request.rb +83 -0
- data/lib/shopify_api/rest/resources/2022_07/carrier_service.rb +113 -0
- data/lib/shopify_api/rest/resources/2022_07/checkout.rb +209 -0
- data/lib/shopify_api/rest/resources/2022_07/collect.rb +142 -0
- data/lib/shopify_api/rest/resources/2022_07/collection.rb +110 -0
- data/lib/shopify_api/rest/resources/2022_07/collection_listing.rb +155 -0
- data/lib/shopify_api/rest/resources/2022_07/comment.rb +283 -0
- data/lib/shopify_api/rest/resources/2022_07/country.rb +137 -0
- data/lib/shopify_api/rest/resources/2022_07/currency.rb +57 -0
- data/lib/shopify_api/rest/resources/2022_07/custom_collection.rb +187 -0
- data/lib/shopify_api/rest/resources/2022_07/customer.rb +321 -0
- data/lib/shopify_api/rest/resources/2022_07/customer_address.rb +201 -0
- data/lib/shopify_api/rest/resources/2022_07/customer_saved_search.rb +169 -0
- data/lib/shopify_api/rest/resources/2022_07/deprecated_api_call.rb +57 -0
- data/lib/shopify_api/rest/resources/2022_07/discount_code.rb +219 -0
- data/lib/shopify_api/rest/resources/2022_07/dispute.rb +111 -0
- data/lib/shopify_api/rest/resources/2022_07/dispute_evidence.rb +117 -0
- data/lib/shopify_api/rest/resources/2022_07/dispute_file_upload.rb +81 -0
- data/lib/shopify_api/rest/resources/2022_07/draft_order.rb +275 -0
- data/lib/shopify_api/rest/resources/2022_07/event.rb +148 -0
- data/lib/shopify_api/rest/resources/2022_07/fulfillment.rb +221 -0
- data/lib/shopify_api/rest/resources/2022_07/fulfillment_event.rb +166 -0
- data/lib/shopify_api/rest/resources/2022_07/fulfillment_order.rb +310 -0
- data/lib/shopify_api/rest/resources/2022_07/fulfillment_request.rb +87 -0
- data/lib/shopify_api/rest/resources/2022_07/fulfillment_service.rb +130 -0
- data/lib/shopify_api/rest/resources/2022_07/gift_card.rb +215 -0
- data/lib/shopify_api/rest/resources/2022_07/gift_card_adjustment.rb +118 -0
- data/lib/shopify_api/rest/resources/2022_07/image.rb +157 -0
- data/lib/shopify_api/rest/resources/2022_07/inventory_item.rb +108 -0
- data/lib/shopify_api/rest/resources/2022_07/inventory_level.rb +179 -0
- data/lib/shopify_api/rest/resources/2022_07/location.rb +167 -0
- data/lib/shopify_api/rest/resources/2022_07/locations_for_move.rb +56 -0
- data/lib/shopify_api/rest/resources/2022_07/marketing_event.rb +209 -0
- data/lib/shopify_api/rest/resources/2022_07/metafield.rb +342 -0
- data/lib/shopify_api/rest/resources/2022_07/mobile_platform_application.rb +110 -0
- data/lib/shopify_api/rest/resources/2022_07/order.rb +473 -0
- data/lib/shopify_api/rest/resources/2022_07/order_risk.rb +135 -0
- data/lib/shopify_api/rest/resources/2022_07/page.rb +194 -0
- data/lib/shopify_api/rest/resources/2022_07/payment.rb +140 -0
- data/lib/shopify_api/rest/resources/2022_07/payment_gateway.rb +143 -0
- data/lib/shopify_api/rest/resources/2022_07/payment_transaction.rb +107 -0
- data/lib/shopify_api/rest/resources/2022_07/payout.rb +97 -0
- data/lib/shopify_api/rest/resources/2022_07/policy.rb +69 -0
- data/lib/shopify_api/rest/resources/2022_07/price_rule.rb +223 -0
- data/lib/shopify_api/rest/resources/2022_07/product.rb +223 -0
- data/lib/shopify_api/rest/resources/2022_07/product_listing.rb +196 -0
- data/lib/shopify_api/rest/resources/2022_07/product_resource_feedback.rb +88 -0
- data/lib/shopify_api/rest/resources/2022_07/province.rb +132 -0
- data/lib/shopify_api/rest/resources/2022_07/recurring_application_charge.rb +167 -0
- data/lib/shopify_api/rest/resources/2022_07/redirect.rb +139 -0
- data/lib/shopify_api/rest/resources/2022_07/refund.rb +151 -0
- data/lib/shopify_api/rest/resources/2022_07/report.rb +121 -0
- data/lib/shopify_api/rest/resources/2022_07/resource_feedback.rb +73 -0
- data/lib/shopify_api/rest/resources/2022_07/script_tag.rb +155 -0
- data/lib/shopify_api/rest/resources/2022_07/shipping_zone.rb +83 -0
- data/lib/shopify_api/rest/resources/2022_07/shop.rb +218 -0
- data/lib/shopify_api/rest/resources/2022_07/smart_collection.rb +216 -0
- data/lib/shopify_api/rest/resources/2022_07/storefront_access_token.rb +87 -0
- data/lib/shopify_api/rest/resources/2022_07/tender_transaction.rb +93 -0
- data/lib/shopify_api/rest/resources/2022_07/theme.rb +120 -0
- data/lib/shopify_api/rest/resources/2022_07/transaction.rb +181 -0
- data/lib/shopify_api/rest/resources/2022_07/usage_charge.rb +97 -0
- data/lib/shopify_api/rest/resources/2022_07/user.rb +138 -0
- data/lib/shopify_api/rest/resources/2022_07/variant.rb +212 -0
- data/lib/shopify_api/rest/resources/2022_07/webhook.rb +168 -0
- data/lib/shopify_api/rest/resources/2022_10/abandoned_checkout.rb +190 -0
- data/lib/shopify_api/rest/resources/2022_10/access_scope.rb +58 -0
- data/lib/shopify_api/rest/resources/2022_10/android_pay_key.rb +77 -0
- data/lib/shopify_api/rest/resources/2022_10/apple_pay_certificate.rb +105 -0
- data/lib/shopify_api/rest/resources/2022_10/application_charge.rb +104 -0
- data/lib/shopify_api/rest/resources/2022_10/application_credit.rb +87 -0
- data/lib/shopify_api/rest/resources/2022_10/article.rb +265 -0
- data/lib/shopify_api/rest/resources/2022_10/asset.rb +118 -0
- data/lib/shopify_api/rest/resources/2022_10/assigned_fulfillment_order.rb +79 -0
- data/lib/shopify_api/rest/resources/2022_10/balance.rb +50 -0
- data/lib/shopify_api/rest/resources/2022_10/blog.rb +162 -0
- data/lib/shopify_api/rest/resources/2022_10/cancellation_request.rb +83 -0
- data/lib/shopify_api/rest/resources/2022_10/carrier_service.rb +113 -0
- data/lib/shopify_api/rest/resources/2022_10/checkout.rb +209 -0
- data/lib/shopify_api/rest/resources/2022_10/collect.rb +142 -0
- data/lib/shopify_api/rest/resources/2022_10/collection.rb +110 -0
- data/lib/shopify_api/rest/resources/2022_10/collection_listing.rb +155 -0
- data/lib/shopify_api/rest/resources/2022_10/comment.rb +283 -0
- data/lib/shopify_api/rest/resources/2022_10/country.rb +137 -0
- data/lib/shopify_api/rest/resources/2022_10/currency.rb +57 -0
- data/lib/shopify_api/rest/resources/2022_10/custom_collection.rb +187 -0
- data/lib/shopify_api/rest/resources/2022_10/customer.rb +321 -0
- data/lib/shopify_api/rest/resources/2022_10/customer_address.rb +201 -0
- data/lib/shopify_api/rest/resources/2022_10/customer_saved_search.rb +169 -0
- data/lib/shopify_api/rest/resources/2022_10/deprecated_api_call.rb +57 -0
- data/lib/shopify_api/rest/resources/2022_10/discount_code.rb +219 -0
- data/lib/shopify_api/rest/resources/2022_10/dispute.rb +111 -0
- data/lib/shopify_api/rest/resources/2022_10/dispute_evidence.rb +117 -0
- data/lib/shopify_api/rest/resources/2022_10/dispute_file_upload.rb +81 -0
- data/lib/shopify_api/rest/resources/2022_10/draft_order.rb +275 -0
- data/lib/shopify_api/rest/resources/2022_10/event.rb +148 -0
- data/lib/shopify_api/rest/resources/2022_10/fulfillment.rb +221 -0
- data/lib/shopify_api/rest/resources/2022_10/fulfillment_event.rb +166 -0
- data/lib/shopify_api/rest/resources/2022_10/fulfillment_order.rb +310 -0
- data/lib/shopify_api/rest/resources/2022_10/fulfillment_request.rb +87 -0
- data/lib/shopify_api/rest/resources/2022_10/fulfillment_service.rb +130 -0
- data/lib/shopify_api/rest/resources/2022_10/gift_card.rb +215 -0
- data/lib/shopify_api/rest/resources/2022_10/gift_card_adjustment.rb +118 -0
- data/lib/shopify_api/rest/resources/2022_10/image.rb +157 -0
- data/lib/shopify_api/rest/resources/2022_10/inventory_item.rb +108 -0
- data/lib/shopify_api/rest/resources/2022_10/inventory_level.rb +179 -0
- data/lib/shopify_api/rest/resources/2022_10/location.rb +167 -0
- data/lib/shopify_api/rest/resources/2022_10/locations_for_move.rb +56 -0
- data/lib/shopify_api/rest/resources/2022_10/marketing_event.rb +209 -0
- data/lib/shopify_api/rest/resources/2022_10/metafield.rb +342 -0
- data/lib/shopify_api/rest/resources/2022_10/mobile_platform_application.rb +110 -0
- data/lib/shopify_api/rest/resources/2022_10/order.rb +476 -0
- data/lib/shopify_api/rest/resources/2022_10/order_risk.rb +135 -0
- data/lib/shopify_api/rest/resources/2022_10/page.rb +194 -0
- data/lib/shopify_api/rest/resources/2022_10/payment.rb +140 -0
- data/lib/shopify_api/rest/resources/2022_10/payment_gateway.rb +143 -0
- data/lib/shopify_api/rest/resources/2022_10/payment_transaction.rb +107 -0
- data/lib/shopify_api/rest/resources/2022_10/payout.rb +97 -0
- data/lib/shopify_api/rest/resources/2022_10/policy.rb +69 -0
- data/lib/shopify_api/rest/resources/2022_10/price_rule.rb +223 -0
- data/lib/shopify_api/rest/resources/2022_10/product.rb +223 -0
- data/lib/shopify_api/rest/resources/2022_10/product_listing.rb +196 -0
- data/lib/shopify_api/rest/resources/2022_10/product_resource_feedback.rb +88 -0
- data/lib/shopify_api/rest/resources/2022_10/province.rb +132 -0
- data/lib/shopify_api/rest/resources/2022_10/recurring_application_charge.rb +167 -0
- data/lib/shopify_api/rest/resources/2022_10/redirect.rb +139 -0
- data/lib/shopify_api/rest/resources/2022_10/refund.rb +151 -0
- data/lib/shopify_api/rest/resources/2022_10/report.rb +121 -0
- data/lib/shopify_api/rest/resources/2022_10/resource_feedback.rb +73 -0
- data/lib/shopify_api/rest/resources/2022_10/script_tag.rb +155 -0
- data/lib/shopify_api/rest/resources/2022_10/shipping_zone.rb +83 -0
- data/lib/shopify_api/rest/resources/2022_10/shop.rb +221 -0
- data/lib/shopify_api/rest/resources/2022_10/smart_collection.rb +216 -0
- data/lib/shopify_api/rest/resources/2022_10/storefront_access_token.rb +87 -0
- data/lib/shopify_api/rest/resources/2022_10/tender_transaction.rb +93 -0
- data/lib/shopify_api/rest/resources/2022_10/theme.rb +120 -0
- data/lib/shopify_api/rest/resources/2022_10/transaction.rb +181 -0
- data/lib/shopify_api/rest/resources/2022_10/usage_charge.rb +97 -0
- data/lib/shopify_api/rest/resources/2022_10/user.rb +138 -0
- data/lib/shopify_api/rest/resources/2022_10/variant.rb +212 -0
- data/lib/shopify_api/rest/resources/2022_10/webhook.rb +168 -0
- data/lib/shopify_api/utils/graphql_proxy.rb +52 -0
- data/lib/shopify_api/utils/hmac_validator.rb +44 -0
- data/lib/shopify_api/utils/http_utils.rb +17 -0
- data/lib/shopify_api/utils/session_utils.rb +152 -0
- data/lib/shopify_api/utils/verifiable_query.rb +18 -0
- data/lib/shopify_api/version.rb +6 -0
- data/lib/shopify_api/webhooks/handler.rb +15 -0
- data/lib/shopify_api/webhooks/register_result.rb +14 -0
- data/lib/shopify_api/webhooks/registration.rb +73 -0
- data/lib/shopify_api/webhooks/registrations/event_bridge.rb +61 -0
- data/lib/shopify_api/webhooks/registrations/http.rb +72 -0
- data/lib/shopify_api/webhooks/registrations/pub_sub.rb +65 -0
- data/lib/shopify_api/webhooks/registry.rb +215 -0
- data/lib/shopify_api/webhooks/request.rb +56 -0
- data/lib/shopify_api.rb +23 -472
- data/service.yml +1 -0
- data/shipit.rubygems.yml +1 -0
- data/shopify_api.gemspec +43 -102
- data/sorbet/config +3 -0
- data/sorbet/rbi/gems/activesupport@7.0.1.rbi +654 -0
- data/sorbet/rbi/gems/addressable@2.8.0.rbi +290 -0
- data/sorbet/rbi/gems/ast@2.4.2.rbi +54 -0
- data/sorbet/rbi/gems/coderay@1.1.3.rbi +8 -0
- data/sorbet/rbi/gems/concurrent-ruby@1.1.9.rbi +2401 -0
- data/sorbet/rbi/gems/crack@0.4.5.rbi +57 -0
- data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +185 -0
- data/sorbet/rbi/gems/fakefs@1.4.1.rbi +571 -0
- data/sorbet/rbi/gems/hash_diff@1.0.0.rbi +47 -0
- data/sorbet/rbi/gems/hashdiff@1.0.1.rbi +82 -0
- data/sorbet/rbi/gems/httparty@0.20.0.rbi +573 -0
- data/sorbet/rbi/gems/i18n@1.8.11.rbi +25 -0
- data/sorbet/rbi/gems/jwt@2.3.0.rbi +437 -0
- data/sorbet/rbi/gems/method_source@1.0.0.rbi +8 -0
- data/sorbet/rbi/gems/mime-types-data@3.2022.0105.rbi +73 -0
- data/sorbet/rbi/gems/mime-types@3.4.1.rbi +295 -0
- data/sorbet/rbi/gems/minitest@5.15.0.rbi +541 -0
- data/sorbet/rbi/gems/mocha@1.13.0.rbi +986 -0
- data/sorbet/rbi/gems/multi_xml@0.6.0.rbi +36 -0
- data/sorbet/rbi/gems/oj@3.13.11.rbi +274 -0
- data/sorbet/rbi/gems/openssl@3.0.0.rbi +581 -0
- data/sorbet/rbi/gems/parallel@1.21.0.rbi +113 -0
- data/sorbet/rbi/gems/parser@3.1.0.0.rbi +1741 -0
- data/sorbet/rbi/gems/pry@0.14.1.rbi +8 -0
- data/sorbet/rbi/gems/public_suffix@4.0.6.rbi +145 -0
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +157 -0
- data/sorbet/rbi/gems/rake@13.0.6.rbi +814 -0
- data/sorbet/rbi/gems/rbi@0.0.11.rbi +1646 -0
- data/sorbet/rbi/gems/regexp_parser@2.2.0.rbi +1130 -0
- data/sorbet/rbi/gems/rexml@3.2.5.rbi +709 -0
- data/sorbet/rbi/gems/rubocop-ast@1.15.1.rbi +1921 -0
- data/sorbet/rbi/gems/rubocop-shopify@2.4.0.rbi +8 -0
- data/sorbet/rbi/gems/rubocop-sorbet@0.6.5.rbi +295 -0
- data/sorbet/rbi/gems/rubocop@1.25.1.rbi +13507 -0
- data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +405 -0
- data/sorbet/rbi/gems/securerandom@0.1.1.rbi +10 -0
- data/sorbet/rbi/gems/spoom@1.1.8.rbi +1252 -0
- data/sorbet/rbi/gems/tapioca@0.6.3.rbi +1238 -0
- data/sorbet/rbi/gems/thor@1.2.1.rbi +844 -0
- data/sorbet/rbi/gems/tzinfo@2.0.4.rbi +858 -0
- data/sorbet/rbi/gems/unicode-display_width@2.1.0.rbi +26 -0
- data/sorbet/rbi/gems/unparser@0.6.3.rbi +1816 -0
- data/sorbet/rbi/gems/webmock@3.14.0.rbi +683 -0
- data/sorbet/rbi/gems/webrick@1.7.0.rbi +601 -0
- data/sorbet/rbi/gems/yard-sorbet@0.6.1.rbi +199 -0
- data/sorbet/rbi/gems/yard@0.9.27.rbi +4145 -0
- data/sorbet/rbi/gems/zeitwerk@2.5.4.rbi +200 -0
- data/sorbet/rbi/shims/fakefs.rbi +1 -0
- data/sorbet/rbi/shims/openssl.rb +3 -0
- data/sorbet/rbi/todo.rbi +8 -0
- data/sorbet/tapioca/config.yml +4 -0
- data/sorbet/tapioca/require.rb +20 -0
- metadata +736 -80
- data/.document +0 -5
- data/.gitignore +0 -5
- data/CHANGELOG +0 -5
- data/README.rdoc +0 -55
- data/VERSION +0 -1
- data/test/order_test.rb +0 -48
- data/test/shopify_api_test.rb +0 -21
- data/test/test_helper.rb +0 -28
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Make a GraphQL API call
|
|
2
|
+
|
|
3
|
+
Once you have a [session](oauth.md#fetching-sessions) after completing oauth, you can make GraphQL queries to the Admin API with `ShopifyAPI::Clients::Graphql::Admin`
|
|
4
|
+
|
|
5
|
+
Below is an example
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
# load the current session with SessionUtils.load_current_session
|
|
9
|
+
session = ShopifyAPI::Utils::SessionUtils.load_current_session(auth_header: <auth-header>, cookies: <cookies>, is_online: <true|false>)
|
|
10
|
+
|
|
11
|
+
# initalize the client
|
|
12
|
+
client = ShopifyAPI::Clients::Graphql::Admin.new(session: session)
|
|
13
|
+
|
|
14
|
+
# make the GraphQL query string
|
|
15
|
+
query =<<~QUERY
|
|
16
|
+
{
|
|
17
|
+
products(first: 10) {
|
|
18
|
+
edges {
|
|
19
|
+
cursor
|
|
20
|
+
node {
|
|
21
|
+
id
|
|
22
|
+
title
|
|
23
|
+
onlineStoreUrl
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
QUERY
|
|
29
|
+
|
|
30
|
+
response = client.query(query: query)
|
|
31
|
+
# do something with the response data
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
You can also make GraphQL calls that take in variables
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
client = ShopifyAPI::Clients::Graphql::Admin.new(session: session)
|
|
38
|
+
|
|
39
|
+
query = <<~QUERY
|
|
40
|
+
query testQueryWithVariables($first: Int!){
|
|
41
|
+
products(first: $first) {
|
|
42
|
+
edges {
|
|
43
|
+
cursor
|
|
44
|
+
node {
|
|
45
|
+
id
|
|
46
|
+
title
|
|
47
|
+
onlineStoreUrl
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
QUERY
|
|
53
|
+
variables = {
|
|
54
|
+
first: 3
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
response = client.query(query: query, variables: variables)
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Here is an example of how you might use fragments as part of the client
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
client = ShopifyAPI::Clients::Graphql::Admin.new(session: session)
|
|
65
|
+
# define the fragment as part of the query
|
|
66
|
+
query = <<~QUERY
|
|
67
|
+
fragment ProductStuff on Product {
|
|
68
|
+
id
|
|
69
|
+
title
|
|
70
|
+
description
|
|
71
|
+
onlineStoreUrl
|
|
72
|
+
}
|
|
73
|
+
query testQueryWithVariables($first: Int){
|
|
74
|
+
products(first: $first) {
|
|
75
|
+
edges {
|
|
76
|
+
cursor
|
|
77
|
+
node {
|
|
78
|
+
...ProductStuff
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
QUERY
|
|
84
|
+
variables = {
|
|
85
|
+
first: 3
|
|
86
|
+
}
|
|
87
|
+
response = client.query(query: query, variables: variables)
|
|
88
|
+
# do something with the reponse
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Want to make calls to the Storefront API? Click [here](graphql_storefront.md)
|
|
92
|
+
|
|
93
|
+
# Proxy a GraphQL Query
|
|
94
|
+
|
|
95
|
+
If you would like to give your front end the ability to make authenticated graphql queries to the Shopify Admin API, the `shopify_api` gem makes proxying a graphql request easy! The gem provides a utility function which will accept the raw request body (a GraphQL query), the headers, and the cookies (optional). It will add authentication to the request, proxy it to the Shopify Admin API, and return a `ShopifyAPI::Clients::HttpResponse`. An example utilization of this in Rails is shown below:
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
def proxy
|
|
99
|
+
begin
|
|
100
|
+
response = ShopifyAPI::Utils::GraphqlProxy.proxy_query(
|
|
101
|
+
headers: request.headers.to_h,
|
|
102
|
+
body: request.raw_post,
|
|
103
|
+
cookies: request.cookies.to_h
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
render json: response.body, status: response.code
|
|
107
|
+
rescue ShopifyAPI::Errors::InvalidGraphqlRequestError
|
|
108
|
+
# Handle bad request
|
|
109
|
+
rescue ShopifyAPI::Errors::SessionNotFoundError
|
|
110
|
+
# Handle no session found
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Note:** GraphQL proxying is only supported for online sessions for non-private apps, the utility will raise a `ShopifyAPI::Errors::SessionNotFoundError` if there are no existing online tokens for the provided credentials, and a `ShopifyAPI::Errors::PrivateAppError` if called from a private app.
|
|
@@ -0,0 +1,42 @@
|
|
|
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/storefront-api). To do that, you can use `ShopifyAPI::Clients::Graphql::Storefront` with the current session and a `storefrontAccessToken`.
|
|
4
|
+
|
|
5
|
+
You can obtain Storefront API access tokens for both private apps and sales channels. Please read [our documentation](https://shopify.dev/docs/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_access_token = ''
|
|
12
|
+
# your shop domain
|
|
13
|
+
shop_url = 'shop.myshopify.com'
|
|
14
|
+
|
|
15
|
+
# initialize the client with session and storefront access token
|
|
16
|
+
client = ShopifyAPI::Clients::Graphql::Storefront.new(shop_url, storefront_access_token)
|
|
17
|
+
|
|
18
|
+
query = <<~QUERY
|
|
19
|
+
{
|
|
20
|
+
collections(first: 2) {
|
|
21
|
+
edges {
|
|
22
|
+
node {
|
|
23
|
+
id
|
|
24
|
+
products(first: 5) {
|
|
25
|
+
edges {
|
|
26
|
+
node {
|
|
27
|
+
id
|
|
28
|
+
title
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
QUERY
|
|
37
|
+
|
|
38
|
+
response = client.query(query: query)
|
|
39
|
+
# do something with the returned data
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Want to make calls to the Admin API? Click [here](graphql.md)
|
data/docs/usage/oauth.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Performing OAuth
|
|
2
|
+
|
|
3
|
+
Once the library is set up for your project, you'll be able to use it to start adding functionality to your app. The first thing your app will need to do is to obtain an access token to the Admin API by performing the OAuth process.
|
|
4
|
+
|
|
5
|
+
To do this, you can follow the steps below.
|
|
6
|
+
For more information on authenticating a Shopify app please see the [Types of Authentication](https://shopify.dev/apps/auth#types-of-authentication) page.
|
|
7
|
+
|
|
8
|
+
## Add a route to start OAuth
|
|
9
|
+
|
|
10
|
+
The route for starting the OAuth process (in this case `/login`) will use the library's `begin_auth` method. The method will return an `auth_route` URI that will be used for redirecting the user to the Shopify Authentication screen and a session cookie to store on the user's browser. These return values will be a hash in the form of {`auth_route`: `String`, `cookie`: `ShopifyAPI::Auth::Oauth::SessionCookie`}
|
|
11
|
+
|
|
12
|
+
| Parameter | Type | Required? | Default Value | Notes |
|
|
13
|
+
| -------------- | ---------------------- | :-------: | :-----------: | ----------------------------------------------------------------------------------------------------------- |
|
|
14
|
+
| `shop` | `String` | Yes | - | A Shopify domain name in the form `{exampleshop}.myshopify.com`. |
|
|
15
|
+
| `redirect_path` | `String` | Yes | - | The redirect path used for callback with a leading `/`. The route should be allowed under the app settings. |
|
|
16
|
+
| `is_online` | `Boolean` | No | `true` | `true` if the session is online and `false` otherwise. |
|
|
17
|
+
|
|
18
|
+
Your app should take the returned values from the `begin_auth` function and redirect the user to url defined by `auth_route` and set the cookie in the user's browser. We strongly recommend that you use secure, httpOnly cookies for this to help prevent session hijacking.
|
|
19
|
+
|
|
20
|
+
An example is shown below in a Rails app but these steps could be applied in any framework:
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
class ShopifyAuthController < ApplicationController
|
|
24
|
+
def login
|
|
25
|
+
shop = request.headers["Shop"]
|
|
26
|
+
|
|
27
|
+
auth_response = ShopifyAPI::Auth::Oauth.begin_auth(shop: domain, redirect_path: "/auth/callback")
|
|
28
|
+
|
|
29
|
+
cookies[auth_response[:cookie].name] = {
|
|
30
|
+
expires: auth_response[:cookie].expires,
|
|
31
|
+
secure: true,
|
|
32
|
+
http_only: true,
|
|
33
|
+
value: auth_response[:cookie].value
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
head 307
|
|
37
|
+
response.set_header("Location", auth_response[:auth_route])
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Add your OAuth callback route
|
|
43
|
+
|
|
44
|
+
After the app is authenticated with Shopify, the Shopify platform will send a request back to your app using this route (which you provided as a parameter to `begin_auth`, above). Your app will now use the provided `validate_auth_callback` method to finalize the OAuth process. This method returns a hash containing the new session and a cookie to be set in the browser in form of {`session`: `ShopifyAPI::Auth::Session`, `cookie`: `ShopifyAPI::Auth::Oauth::SessionCookie`}.
|
|
45
|
+
|
|
46
|
+
An example is shown below in a Rails app but these steps could be applied in any framework:
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
def callback
|
|
50
|
+
begin
|
|
51
|
+
auth_result = ShopifyAPI::Auth::Oauth.validate_auth_callback(
|
|
52
|
+
cookies: cookies.to_h,
|
|
53
|
+
auth_query: ShopifyAPI::Auth::Oauth::AuthQuery.new(request.parameters.symbolize_keys.except(:controller, :action))
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
cookies[auth_result[:cookie].name] = {
|
|
57
|
+
expires: auth_result[:cookie].expires,
|
|
58
|
+
secure: true,
|
|
59
|
+
http_only: true,
|
|
60
|
+
value: auth_result[:cookie].value
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
puts("OAuth complete! New access token: #{auth_result[:session].access_token}")
|
|
64
|
+
|
|
65
|
+
head 307
|
|
66
|
+
response.set_header("Location", "<some-redirect-url>")
|
|
67
|
+
rescue => e
|
|
68
|
+
puts(e.message)
|
|
69
|
+
head 500
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
```
|
|
73
|
+
## Fetching sessions
|
|
74
|
+
|
|
75
|
+
You can use the OAuth methods to create both offline and online sessions. Once the process is completed, the session will be stored as per your `Context.session_storage`, and can be retrieved with `SessionUtils` class methods.
|
|
76
|
+
|
|
77
|
+
- To load current session, you can use the following method:
|
|
78
|
+
|
|
79
|
+
```ruby
|
|
80
|
+
ShopifyAPI::Utils::SessionUtils.load_current_session(auth_header: <auth-header>, cookies: <cookies>, is_online: <true|false>)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Accepted arguments:
|
|
84
|
+
| Parameter | Type | Notes |
|
|
85
|
+
| ----------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| `auth_header` | `String` | JWT token will be extracted from `auth_header` to load session for embedded apps. If JWT token is not provided this methods will try with `cookies`. Only required if trying to load, an embedded session. |
|
|
87
|
+
| `cookies` | `Hash(String, String)` | The cookies from the HTTP request. A session cookie named `shopify_app_session` is used to load session for non-embedded apps. Can be omitted if loading and embedded session without falling back on cookies |
|
|
88
|
+
| `is_online` | `Boolean` | Whether to load online or offline sessions. Defaults to `false` |
|
|
89
|
+
|
|
90
|
+
This method will return a `ShopifyAPI::Auth::Session` if a session exists. Either a proper token or a proper cookie must be present.
|
|
91
|
+
|
|
92
|
+
- To load offline session, you can use the following method:
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
ShopifyAPI::Utils::SessionUtils.load_offline_session(shop)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Accepted arguments:
|
|
99
|
+
| Parameter | Type | Notes |
|
|
100
|
+
| ------------------- | --------- | --------------------------------------------- |
|
|
101
|
+
| `shop` | `String` | The shop url to find the offline session for. |
|
|
102
|
+
| `include_expired` | `Boolean` | Include expired sessions or not. |
|
|
103
|
+
|
|
104
|
+
This method will return a `ShopifyAPI::Auth::Session` if a session exists and `nil` otherwise. This method **does not** perform any validation on the shop domain, so it **must not** rely on user input for the domain. This method is typically meant to be used in background tasks like webhooks, where the data is expected to have been validated when the task was enqueued.
|
data/docs/usage/rest.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Make a REST API call
|
|
2
|
+
|
|
3
|
+
Once OAuth is complete, we can use the `ShopifyAPI::Clients::Rest::Admin` client to make an API call to the Shopify Admin API. To do this, you can create an instance of `ShopifyAPI::Clients::Rest::Admin` using the current session to make requests to the Admin API.
|
|
4
|
+
|
|
5
|
+
## Methods
|
|
6
|
+
|
|
7
|
+
The Rest Admin client offers the 4 core request methods: `get`, `delete`, `post`, and `put`. These methods each take the parameters outlined in the table below. If the request is successful these methods will all return a `ShopifyAPI::Clients::HttpResponse` object, which has properties `code`, `headers`, and `body` otherwise an error will be raised describing what went wrong.
|
|
8
|
+
|
|
9
|
+
| Parameter | Type | Required in Methods | Default Value | Notes |
|
|
10
|
+
| -------------- | -------------------------------------------------------- | :-----------------: | :-----------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
11
|
+
| `path` | `String` | all | none | The requested API endpoint path. This can be one of two formats:<ul><li>The path starting after the `/admin/api/{version}/` prefix, such as `products`, which executes `/admin/api/{version}/products.json`</li><li>The full path, such as `/admin/oauth/access_scopes.json`</li></ul> |
|
|
12
|
+
| `body` | `Hash(any(Symbol, String), untyped)` | `put`, `post` | none | The body of the request |
|
|
13
|
+
| `query` | `Hash(any(Symbol, String), any(String, Integer, Float))` | none | none | An optional query object to be appended to the request url as a query string |
|
|
14
|
+
| `extraHeaders` | `Hash(any(Symbol, String), any(String, Integer, Float))` | none | none | Any additional headers you want to send with your request |
|
|
15
|
+
| `tries` | `Integer` | None | `1` | The maximum number of times to try the request _(must be >= 0)_ |
|
|
16
|
+
|
|
17
|
+
**Note:** _These paramaters can still be used in all methods regardless of if they are required._
|
|
18
|
+
|
|
19
|
+
## Usage Examples:
|
|
20
|
+
|
|
21
|
+
### Perform a `GET` request:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
# Load the current session to get the `accessToken`.
|
|
25
|
+
session = ShopifyAPI::Utils::SessionUtils.load_current_session(headers, cookies, is_online)
|
|
26
|
+
|
|
27
|
+
# Create a new client.
|
|
28
|
+
client = ShopifyAPI::Clients::Rest::Admin.new(session: session)
|
|
29
|
+
|
|
30
|
+
# Use `client.get` to request the specified Shopify REST API endpoint, in this case `products`.
|
|
31
|
+
response = client.get(path: "products")
|
|
32
|
+
|
|
33
|
+
# Do something with the returned data
|
|
34
|
+
some_function(response.body)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Perform a `POST` request:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
# Load the current session to get the `accessToken`.
|
|
41
|
+
session = ShopifyAPI::Utils::SessionUtils.load_current_session(headers, cookies, is_online)
|
|
42
|
+
|
|
43
|
+
# Create a new client.
|
|
44
|
+
client = ShopifyAPI::Clients::Rest::Admin.new(session: session)
|
|
45
|
+
|
|
46
|
+
# Build your post request body.
|
|
47
|
+
body = {
|
|
48
|
+
product: {
|
|
49
|
+
title: "Burton Custom Freestyle 151",
|
|
50
|
+
body_html: "\u003cstrong\u003eGood snowboard!\u003c\/strong\u003e",
|
|
51
|
+
vendor: "Burton",
|
|
52
|
+
product_type: "Snowboard",
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Use `client.post` to send your request to the specified Shopify Admin REST API endpoint.
|
|
57
|
+
client.post({
|
|
58
|
+
path: "products",
|
|
59
|
+
body: body,
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
_for more information on the `products` endpoint, [check out our API reference guide](https://shopify.dev/api/admin-rest/unstable/resources/product)._
|
|
64
|
+
|
|
65
|
+
## Pagination
|
|
66
|
+
|
|
67
|
+
This library also supports cursor-based pagination for REST Admin API requests. [Learn more about REST request pagination](https://shopify.dev/api/usage/pagination-rest).
|
|
68
|
+
|
|
69
|
+
After making a request, the `next_page_info` and `prev_page_info` can be found on the response object and passed as the page_info query param in other requests.
|
|
70
|
+
|
|
71
|
+
An example of this is shown below:
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
session = ShopifyAPI::Utils::SessionUtils.load_current_session(headers, cookies, is_online)
|
|
75
|
+
client = ShopifyAPI::Clients::Rest::Admin.new(session: session)
|
|
76
|
+
|
|
77
|
+
response = client.get(path: "products", query: { limit: 10 })
|
|
78
|
+
|
|
79
|
+
loop do
|
|
80
|
+
some_function(response.body)
|
|
81
|
+
break unless response.next_page_info
|
|
82
|
+
response = client.get(path: "products", query: { limit: 10, page_info: response.next_page_info })
|
|
83
|
+
end
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Similarly, when using REST resources the `next_page_info` and `prev_page_info` can be found on the Resource class and passed as the page_info query param in other requests.
|
|
87
|
+
|
|
88
|
+
An example of this is shown below:
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
session = ShopifyAPI::Utils::SessionUtils.load_current_session(headers, cookies, is_online)
|
|
92
|
+
|
|
93
|
+
products = ShopifyAPI::Product.all(session: session, limit: 10)
|
|
94
|
+
|
|
95
|
+
loop do
|
|
96
|
+
some_function(products)
|
|
97
|
+
break unless ShopifyAPI::Product.next_page?
|
|
98
|
+
products = ShopifyAPI::Product.all(session: session, limit: 10, page_info: ShopifyAPI::Product.next_page_info)
|
|
99
|
+
end
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The next/previous page_info strings can also be retrieved from the response object and added to a request query to retrieve the next/previous pages.
|
|
103
|
+
|
|
104
|
+
An example of this is shown below:
|
|
105
|
+
|
|
106
|
+
```ruby
|
|
107
|
+
session = ShopifyAPI::Utils::SessionUtils.load_current_session(headers, cookies, is_online)
|
|
108
|
+
client = ShopifyAPI::Clients::Rest::Admin.new(session: session)
|
|
109
|
+
|
|
110
|
+
response = client.get(path: "products", query: { limit: 10 })
|
|
111
|
+
next_page_info = response.next_page_info
|
|
112
|
+
|
|
113
|
+
if next_page_info
|
|
114
|
+
next_page_response =client.get(path: "products", query: { limit: 10, page_info: next_page_info })
|
|
115
|
+
some_function(next_page_response)
|
|
116
|
+
end
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Error Messages
|
|
120
|
+
|
|
121
|
+
You can rescue `ShopifyAPI::Errors::HttpResponseError` and output error messages with `errors.full_messages`
|
|
122
|
+
|
|
123
|
+
See example:
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
fulfillment = ShopifyAPI::Fulfillment.new(session: @session)
|
|
127
|
+
fulfillment.order_id = 2776493818000
|
|
128
|
+
...
|
|
129
|
+
fulfillment.tracking_company = "Jack Black's Pack, Stack and Track"
|
|
130
|
+
fulfillment.save()
|
|
131
|
+
rescue ShopifyAPI::Errors::HttpResponseError => e
|
|
132
|
+
puts fulfillment.errors.full_messages
|
|
133
|
+
# {"base"=>["Line items are already fulfilled"]}
|
|
134
|
+
# If you report this error, please include this id: e712dde0-1270-4258-8cdb-d198792c917e.
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
[Back to guide index](../README.md)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
|
|
2
|
+
# Create a Session Storage Implementation
|
|
3
|
+
|
|
4
|
+
The implementation of session storage that you pass in `ShopifyAPI::Context.setup` is what the Shopify gem will use to store and load sessions. [Shopify::Auth::FileSessionStorage](../../lib/shopify_api/auth/file_session_storage.rb) can be used for testing purposes and as an example of how to make an implementation in your app. This is not recommended for production, we recommend you implement a solution that will store and load serialized sessions from a more ideal store such as a database like MySQL or MongoDB.
|
|
5
|
+
|
|
6
|
+
## Create a New Session Storage Class
|
|
7
|
+
|
|
8
|
+
You can create a session storage class that includes `ShopifyAPI::Auth::SessionStorage` and override the methods as shown in the table and example below:
|
|
9
|
+
|
|
10
|
+
| Method Name | Input Type | Return Type |
|
|
11
|
+
| ---------------------- | --------------------------------- | ------------------------------ |
|
|
12
|
+
| `store_session` | `ShopifyAPI::Auth::Session` | `Boolean` (true if successful) |
|
|
13
|
+
| `load_session` | `String` (session id) | `ShopifyAPI::Auth::Session` |
|
|
14
|
+
| `delete_session` | `String` (session id) | `Boolean` (true if successful) |
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
class CustomSessionStorage
|
|
19
|
+
include ShopifyAPI::Auth::SessionStorage
|
|
20
|
+
|
|
21
|
+
def initialize
|
|
22
|
+
# Initialize as needed
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def store_session(session)
|
|
26
|
+
# Implement a store function
|
|
27
|
+
some_store_function(id: session.id, session_data: session.serialize)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def load_session(id)
|
|
31
|
+
# Implement a fetch function
|
|
32
|
+
session_data = some_fetch_function(id)
|
|
33
|
+
ShopifyAPI::Auth::Session.deserialize(session_data)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def delete_session(id)
|
|
37
|
+
# Implement a delete function
|
|
38
|
+
some_delete_function(id)
|
|
39
|
+
true
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Note:** We recommend utilizing the Session `serialize` and `deserialize` functions to make storing and loading sessions easier.
|
|
45
|
+
|
|
46
|
+
Once this is complete you can pass an instance of this session storage class as `session_storage` in `ShopifyAPI::Context.setup`
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Webhooks
|
|
2
|
+
|
|
3
|
+
The `shopify_api` gem provides webhook functionality to make it easy to both subscribe to and process webhooks. To implement in your app follow the steps outlined below.
|
|
4
|
+
|
|
5
|
+
## Create a Webhook Handler
|
|
6
|
+
|
|
7
|
+
If you want to register for an http webhook you need to implement a webhook handler which the `shopify_api` gem can use to determine how to process your webhook. You can make multiple implementations (one per topic) or you can make one implementation capable of handling all the topics you want to subscribe to. To do this simply make a module or class that includes or extends `ShopifyAPI::Webhooks::WebhookHandler` and implement the handle method which accepts the following named parameters: topic: `String`, shop: `String`, and body: `Hash[String, untyped]`. An example implementation is shown below:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
module WebhookHandler
|
|
11
|
+
include ShopifyAPI::Webhooks::Handler
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
def handle(topic:, shop:, body:)
|
|
15
|
+
puts "Received webhook! topic: #{topic} shop: #{shop} body: #{body}"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Note:** It is recommended that in order to respond quickly to the Shopify webhook request that the handler not do any heavy logic or network calls, rather it should simply enqueue the work in some job queue in order to be executed later.
|
|
22
|
+
|
|
23
|
+
## Add to Webhook Registry
|
|
24
|
+
|
|
25
|
+
The next step is to add all the webhooks you would like to subscribe to for any shop to the webhook registry. To do this you can call `ShopifyAPI::Webhooks::Registry.add_registration` for each webhook you would like to handle. `add_registration` accepts a topic string, a delivery_method symbol (currently supporting `:http`, `:event_bridge`, and `:pub_sub`), a webhook path (the relative path for an http webhook) and a handler. This only needs to be done once when the app is started and we recommend doing this at the same time that you setup `ShopifyAPI::Context`. An example is shown below to register an http webhook:
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
registration = ShopifyAPI::Webhooks::Registry.add_registration(topic: "orders/create", delivery_method: :http, handler: WebhookHandler)
|
|
29
|
+
```
|
|
30
|
+
If you are only interested in particular fields, you can optionally filter the data sent by Shopify by specifying the `fields` parameter. Note that you will still receive a webhook request from Shopify every time the resource is updated, but only the specified fields will be sent:
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
registration = ShopifyAPI::Webhooks::Registry.add_registration(
|
|
34
|
+
topic: "orders/create",
|
|
35
|
+
delivery_method: :http,
|
|
36
|
+
handler: WebhookHandler,
|
|
37
|
+
fields: ["number","note"] # this can also be a single comma separated string
|
|
38
|
+
)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Note**: The webhooks you register with Shopify are saved in the Shopify platform, but the local `ShopifyAPI::Webhooks::Registry` needs to be reloaded whenever your server restarts.
|
|
42
|
+
|
|
43
|
+
### EventBridge and PubSub Webhooks
|
|
44
|
+
|
|
45
|
+
You can also register webhooks for delivery to Amazon EventBridge or Google Cloud
|
|
46
|
+
Pub/Sub. In this case the `path` argument to
|
|
47
|
+
`Shopify.Webhooks.Registry.register` needs to be of a specific form.
|
|
48
|
+
|
|
49
|
+
For EventBridge, the `path` must be the [ARN of the partner event
|
|
50
|
+
source](https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_EventSource.html).
|
|
51
|
+
|
|
52
|
+
For Pub/Sub, the `path` must be of the form
|
|
53
|
+
`pubsub://[PROJECT-ID]:[PUB-SUB-TOPIC-ID]`. For example, if you created a topic
|
|
54
|
+
with id `red` in the project `blue`, then the value of `path` would be
|
|
55
|
+
`pubsub://blue:red`.
|
|
56
|
+
|
|
57
|
+
When registering for an EventBridge or PubSub Webhook you do not need to specify a handler as this is only used for handling Http webhooks.
|
|
58
|
+
|
|
59
|
+
## Register a Webhook for a Shop
|
|
60
|
+
At any point that you have a session for a shop you can register to receive webhooks for that shop. We recommend registering for webhooks immediately after [OAuth](./oauth.md).
|
|
61
|
+
|
|
62
|
+
This can be done in one of two ways:
|
|
63
|
+
|
|
64
|
+
If you would like to register to receive webhooks for all topics you have added to the registry for a specific shop you can simply call:
|
|
65
|
+
```ruby
|
|
66
|
+
ShopifyAPI::Webhooks::Registry.register_all(session: shop_session)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
This will return an Array of `ShopifyAPI::Webhooks::RegisterResult`s that have fields `topic`, `success`, and `body` which can be used to see which webhooks were successfully registered.
|
|
70
|
+
|
|
71
|
+
Or if you would like to register to receive webhooks for specific topics that have been added to the registry for a specific shop you can simply call `register` for any needed topics:
|
|
72
|
+
```ruby
|
|
73
|
+
ShopifyAPI::Webhooks::Registry.register(topic: "<specific-topic>", session: shop_session)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
This will return a single `ShopifyAPI::Webhooks::RegisterResult`.
|
|
77
|
+
|
|
78
|
+
## Unregister a Webhook
|
|
79
|
+
|
|
80
|
+
To unregister a topic from a shop you can simply call:
|
|
81
|
+
```ruby
|
|
82
|
+
ShopifyAPI::Webhooks::Registry.unregister(topic: "orders/create", session: shop_session)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Process a Webhook
|
|
86
|
+
|
|
87
|
+
To process an http webhook, you need to listen on the route(s) you provided during the Webhook registration process, then when the route is hit construct a `ShopifyAPI::Webhooks::Request` and call `ShopifyAPI::Webhooks::Registry.process`. This will verify the request did indeed come from Shopify and then call the specified handler for that webhook. An example in Rails is shown below:
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
class WebhookController < ApplicationController
|
|
91
|
+
def webhook
|
|
92
|
+
ShopifyAPI::Webhooks::Registry.process(
|
|
93
|
+
ShopifyAPI::Webhooks::WebhookRequest.new(raw_body: request.raw_post, headers: request.headers.to_h)
|
|
94
|
+
)
|
|
95
|
+
render json: {success: true}.to_json
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module ShopifyAPI
|
|
5
|
+
module AdminVersions
|
|
6
|
+
SUPPORTED_ADMIN_VERSIONS = T.let([
|
|
7
|
+
"unstable",
|
|
8
|
+
"2022-10",
|
|
9
|
+
"2022-07",
|
|
10
|
+
"2022-04",
|
|
11
|
+
"2022-01",
|
|
12
|
+
], T::Array[String])
|
|
13
|
+
|
|
14
|
+
LATEST_SUPPORTED_ADMIN_VERSION = T.let("2022-10", String)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
SUPPORTED_ADMIN_VERSIONS = ShopifyAPI::AdminVersions::SUPPORTED_ADMIN_VERSIONS
|
|
18
|
+
LATEST_SUPPORTED_ADMIN_VERSION = ShopifyAPI::AdminVersions::LATEST_SUPPORTED_ADMIN_VERSION
|
|
19
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|