Skip to content
This repository has been archived by the owner on Jul 27, 2024. It is now read-only.

Refresh theme-liquid-docs on theme-check startup #671

Merged
merged 5 commits into from
Dec 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/theme_check/language_server/handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ def on_initialized(_id, _params)

@configuration.fetch
@configuration.register_did_change_capability

ShopifyLiquid::SourceManager.download_or_refresh_files
end

def on_shutdown(id, _params)
Expand Down
20 changes: 13 additions & 7 deletions lib/theme_check/remote_asset_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,7 @@ def content
return if @uri.nil?
return @content unless @content.nil?

res = Net::HTTP.start(@uri.hostname, @uri.port, use_ssl: @uri.scheme == 'https') do |http|
req = Net::HTTP::Get.new(@uri)
req['Accept-Encoding'] = 'gzip, deflate, br'
http.request(req)
end

@content = res.body
@content = request(@uri)

rescue OpenSSL::SSL::SSLError, Zlib::StreamError, *NET_HTTP_EXCEPTIONS
@contents = ''
Expand All @@ -47,5 +41,17 @@ def gzipped_size
return if @uri.nil?
@gzipped_size ||= content.bytesize
end

private

def request(uri)
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
req = Net::HTTP::Get.new(uri)
req['Accept-Encoding'] = 'gzip, deflate, br'
http.request(req)
end

res.body
end
end
end
13 changes: 13 additions & 0 deletions lib/theme_check/shopify_liquid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@
require_relative 'shopify_liquid/documentation'
require_relative 'shopify_liquid/filter'
require_relative 'shopify_liquid/object'
require_relative 'shopify_liquid/source_manager'
require_relative 'shopify_liquid/source_index'
require_relative 'shopify_liquid/system_translations'
require_relative 'shopify_liquid/tag'

require_relative 'shopify_liquid/source_index/base_entry'
require_relative 'shopify_liquid/source_index/filter_entry'
require_relative 'shopify_liquid/source_index/object_entry'
require_relative 'shopify_liquid/source_index/parameter_entry'
require_relative 'shopify_liquid/source_index/property_entry'
require_relative 'shopify_liquid/source_index/return_type_entry'
require_relative 'shopify_liquid/source_index/tag_entry'
require_relative 'shopify_liquid/source_index/base_state'
require_relative 'shopify_liquid/source_index/filter_state'
require_relative 'shopify_liquid/source_index/object_state'
require_relative 'shopify_liquid/source_index/tag_state'
44 changes: 23 additions & 21 deletions lib/theme_check/shopify_liquid/source_index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,50 +3,52 @@
require 'json'
require 'pathname'

require_relative 'source_index/base_entry'
require_relative 'source_index/filter_entry'
require_relative 'source_index/object_entry'
require_relative 'source_index/parameter_entry'
require_relative 'source_index/property_entry'
require_relative 'source_index/return_type_entry'
require_relative 'source_index/tag_entry'

require_relative './source_manager'

module ThemeCheck
module ShopifyLiquid
class SourceIndex
class << self
def filters
@filters ||= load_file(:filters)
.map { |hash| SourceIndex::FilterEntry.new(hash) }
@filters = nil if FilterState.outdated?

@filters ||= FilterState.mark_up_to_date &&
load_file(:filters)
.map { |hash| FilterEntry.new(hash) }
end

def objects
@objects ||= load_file(:objects)
.concat(built_in_objects)
.map { |hash| SourceIndex::ObjectEntry.new(hash) }
@objects = nil if ObjectState.outdated?

@objects ||= ObjectState.mark_up_to_date &&
load_file(:objects)
.concat(built_in_objects)
.map { |hash| ObjectEntry.new(hash) }
end

def tags
@tags ||= load_file(:tags)
.map { |hash| SourceIndex::TagEntry.new(hash) }
@tags = nil if TagState.outdated?

@tags ||= TagState.mark_up_to_date &&
load_file(:tags)
.map { |hash| TagEntry.new(hash) }
end

private

def load_file(file_name)
read_json(SourceManager.local_path(file_name))
read_json(local_path!(file_name))
end

def read_json(path)
SourceManager.download_or_refresh_files
def local_path!(file_name)
SourceManager.download unless SourceManager.has_required_files?
SourceManager.local_path(file_name)
end

def read_json(path)
JSON.parse(path.read)
end

def built_in_objects
load_file("../built_in_liquid_objects")
load_file('../built_in_liquid_objects')
end
end
end
Expand Down
23 changes: 23 additions & 0 deletions lib/theme_check/shopify_liquid/source_index/base_state.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

module ThemeCheck
module ShopifyLiquid
class SourceIndex
class BaseState
class << self
def mark_outdated
@up_to_date = false
end

def mark_up_to_date
@up_to_date = true
end

def outdated?
@up_to_date == false
end
end
end
end
end
end
11 changes: 11 additions & 0 deletions lib/theme_check/shopify_liquid/source_index/filter_state.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

module ThemeCheck
module ShopifyLiquid
class SourceIndex
class FilterState < BaseState
@up_to_date = false
end
end
end
end
11 changes: 11 additions & 0 deletions lib/theme_check/shopify_liquid/source_index/object_state.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

module ThemeCheck
module ShopifyLiquid
class SourceIndex
class ObjectState < BaseState
@up_to_date = false
end
end
end
end
11 changes: 11 additions & 0 deletions lib/theme_check/shopify_liquid/source_index/tag_state.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

module ThemeCheck
module ShopifyLiquid
class SourceIndex
class TagState < BaseState
@up_to_date = false
end
end
end
end
135 changes: 82 additions & 53 deletions lib/theme_check/shopify_liquid/source_manager.rb
Original file line number Diff line number Diff line change
@@ -1,81 +1,110 @@
# frozen_string_literal: true

require 'net/http'
require 'pathname'
require 'tmpdir'

module ThemeCheck
module ShopifyLiquid
module SourceManager
extend self

def download_or_refresh_files
if has_required_files?
# TODO: https://github.com/Shopify/theme-check/issues/651
#
# Refresh files if they exist locally
else
download
class SourceManager
REQUIRED_FILE_NAMES = [:filters, :objects, :tags, :latest].freeze

class << self
def download_or_refresh_files(destination = default_destination)
if has_required_files?(destination)
refresh(destination)
else
download(destination)
end
end
end

def download
create_destination unless destination_exist?
def download(destination = default_destination)
Dir.mkdir(destination) unless destination.exist?

required_file_names.each do |file_name|
download_file(local_path(file_name), remote_path(file_name))
REQUIRED_FILE_NAMES.each do |file_name|
download_file(local_path(file_name, destination), remote_path(file_name))
end
end
end

def local_path(file_name)
documentation_directory + "#{file_name}.json"
end
def refresh(destination = default_destination)
refresh_threads << Thread.new { refresh_thread(destination) }
end

private
def local_path(file_name, destination = default_destination)
destination + "#{file_name}.json"
end

DOCUMENTATION_FETCH_URL = "https://github.com/Shopify/theme-liquid-docs/raw/main/data"
DOCUMENTATION_DIRECTORY = Pathname.new("#{__dir__}/../../../data/shopify_liquid/documentation")
REQUIRED_FILE_NAMES = [:filters, :objects, :tags].freeze
def has_required_files?(destination = default_destination)
REQUIRED_FILE_NAMES.all? { |file_name| local_path(file_name, destination).exist? }
end

def remote_path(file_name)
"#{documentation_fetch_url}/#{file_name}.json"
end
def wait_downloads
refresh_threads.each(&:join)
end

def download_file(local, remote)
File.open(local, "wb") do |file|
content = open_uri(remote)
private

file.write(content)
def refresh_thread(destination)
return unless refresh_needed?(destination)

Dir.mktmpdir do |tmp_dir|
download(Pathname.new(tmp_dir))

FileUtils.cp_r("#{tmp_dir}/.", destination)

mark_all_indexes_outdated
end
end
end

def open_uri(remote)
# require at usage point to not slow down theme-check on startup
require 'open-uri'
def refresh_needed?(destination)
local_latest_content = local_path(:latest, destination).read
remote_latest_content = open_uri(remote_path(:latest))

URI.parse(remote).open.read
end
revision(local_latest_content) != revision(remote_latest_content)
end

def destination_exist?
documentation_directory.exist?
end
def revision(json_content)
Poitrin marked this conversation as resolved.
Show resolved Hide resolved
# Raise an error if revision isn't found to avoid returning nil
JSON.parse(json_content).fetch('revision')
end

def create_destination
Dir.mkdir(documentation_directory)
end
def remote_path(file_name)
"https://raw.githubusercontent.com/Shopify/theme-liquid-docs/main/data/#{file_name}.json"
end

def documentation_directory
DOCUMENTATION_DIRECTORY
end
def download_file(local_path, remote_uri)
File.open(local_path, "wb") do |file|
content = open_uri(remote_uri)
file.write(content)
end
end

def documentation_fetch_url
DOCUMENTATION_FETCH_URL
end
def mark_all_indexes_outdated
SourceIndex::FilterState.mark_outdated
SourceIndex::ObjectState.mark_outdated
SourceIndex::TagState.mark_outdated
end

def required_file_names
REQUIRED_FILE_NAMES
end
# State

def has_required_files?
required_file_names.all? { |file_name| local_path(file_name).exist? }
def default_destination
@default_destination ||= Pathname.new("#{__dir__}/../../../data/shopify_liquid/documentation")
end

def refresh_threads
@refresh_threads ||= []
end

def open_uri(uri_str)
uri = URI.parse(uri_str)

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
req = Net::HTTP::Get.new(uri)
http.request(req)
end

res.body
end
end
end
end
Expand Down
1 change: 1 addition & 0 deletions test/data/shopify_liquid/built_in_liquid_objects.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
1 change: 1 addition & 0 deletions test/data/shopify_liquid/documentation/latest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "revision": "59abb1dcfe7ca9f9560356353e3d23b46489a330" }
14 changes: 8 additions & 6 deletions test/remote_asset_file_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

module ThemeCheck
class RemoteAssetFileTest < Minitest::Test
FakeResponse = Struct.new(:body)

def setup
@src = 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js'
@asset = RemoteAssetFile.from_src(@src)
Expand All @@ -16,10 +14,8 @@ def test_instance_caching
end

def test_network_request
Net::HTTP.any_instance
.expects(:request)
.with { |req| req['Accept-Encoding'] == 'gzip, deflate, br' }
.returns(FakeResponse.new("..."))
@asset.expects(:request).with(uri).returns("...")

assert_equal("...", @asset.content)
assert_equal(3, @asset.gzipped_size)
end
Expand All @@ -35,5 +31,11 @@ def test_handles_eaddr_not_avail_errors
assert(asset.gzipped_size == 0)
assert(asset.content.empty?)
end

private

def uri
RemoteAssetFile.uri(@src)
end
end
end
Loading