zendesk_apps_tools 2.3.0 → 2.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6f6aebc0331def0f61f777fcfee6c885ca518751
4
- data.tar.gz: 8b5eb53908cb059769583cd42da31fe8758d5c62
3
+ metadata.gz: c9671e253af31f401823ee8fbe0ca6b9aed32b1c
4
+ data.tar.gz: ba2aa8edc896d0d1f3d22e5da360ea2e569dd94d
5
5
  SHA512:
6
- metadata.gz: cc89513fb13b00cee92a7362f3909f5e21a4da8260cd5347224d1490a473d988485bddfa0276c5503a74e84d10ae5473a2515c2b53f9996782c460e171cb175a
7
- data.tar.gz: 9fe853d0f697005c47bdbe8bf4d74ec0988dbd4da5b90216a2230509ab9126d685a7d920a013c99055cf22c18db939d9ceb941cc215a037ea1200176e4370f7b
6
+ metadata.gz: 869b048a92f9721b689030c45ec5f5b355cb7640a8d16c738d28664c600a6c52e039008bf26e11db2247af3879e0b5729e41173e09642f903861fcb88d58ba36
7
+ data.tar.gz: 988d22c6814670f837304c52365a7a07f67fd08d536ad5d2f57ce306a94fd8abb67ff708d6041085ba5afbec57223d4b936e47835cbad088a12cf68d4fb8f4e6
@@ -16,7 +16,7 @@ module ZendeskAppsTools
16
16
  require 'faraday'
17
17
  prepare_api_auth
18
18
  Faraday.new full_url do |f|
19
- f.request encoding
19
+ f.request encoding if encoding
20
20
  f.adapter :net_http
21
21
  f.basic_auth @username, @password
22
22
  end
@@ -10,13 +10,17 @@ module ZendeskAppsTools
10
10
  include Thor::Actions
11
11
  include ZendeskAppsTools::CommandHelpers
12
12
 
13
- map %w(-v) => :version
13
+ map %w[-v] => :version
14
+ DEFAULT_SERVER_PORT = '4567'
14
15
 
15
16
  source_root File.expand_path(File.join(File.dirname(__FILE__), '../..'))
16
17
 
17
18
  desc 'translate SUBCOMMAND', 'Manage translation files', hide: true
18
19
  subcommand 'translate', Translate
19
20
 
21
+ desc 'theme SUBCOMMAND', 'Development tools for Theming Center (Beta)', hide: false
22
+ subcommand 'theme', Theme
23
+
20
24
  desc 'bump SUBCOMMAND', 'Bump version for app', hide: true
21
25
  subcommand 'bump', Bump
22
26
 
@@ -125,13 +129,12 @@ module ZendeskAppsTools
125
129
 
126
130
  DEFAULT_SERVER_PATH = './'
127
131
  DEFAULT_CONFIG_PATH = './settings.yml'
128
- DEFAULT_SERVER_PORT = '4567'
129
132
  DEFAULT_APP_ID = 0
130
133
 
131
134
  desc 'server', 'Run a http server to serve the local app'
132
135
  shared_options(except: [:clean])
133
136
  method_option :config, default: DEFAULT_CONFIG_PATH, required: false, aliases: '-c'
134
- method_option :port, default: DEFAULT_SERVER_PORT, required: false
137
+ method_option :port, default: DEFAULT_SERVER_PORT, required: false, desc: 'Port for the http server to use.'
135
138
  method_option :app_id, default: DEFAULT_APP_ID, required: false, type: :numeric
136
139
  method_option :bind, required: false
137
140
  def server
@@ -206,10 +209,6 @@ module ZendeskAppsTools
206
209
  manifest.location_options.collect{ |option| option.location.product_code }
207
210
  end
208
211
 
209
- def setup_path(path)
210
- @destination_stack << relative_to_original_destination_root(path) unless @destination_stack.last == path
211
- end
212
-
213
212
  def settings
214
213
  settings_helper.get_settings_from_file(options[:config], manifest.original_parameters) ||
215
214
  settings_helper.get_settings_from_user_input(manifest.original_parameters)
@@ -25,5 +25,9 @@ module ZendeskAppsTools
25
25
  Cache.new(options)
26
26
  end
27
27
  end
28
+
29
+ def setup_path(path)
30
+ @destination_stack << relative_to_original_destination_root(path) unless @destination_stack.last == path
31
+ end
28
32
  end
