Implement Unified Embeds for Stackblitz URLs (#16313)
* building * complete implementation and add specs * handle params "?" * changes * optimization
This commit is contained in:
parent
feebae6ae3
commit
337657e53d
4 changed files with 47 additions and 32 deletions
|
|
@ -1,14 +1,18 @@
|
|||
class StackblitzTag < LiquidTagBase
|
||||
PARTIAL = "liquids/stackblitz".freeze
|
||||
ID_REGEXP = /\A[a-zA-Z0-9\-]{0,60}\z/
|
||||
VIEW_OPTION_REGEXP = /\Aview=(preview|editor|both)\z/
|
||||
FILE_OPTION_REGEXP = /\Afile=(.*)\z/
|
||||
REGISTRY_REGEXP = %r{https://stackblitz.com/edit/(?<id>[\w\-]{,60})(?<params>\?.*)?}
|
||||
ID_REGEXP = /\A(?<id>[\w\-]{,60})\Z/
|
||||
REGEXP_OPTIONS = [REGISTRY_REGEXP, ID_REGEXP].freeze
|
||||
# rubocop:disable Layout/LineLength
|
||||
PARAM_REGEXP = /\A(view=(preview|editor|both))|(file=(.*))|(embed=1)|(hideExplorer=1)|(hideNavigation=1)|(theme=(default|light|dark))|(ctl=1)|(devtoolsheight=\d)\Z/
|
||||
# rubocop:enable Layout/LineLength
|
||||
|
||||
def initialize(_tag_name, id, _parse_context)
|
||||
def initialize(_tag_name, input, _parse_context)
|
||||
super
|
||||
@id = parse_id(id)
|
||||
@view = parse_input(id, method(:valid_view?))
|
||||
@file = parse_input(id, method(:valid_file?))
|
||||
|
||||
stripped_input = strip_tags(input)
|
||||
unescaped_input = CGI.unescape_html(stripped_input)
|
||||
@id, @params = parsed_input(unescaped_input)
|
||||
@height = 500
|
||||
end
|
||||
|
||||
|
|
@ -17,8 +21,7 @@ class StackblitzTag < LiquidTagBase
|
|||
partial: PARTIAL,
|
||||
locals: {
|
||||
id: @id,
|
||||
view: @view,
|
||||
file: @file,
|
||||
params: @params,
|
||||
height: @height
|
||||
},
|
||||
)
|
||||
|
|
@ -26,35 +29,34 @@ class StackblitzTag < LiquidTagBase
|
|||
|
||||
private
|
||||
|
||||
def valid_id?(id)
|
||||
id =~ ID_REGEXP
|
||||
def parsed_input(input)
|
||||
id, *params = input.split
|
||||
match = pattern_match_for(id, REGEXP_OPTIONS)
|
||||
raise StandardError, I18n.t("liquid_tags.stackblitz_tag.invalid_stackblitz_id") unless match
|
||||
|
||||
return [match[:id], nil] unless params_present?(match) || params
|
||||
|
||||
build_link_with_params(match[:id], (params_present?(match) || params))
|
||||
end
|
||||
|
||||
def parse_id(input)
|
||||
input_no_space = input.split.first
|
||||
raise StandardError, I18n.t("liquid_tags.stackblitz_tag.invalid_stackblitz_id") unless valid_id?(input_no_space)
|
||||
def params_present?(match)
|
||||
return unless match.names.include?("params")
|
||||
|
||||
input_no_space
|
||||
match[:params].delete("?")
|
||||
end
|
||||
|
||||
def parse_input(input, validator)
|
||||
inputs = input.split
|
||||
def build_link_with_params(id, params)
|
||||
params = params.split("&") if params.is_a?(String)
|
||||
vetted_params = params.select { |param| valid_param(param) }.join("&")
|
||||
|
||||
# Validation
|
||||
validated_views = inputs.filter_map { |input_option| validator.call(input_option) }
|
||||
raise StandardError, I18n.t("liquid_tags.stackblitz_tag.invalid_options") unless validated_views.length.between?(0,
|
||||
1)
|
||||
|
||||
validated_views.length.zero? ? "" : validated_views.join.to_s
|
||||
[id, vetted_params]
|
||||
end
|
||||
|
||||
def valid_view?(option)
|
||||
option.match(VIEW_OPTION_REGEXP)
|
||||
end
|
||||
|
||||
def valid_file?(option)
|
||||
option.match(FILE_OPTION_REGEXP)
|
||||
def valid_param(param)
|
||||
(param =~ PARAM_REGEXP)&.zero?
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag("stackblitz", StackblitzTag)
|
||||
|
||||
UnifiedEmbed.register(StackblitzTag, regexp: StackblitzTag::REGISTRY_REGEXP)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<iframe
|
||||
src="https://stackblitz.com/edit/<%= id %>?embed=1&<%= view %>&<%= file %>"
|
||||
src="https://stackblitz.com/edit/<%= id %>?<%= params %>"
|
||||
width="100%"
|
||||
height="<%= height %>"
|
||||
scrolling="no"
|
||||
|
|
|
|||
|
|
@ -47,9 +47,9 @@ RSpec.describe StackblitzTag, type: :liquid_tag do
|
|||
end.not_to raise_error
|
||||
end
|
||||
|
||||
it "parses stackblitz if with a view and file parameter" do
|
||||
it "parses stackblitz id with a view and file parameter" do
|
||||
liquid = generate_new_liquid(stackblitz_id_with_view_and_file)
|
||||
expect(liquid.render).to include("https://stackblitz.com/edit/ball-demo?embed=1&view=preview&file=style.css")
|
||||
expect(liquid.render).to include("https://stackblitz.com/edit/ball-demo?view=preview&file=style.css")
|
||||
end
|
||||
|
||||
it "rejects XSS attempts" do
|
||||
|
|
|
|||
|
|
@ -56,6 +56,12 @@ RSpec.describe UnifiedEmbed::Registry do
|
|||
"https://open.spotify.com/show/3sRrtlRiByFrOC49vPwP8L?si=c3c6c1180afe4094",
|
||||
]
|
||||
|
||||
valid_stackblitz_url_formats = [
|
||||
"https://stackblitz.com/edit/web-platform-3tqbd4",
|
||||
"https://stackblitz.com/edit/web-platform-3tqbd4?embed=1&file=index.html&hideExplorer=1&hideNavigation=1&theme=dark",
|
||||
"https://stackblitz.com/edit/web-platform-3tqbd4?embed=1&file=index.html&theme=light",
|
||||
]
|
||||
|
||||
valid_twitch_url_formats = [
|
||||
"https://clips.twitch.tv/embed?clip=SpeedyVivaciousDolphinKappaRoss-IQl5YslMAGKbMOGM&parent=www.example.com",
|
||||
"https://player.twitch.tv/?video=1222841752&parent=www.example.com",
|
||||
|
|
@ -195,6 +201,13 @@ RSpec.describe UnifiedEmbed::Registry do
|
|||
end
|
||||
end
|
||||
|
||||
it "returns StackblitzTag for a valid stackblitz url" do
|
||||
valid_stackblitz_url_formats.each do |url|
|
||||
expect(described_class.find_liquid_tag_for(link: url))
|
||||
.to eq(StackblitzTag)
|
||||
end
|
||||
end
|
||||
|
||||
it "returns StackeryTag for a stackery url" do
|
||||
expect(described_class.find_liquid_tag_for(link: "https://app.stackery.io/editor/design?owner=stackery&repo=quickstart-ruby&file=template.yaml"))
|
||||
.to eq(StackeryTag)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue