Skip to content

Commit

Permalink
Add docs
Browse files Browse the repository at this point in the history
  • Loading branch information
lizkenyon committed Mar 10, 2025
1 parent 79ff8ec commit bb215e5
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 13 deletions.
50 changes: 50 additions & 0 deletions docs/shopify_app/script-tags.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# ScriptTags

ShopifyApp can manage your app's [ScriptTags](https://shopify.dev/docs/admin-api/graphql/reference/online-store/scripttag) for you by setting which scripttags you require in the initializer.

## Configuration

```ruby
ShopifyApp.configure do |config|
config.scripttags = [
# Basic script tag
{cache: true, src: 'https://example.com/fancy.js'},

# Script tag with template_types for app block detection
{
cache: true,
src: 'https://example.com/product-script.js',
template_types: ['product', 'collection']
}
]
end
```

## Required Scopes
Both the `write_script_tags` and `read_themes` scopes are required.

For apps created with the Shopify CLI, set these scopes in your `shopify.app.toml` file:

```toml
[access_scopes]
# Learn more at https://shopify.dev/docs/apps/tools/cli/configuration#access_scopes
scopes = "write_products,write_script_tags,read_themes"
```

For older apps, you can set the scopes in the initializer:

```ruby
config.scope = 'write_products,write_script_tags,read_themes'
```

## How It Works

### Script Tag Creation

Scripttags are created in the same way as [Webhooks](/docs/shopify_app/webhooks.md), with a background job which will create the required scripttags.

### App Block Detection

When you specify `template_types` for a script tag, ShopifyApp will check if the store's active theme supports app blocks for those template types. If any template type doesn't support app blocks, the script tags will be created as a fallback

This allows your app to automatically adapt to the store's theme capabilities, using app blocks when available and falling back to script tags when necessary.
27 changes: 23 additions & 4 deletions lib/shopify_app/managers/scripttags_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,21 @@ def create_scripttags(session:)
template_types_to_check = required_scripttags.flat_map { |tag| tag[:template_types] }.compact.uniq

# If template types are specified, check if the theme supports app blocks for those templates
if template_types_to_check.any? && theme_supports_app_blocks?(template_types_to_check)
ShopifyApp::Logger.info("Theme supports app blocks for templates: #{template_types_to_check.join(", ")}. Skipping script tag creation.")
return
if template_types_to_check.any?
# First fetch the active theme
active_theme = fetch_active_theme

# If we failed to fetch the active theme, don't proceed with script tag creation
unless active_theme
ShopifyApp::Logger.info("Failed to fetch active theme. Skipping script tag creation.")
return
end

# Check if the theme supports app blocks for the specified templates
if template_types_to_check.all? { |template_type| template_supports_app_blocks?(active_theme["id"], template_type) }
ShopifyApp::Logger.info("Theme supports app blocks for templates: #{template_types_to_check.join(", ")}. Skipping script tag creation.")
return
end
end

expanded_scripttags.each do |scripttag|
Expand Down Expand Up @@ -75,7 +87,7 @@ def theme_supports_app_blocks?(template_types)
template_supports_app_blocks?(active_theme["id"], template_type)
end
rescue => e
ShopifyApp::Logger.warn("Unable to check theme app block support: #{e.message}. Continuing with script tag creation.")
ShopifyApp::Logger.warn("Unable to check theme app block support: #{e.message}.")
false
end
end
Expand Down Expand Up @@ -103,7 +115,14 @@ def fetch_active_theme
raise "GraphQL error: #{error_message}"
end

# Check if the response has the expected structure
unless response.body["data"] && response.body["data"]["themes"] && response.body["data"]["themes"]["nodes"]
raise "Invalid response structure"
end

themes = response.body["data"]["themes"]["nodes"]
return nil if themes.empty?

themes.first
rescue => e
ShopifyApp::Logger.warn("Failed to fetch active theme: #{e.message}")
Expand Down
67 changes: 58 additions & 9 deletions test/shopify_app/managers/scripttags_manager_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ class ShopifyApp::ScripttagsManagerTest < ActiveSupport::TestCase
{ cache: true, src: "https://example-app.com/foobar.js", template_types: ["index"] },
]

ShopifyAPI::Context.load_rest_resources(api_version: ShopifyApp.configuration.api_version)
@session = ShopifyAPI::Auth::Session.new(shop: "some-shop.myshopify.com")
ShopifyAPI::Context.activate_session(@session)
@manager = ShopifyApp::ScripttagsManager.new(@scripttags, "example-app.com")
Expand Down Expand Up @@ -460,7 +459,7 @@ class ShopifyApp::ScripttagsManagerTest < ActiveSupport::TestCase
manager.create_scripttags(session: @session)
end

test "#create_scripttags creates scripttags when theme API access fails" do
test "#create_scripttags_skips_creation_when_theme_API_access_fails" do
# Create manager with scripttags that have template_types
manager = ShopifyApp::ScripttagsManager.new(@scripttags_with_template_types, "example-app.com")
manager.stubs(:graphql_client).returns(@mock_client)
Expand All @@ -483,17 +482,67 @@ class ShopifyApp::ScripttagsManagerTest < ActiveSupport::TestCase
"data" => { "themes" => nil },
})

# Mock empty scripttags response
mock_empty_scripttags_response

# Set up the sequence of responses for the GraphQL client
# Set up the response for the GraphQL client
@mock_client.expects(:query).returns(error_response).once

# Expect scripttag creation calls
expect_scripttag_creation("https://example-app.com/fancy.js")
expect_scripttag_creation("https://example-app.com/foobar.js")
# No scripttag creation should be attempted
@mock_client.expects(:query).with(has_entry(variables: has_key(:input))).never

# Allow logging without capturing
ShopifyApp::Logger.stubs(:info)
ShopifyApp::Logger.stubs(:warn)

manager.create_scripttags(session: @session)
end

test "#create_scripttags skips creation when active theme is empty" do
# Create manager with scripttags that have template_types
manager = ShopifyApp::ScripttagsManager.new(@scripttags_with_template_types, "example-app.com")
manager.stubs(:graphql_client).returns(@mock_client)

# Mock theme response with empty nodes array
empty_theme_response = mock
empty_theme_response.stubs(:body).returns({
"data" => {
"themes" => {
"nodes" => [],
},
},
})

# Set up the response for the GraphQL client
@mock_client.expects(:query).returns(empty_theme_response).once

# No scripttag creation should be attempted
@mock_client.expects(:query).with(has_entry(variables: has_key(:input))).never

# Allow logging without capturing
ShopifyApp::Logger.stubs(:info)

manager.create_scripttags(session: @session)
end

test "#create_scripttags skips creation when theme response has invalid structure" do
# Create manager with scripttags that have template_types
manager = ShopifyApp::ScripttagsManager.new(@scripttags_with_template_types, "example-app.com")
manager.stubs(:graphql_client).returns(@mock_client)

# Mock theme response with invalid structure
invalid_response = mock
invalid_response.stubs(:body).returns({
"data" => {
# Missing "themes" key
},
})

# Set up the response for the GraphQL client
@mock_client.expects(:query).returns(invalid_response).once

# No scripttag creation should be attempted
@mock_client.expects(:query).with(has_entry(variables: has_key(:input))).never

# Allow logging without capturing
ShopifyApp::Logger.stubs(:info)
ShopifyApp::Logger.stubs(:warn)

manager.create_scripttags(session: @session)
Expand Down

0 comments on commit bb215e5

Please sign in to comment.