29
33
  end
@@ -5,6 +5,7 @@ require 'zendesk_apps_support/package'
5
5
 
6
6
  module ZendeskAppsTools
7
7
  class Server < Sinatra::Base
8
+ set :server, :thin
8
9
  set :protection, except: :frame_options
9
10
  ZENDESK_DOMAINS_REGEX = %r{^http(?:s)?://[a-z0-9-]+\.(?:zendesk|zopim|zd-(?:dev|master|staging))\.com$}
10
11
 
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zendesk_apps_tools/theming/common'
4
+
5
+ module ZendeskAppsTools
6
+ class Theme < Thor
7
+ include Thor::Actions
8
+ include ZendeskAppsTools::CommandHelpers
9
+ include ZendeskAppsTools::Theming::Common
10
+
11
+ desc 'preview', 'Preview a theme in development'
12
+ shared_options(except: %i[clean unattended])
13
+ method_option :port, default: Command::DEFAULT_SERVER_PORT, required: false, desc: 'Port for the http server to use.'
14
+ method_option :bind, required: false
15
+ method_option :role,
16
+ type: :string,
17
+ enum: %w[manager agent end_user anonymous],
18
+ default: 'manager',
19
+ desc: 'The role for the preview URL',
20
+ aliases: '-r'
21
+ method_option :livereload, type: :boolean, default: true, desc: 'Enable or disable live-reloading the preview when a change is made.'
22
+ def preview
23
+ setup_path(options[:path])
24
+ ensure_manifest!
25
+ require 'faraday'
26
+ full_upload
27
+ callbacks_after_upload = []
28
+ start_listener(callbacks_after_upload)
29
+ start_server(callbacks_after_upload)
30
+ end
31
+
32
+ no_commands do
33
+ def full_upload
34
+ say_status 'Generating', 'Generating theme from local files'
35
+ payload = generate_payload.merge(role: options[:role])
36
+ say_status 'Generating', 'OK'
37
+ say_status 'Uploading', 'Uploading theme'
38
+ connection = get_connection(nil)
39
+ connection.use Faraday::Response::RaiseError
40
+ connection.put do |req|
41
+ req.url '/hc/api/internal/theming/local_preview'
42
+ req.body = JSON.dump(payload)
43
+ req.headers['Content-Type'] = 'application/json'
44
+ end
45
+ say_status 'Uploading', 'OK'
46
+ say_status 'Ready', "#{connection.url_prefix}hc/admin/local_preview/start"
47
+ say "You can exit preview mode in the UI or by visiting #{connection.url_prefix}hc/admin/local_preview/stop"
48
+ rescue Faraday::Error::ClientError => e
49
+ say_status 'Uploading', "Failed: #{e.message}", :red
50
+ begin
51
+ error_hash = JSON.parse(e.response[:body])
52
+ broken_templates = error_hash['template_errors']
53
+ if broken_templates
54
+ broken_templates.each do |template_name, errors|
55
+ errors.each do |error|
56
+ say_status 'Error', "#{template_name} L#{error['line']}:#{error['column']}: #{error['description']}", :red
57
+ end
58
+ end
59
+ else
60
+ say_status 'Error', error_hash
61
+ end
62
+ rescue JSON::ParserError
63
+ say_error_and_exit 'Server error 😭'
64
+ end
65
+ end
66
+
67
+ def start_listener(callbacks_after_upload)
68
+ # TODO: do we need to stop the listener at some point?
69
+ require 'listen'
70
+ path = Pathname.new(theme_package_path('.')).cleanpath
71
+ listener = ::Listen.to(path, ignore: /\.zat/) do |modified, added, removed|
72
+ need_upload = false
73
+ if modified.any? { |file| file[/templates|manifest/] }
74
+ need_upload = true
75
+ end
76
+ if added.any? || removed.any?
77
+ need_upload = true
78
+ end
79
+ if need_upload
80
+ full_upload
81
+ end
82
+ callbacks_after_upload.each do |callback|
83
+ callback.call
84
+ end
85
+ end
86
+ listener.start
87
+ end
88
+
89
+ def generate_payload
90
+ payload = {}
91
+ templates = Dir.glob(theme_package_path('templates', '*.hbs'))
92
+ templates_payload = {}
93
+ templates.each do |template|
94
+ templates_payload[File.basename(template, '.hbs')] = File.read(template)
95
+ end
96
+ payload['templates'] = templates_payload
97
+ payload['templates']['document_head'] = inject_external_tags(payload['templates']['document_head'])
98
+ payload['templates']['css'] = ''
99
+ payload['templates']['js'] = ''
100
+ payload['templates']['assets'] = assets
101
+ payload['templates']['variables'] = settings_hash
102
+ payload
103
+ end
104
+
105
+ def inject_external_tags(head_template)
106
+ live_reload_script_tag = <<-html
107
+ <script type="text/javascript">
108
+ RACK_LIVERELOAD_PORT = 4567;
109
+ </script>
110
+ <script src="http://localhost:4567/__rack/livereload.js?host=localhost"></script>
111
+ html
112
+
113
+ js_tag = <<-html
114
+ <script src="http://localhost:4567/guide/script.js"></script>
115
+ html
116
+
117
+ css_tag = <<-html
118
+ <link rel="stylesheet" type="text/css" href="http://localhost:4567/guide/style.css">
119
+ html
120
+
121
+ template = StringIO.new
122
+ template << css_tag
123
+ template << head_template
124
+ template << js_tag
125
+ template << live_reload_script_tag
126
+ template.string
127
+ end
128
+
129
+ alias_method :ensure_manifest!, :manifest
130
+
131
+ def start_server(callbacks_after_upload)
132
+ require 'zendesk_apps_tools/theming/server'
133
+ if options[:livereload]
134
+ require 'rack-livereload'
135
+ require 'faye/websocket'
136
+ Faye::WebSocket.load_adapter('thin')
137
+ end
138
+
139
+ ZendeskAppsTools::Theming::Server.tap do |server|
140
+ server.set :bind, options[:bind] if options[:bind]
141
+ server.set :port, options[:port]
142
+ server.set :root, app_dir
143
+ server.set :public_folder, app_dir
144
+ server.set :livereload, options[:livereload]
145
+ server.set :callbacks_after_load, callbacks_after_upload
146
+ server.set :callback_map, {}
147
+ server.use Rack::LiveReload, live_reload_port: 4567 if options[:livereload]
148
+ server.run!
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,64 @@
1
+ module ZendeskAppsTools
2
+ module Theming
3
+ module Common
4
+ def theme_package_path(*file)
5
+ File.expand_path(File.join(app_dir, *file))
6
+ end
7
+
8
+ def url_for(package_file)
9
+ relative_path = relative_path_for(package_file)
10
+ path_parts = recursive_pathname_split(relative_path)
11
+ path_parts.shift
12
+ "http://localhost:4567/guide/#{path_parts.join('/')}"
13
+ end
14
+
15
+ def relative_path_for(filename)
16
+ Pathname.new(filename).relative_path_from(Pathname.new(File.expand_path(app_dir))).cleanpath
17
+ end
18
+
19
+ def assets
20
+ assets = Dir.glob(theme_package_path('assets', '*'))
21
+ assets.each_with_object({}) do |asset, asset_payload|
22
+ asset_payload[File.basename(asset)] = url_for(asset)
23
+ end
24
+ end
25
+
26
+ def assets_hash
27
+ assets.each_with_object({}) do |(k,v), h|
28
+ parametrized = k.gsub(/[^a-z0-9\-_]+/, '-')
29
+ h["assets-#{parametrized}"] = v
30
+ end
31
+ end
32
+
33
+ def manifest
34
+ full_manifest_path = theme_package_path('manifest.json')
35
+ JSON.parse(File.read(full_manifest_path))
36
+ rescue Errno::ENOENT
37
+ say_error_and_exit "There's no manifest file in #{full_manifest_path}"
38
+ rescue JSON::ParserError
39
+ say_error_and_exit "The manifest file is invalid at #{full_manifest_path}"
40
+ end
41
+
42
+ def settings_hash
43
+ manifest['settings'].flat_map { |setting_group| setting_group['variables'] }.each_with_object({}) do |variable, result|
44
+ result[variable.fetch('identifier')] = value_for_setting(variable)
45
+ end
46
+ end
47
+
48
+ def value_for_setting(variable)
49
+ return variable.fetch('value') unless variable.fetch('type') == 'file'
50
+
51
+ files = Dir.glob(theme_package_path('settings', '*.*'))
52
+ file = files.find { |f| File.basename(f, '.*') == variable.fetch('identifier') }
53
+ url_for(file)
54
+ end
55
+
56
+ def recursive_pathname_split(relative_path)
57
+ split_path = relative_path.split
58
+ joined_directories = split_path[0]
59
+ return split_path if split_path[0] == joined_directories.split[0]
60
+ [*recursive_pathname_split(joined_directories), split_path[1]]
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sinatra/base'
4
+ require 'zendesk_apps_tools/theming/common'
5
+
6
+ module ZendeskAppsTools
7
+ module Theming
8
+ class Server < Sinatra::Base
9
+ include Common
10
+
11
+ get '/livereload' do
12
+ if settings.livereload && Faye::WebSocket.websocket?(env)
13
+ ws = Faye::WebSocket.new(env)
14
+
15
+ new_callback = ->() { ws.send(JSON.dump(command: 'reload', path: '')) }
16
+ settings.callback_map[ws] = new_callback
17
+ settings.callbacks_after_load.push(new_callback)
18
+
19
+ ws.onmessage = lambda do |event|
20
+ message = JSON.parse(event.data)
21
+ if message['command'] == 'hello'
22
+ ws.send(JSON.dump(
23
+ command: 'hello',
24
+ protocols: [
25
+ 'http://livereload.com/protocols/official-7',
26
+ 'http://livereload.com/protocols/official-8',
27
+ 'http://livereload.com/protocols/official-9',
28
+ 'http://livereload.com/protocols/2.x-origin-version-negotiation',
29
+ 'http://livereload.com/protocols/2.x-remote-control'
30
+ ],
31
+ serverName: 'ZAT LiveReload 2'
32
+ ))
33
+ end
34
+ end
35
+
36
+ ws.onclose = lambda do |event|
37
+ settings.callbacks_after_load.delete_if do |entry|
38
+ entry == settings.callback_map[ws]
39
+ end
40
+ settings.callback_map.delete ws
41
+ ws = nil
42
+ end
43
+
44
+ # Return async Rack response
45
+ ws.rack_response
46
+ else
47
+ [500, {}, 'Websocket Server Error']
48
+ end
49
+ end
50
+
51
+ get '/guide/style.css' do
52
+ content_type 'text/css'
53
+ style_css = theme_package_path('style.css')
54
+ raise Sinatra::NotFound unless File.exist?(style_css)
55
+ zass_source = File.read(style_css)
56
+ require 'zendesk_apps_tools/theming/zass_formatter'
57
+ response = ZassFormatter.format(zass_source, settings_hash.merge(assets_hash))
58
+ response
59
+ end
60
+
61
+ get '/guide/*' do
62
+ path = File.join(app_dir, *params[:splat])
63
+ return send_file path if File.exist?(path)
64
+ raise Sinatra::NotFound
65
+ end
66
+
67
+ def app_dir
68
+ settings.root
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sass'
4
+ require 'English'
5
+
6
+ module ZendeskAppsTools
7
+ module Theming
8
+ class ZassFormatter
9
+ COMMAND = /(?<command>lighten|darken)/i
10
+ COLOR = /(?<color>#\h{6}|#\h{3})/
11
+ PERCENTAGE = /(?<percentage>\d{1,3})%/
12
+ COLOR_COMMAND_REGEXP = /#{COMMAND}\s*\(\s*#{COLOR}\s*,\s*#{PERCENTAGE}\s*\)/
13
+
14
+ def self.format(raw, variables)
15
+ joined_keys = "(#{variables.keys.join('|')})"
16
+
17
+ keys_regex = /(\$#{joined_keys}\b)|(#\{\$#{joined_keys}\})/
18
+
19
+ substitution_hash = variables.each_with_object({}) do |(k, v), hash|
20
+ hash["$#{k}"] = v.to_s
21
+ hash["\#{$#{k}}"] = v.to_s
22
+ end
23
+
24
+ body = raw.to_s.dup
25
+ body.gsub!(keys_regex, substitution_hash)
26
+
27
+ # Color manipulation
28
+ body.gsub!(COLOR_COMMAND_REGEXP) do |whole_match|
29
+ if $LAST_MATCH_INFO[:command] == 'lighten'
30
+ color_adjust($LAST_MATCH_INFO[:color], $LAST_MATCH_INFO[:percentage].to_i, :+) || whole_match
31
+ else
32
+ color_adjust($LAST_MATCH_INFO[:color], $LAST_MATCH_INFO[:percentage].to_i, :-) || whole_match
33
+ end
34
+ end
35
+
36
+ body
37
+ end
38
+
39
+ def self.color_adjust(rgb, percentage, op)
40
+ color = Sass::Script::Value::Color.from_hex(rgb)
41
+
42
+ # Copied from:
43
+ # https://github.com/sass/sass/blob/3.4.21/lib/sass/script/functions.rb#L2671
44
+ new_color = color.with(lightness: color.lightness.public_send(op, percentage))
45
+
46
+ # We set the Sass Output to compressed
47
+ new_color.options = { style: :compressed }
48
+ new_color.to_sass
49
+ rescue ArgumentError
50
+ nil
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module ZendeskAppsTools
3
- VERSION = '2.3.0'
3
+ VERSION = '2.4.0'
4
4
  end
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ZendeskAppsTools
2
4
  autoload :Command, 'zendesk_apps_tools/command'
3
5
  autoload :Translate, 'zendesk_apps_tools/translate'
6
+ autoload :Theme, 'zendesk_apps_tools/theme'
4
7
  autoload :LocaleIdentifier, 'zendesk_apps_tools/locale_identifier'
5
8
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zendesk_apps_tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James A. Rosen
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2017-09-12 00:00:00.000000000 Z
14
+ date: 2017-09-26 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: thor
@@ -41,6 +41,20 @@ dependencies:
41
41
  - - "~>"
42
42
  - !ruby/object:Gem::Version
43
43
  version: 1.2.1
44
+ - !ruby/object:Gem::Dependency
45
+ name: thin
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - "~>"
49
+ - !ruby/object:Gem::Version
50
+ version: 1.7.2
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - "~>"
56
+ - !ruby/object:Gem::Version
57
+ version: 1.7.2
44
58
  - !ruby/object:Gem::Dependency
45
59
  name: sinatra
46
60
  requirement: !ruby/object:Gem::Requirement
@@ -111,6 +125,48 @@ dependencies:
111
125
  - - "~>"
112
126
  - !ruby/object:Gem::Version
113
127
  version: 0.3.1
128
+ - !ruby/object:Gem::Dependency
129
+ name: listen
130
+ requirement: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - "~>"
133
+ - !ruby/object:Gem::Version
134
+ version: '2.10'
135
+ type: :runtime
136
+ prerelease: false
137
+ version_requirements: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - "~>"
140
+ - !ruby/object:Gem::Version
141
+ version: '2.10'
142
+ - !ruby/object:Gem::Dependency
143
+ name: rack-livereload
144
+ requirement: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ type: :runtime
150
+ prerelease: false
151
+ version_requirements: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ - !ruby/object:Gem::Dependency
157
+ name: faye-websocket
158
+ requirement: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - "~>"
161
+ - !ruby/object:Gem::Version
162
+ version: 0.10.7
163
+ type: :runtime
164
+ prerelease: false
165
+ version_requirements: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - "~>"
168
+ - !ruby/object:Gem::Version
169
+ version: 0.10.7
114
170
  - !ruby/object:Gem::Dependency
115
171
  name: cucumber
116
172
  requirement: !ruby/object:Gem::Requirement
@@ -238,6 +294,10 @@ files:
238
294
  - lib/zendesk_apps_tools/package_helper.rb
239
295
  - lib/zendesk_apps_tools/server.rb
240
296
  - lib/zendesk_apps_tools/settings.rb
297
+ - lib/zendesk_apps_tools/theme.rb
298
+ - lib/zendesk_apps_tools/theming/common.rb
299
+ - lib/zendesk_apps_tools/theming/server.rb
300
+ - lib/zendesk_apps_tools/theming/zass_formatter.rb
241
301
  - lib/zendesk_apps_tools/translate.rb
242
302
  - lib/zendesk_apps_tools/version.rb
243
303
  - templates/translation.erb.tt
@@ -261,7 +321,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
261
321
  version: 1.3.6
262
322
  requirements: []
263
323
  rubyforge_project:
264
- rubygems_version: 2.6.11
324
+ rubygems_version: 2.6.13
265
325
  signing_key:
266
326
  specification_version: 4
267
327
  summary: Tools to help you develop Zendesk Apps.
@@ -276,3 +336,4 @@ test_files:
276
336
  - features/support/helpers.rb
277
337
  - features/support/webmock.rb
278
338
  - features/validate.feature
339
+ has_rdoc: