zendesk_apps_support 2.0.0 → 3.0.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: 4dd2daecb448295a2847cc9d879318f8b6011ba0
4
- data.tar.gz: b3cab095bb292245d829e3577c3f58e95118b992
3
+ metadata.gz: 8f36f2639f048681823a88ef9669638499ce53d2
4
+ data.tar.gz: 526ed308014fe9475d670597f63904a112ecdd5c
5
5
  SHA512:
6
- metadata.gz: 0120e92ede4d68764a5727116a2fcb21e6d928e10b528e0333f4f76e4078b06133b1e437717ee1da8ba6ebfd3a2edd69e445b37aa27b3fc0f545deafdea64318
7
- data.tar.gz: 9fa406bf712f245602583e334a02fa14c3a220e6531714c6173d5e0a2d01bfb8a514b83178886f89b029643a64c08ac25d6fade7d3ce82dd68965d29ed13960f
6
+ metadata.gz: 881df6e418a86e65c7e8774b72f96924fd2b5d4aa96ffa34bb27d1f9fadc141d852835cc6677ceae7bfcad3284399b785f788c91547b982f20e5d109319a3b0b
7
+ data.tar.gz: d49736cdfb17419959dadee4b5755f0ef709432d0ffea53bf86655fe57ed5ec9794c1ea4276a8dcd3c9c93fe6be183f54dc23725fc19904295d5aff831e307dc
@@ -47,8 +47,8 @@ en:
47
47
  style_in_template: "<style> tag in %{template}. Use an app.css file instead."
48
48
  no_code_for_ifo_notemplate: Javascripts, stylesheets, and templates are
49
49
  not allowed when an iframe URI or noTemplate is specified
50
- invalid_default_locale: "%{defaultLocale} is not a valid default locale."
51
- missing_translation_file: 'Missing translation file for locale ''%{defaultLocale}''.
50
+ invalid_default_locale: "%{default_locale} is not a valid default locale."
51
+ missing_translation_file: 'Missing translation file for locale ''%{default_locale}''.
52
52
  Please read: http://developer.zendesk.com/documentation/apps/i18n.html'
53
53
  invalid_host: "%{host_name} is an invalid host for apps."
54
54
  invalid_location:
@@ -77,6 +77,12 @@ en:
77
77
  invalid_type_parameter:
78
78
  one: "%{invalid_types} is an invalid parameter type."
79
79
  other: "%{invalid_types} are invalid parameter types."
80
+ unacceptable_boolean: '%{field} must be a Boolean value, got "%{value}"'
81
+ invalid_no_template: noTemplate must be set to true, false, or an array
82
+ of valid locations.
83
+ duplicate_reference: 'Duplicate reference in manifest: "%{key}".'
84
+ duplicate_reference_values: Initially set to "%{original}", attempted
85
+ overwrite to "%{attempted}".
80
86
  warning:
81
87
  app_build:
82
88
  deprecated_version: You are targeting a deprecated version of the framework.
@@ -115,11 +115,11 @@ parts:
115
115
  - translation:
116
116
  key: "txt.apps.admin.error.app_build.invalid_default_locale"
117
117
  title: "App builder job: invalid default locale"
118
- value: "%{defaultLocale} is not a valid default locale."
118
+ value: "%{default_locale} is not a valid default locale."
119
119
  - translation:
120
120
  key: "txt.apps.admin.error.app_build.missing_translation_file"
121
121
  title: "App builder job: missing translation file"
122
- value: "Missing translation file for locale '%{defaultLocale}'. Please read: http://developer.zendesk.com/documentation/apps/i18n.html"
122
+ value: "Missing translation file for locale '%{default_locale}'. Please read: http://developer.zendesk.com/documentation/apps/i18n.html"
123
123
  - translation:
124
124
  key: "txt.apps.admin.error.app_build.invalid_host"
125
125
  title: "App builder job: invalid host application for apps"
@@ -204,3 +204,19 @@ parts:
204
204
  key: "txt.apps.admin.error.app_build.invalid_type_parameter.other"
205
205
  title: "App builder job: multiple invalid parameter types error"
206
206
  value: "%{invalid_types} are invalid parameter types."
207
+ - translation:
208
+ key: "txt.apps.admin.error.app_build.unacceptable_boolean"
209
+ title: "App builder job: this value needs to be either true or false, but something else was passed in"
210
+ value: "%{field} must be a Boolean value, got \"%{value}\""
211
+ - translation:
212
+ key: "txt.apps.admin.error.app_build.invalid_no_template"
213
+ title: "App builder job: `noTemplate` needs to be true, false or a valid array, but something else was passed in. noTemplate, true, and false should not be translated."
214
+ value: "noTemplate must be set to true, false, or an array of valid locations."
215
+ - translation:
216
+ key: "txt.apps.admin.error.app_build.duplicate_reference"
217
+ title: "App builder job: a key was included twice in the supplied manifest file, but it is only allowed once"
218
+ value: "Duplicate reference in manifest: \"%{key}\"."
219
+ - translation:
220
+ key: "txt.apps.admin.error.app_build.duplicate_reference_values"
221
+ title: "This sentence follows txt.apps.admin.error.app_build.duplicate_reference. The values are included to help find the problem."
222
+ value: "Initially set to \"%{original}\", attempted overwrite to \"%{attempted}\"."
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+ require 'json'
3
+
4
+ module ZendeskAppsSupport
5
+ class Manifest
6
+ class OverrideError < StandardError
7
+ attr_reader :key, :original, :attempted
8
+ attr_accessor :message
9
+ def initialize(key, original, attempted)
10
+ @key = key
11
+ @original = original
12
+ @attempted = attempted
13
+ end
14
+
15
+ def message
16
+ @message ||= begin
17
+ translated_error_key = 'txt.apps.admin.error.app_build.duplicate_reference'
18
+ translated_error = ZendeskAppsSupport::I18n.t(translated_error_key, key: key)
19
+
20
+ # if the error contains the word `_legacy` in the second sentence, let's
21
+ # only use the first one.
22
+ if [original, attempted].any? { |val| val =~ /_legacy/ }
23
+ return translated_error
24
+ end
25
+ translated_detail_key = 'txt.apps.admin.error.app_build.duplicate_reference_values'
26
+ translated_detail = ZendeskAppsSupport::I18n.t(translated_detail_key,
27
+ original: original,
28
+ attempted: attempted)
29
+ "#{translated_error} #{translated_detail}"
30
+ end
31
+ end
32
+ end
33
+
34
+ class NoOverrideHash < Hash
35
+ class << self
36
+ def [](array)
37
+ new.tap do |hash|
38
+ array.each do |key, value|
39
+ hash[key] = value
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ def []=(key, value)
46
+ raise OverrideError.new(key, self[key], value) if key? key
47
+ super
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ module ZendeskAppsSupport
3
+ class Manifest
4
+ class Parameter
5
+ TYPES = %w(text password checkbox url number multiline hidden).freeze
6
+ attr_reader :name, :type, :required, :secure, :default
7
+ def default?
8
+ @has_default
9
+ end
10
+
11
+ def initialize(p)
12
+ @name = p['name']
13
+ @type = p['type'] || 'text'
14
+ @required = p['required'] || false
15
+ @secure = p['secure'] || false
16
+ @has_default = p.key? 'default'
17
+ @default = p['default'] if @has_default
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+ module ZendeskAppsSupport
3
+ class Manifest
4
+ LEGACY_URI_STUB = '_legacy'
5
+
6
+ RUBY_TO_JSON = {
7
+ requirements_only: 'requirementsOnly',
8
+ version: 'version',
9
+ author: 'author',
10
+ framework_version: 'frameworkVersion',
11
+ single_install: 'singleInstall',
12
+ signed_urls: 'signedUrls',
13
+ no_template: 'noTemplate',
14
+ default_locale: 'defaultLocale',
15
+ original_locations: 'location',
16
+ private: 'private',
17
+ oauth: 'oauth',
18
+ original_parameters: 'parameters',
19
+ domain_whitelist: 'domainWhitelist',
20
+ remote_installation_url: 'remoteInstallationURL',
21
+ terms_conditions_url: 'termsConditionsURL',
22
+ google_analytics_code: 'gaID'
23
+ }.freeze
24
+
25
+ attr_reader(*RUBY_TO_JSON.keys)
26
+
27
+ alias_method :requirements_only?, :requirements_only
28
+ alias_method :signed_urls?, :signed_urls
29
+ alias_method :single_install?, :single_install
30
+ alias_method :private?, :private
31
+
32
+ def no_template?
33
+ if no_template.is_a?(Array)
34
+ false
35
+ else
36
+ no_template
37
+ end
38
+ end
39
+
40
+ def no_template_locations
41
+ no_template || []
42
+ end
43
+
44
+ def location?
45
+ !locations.values.all?(&:empty?)
46
+ end
47
+
48
+ def locations
49
+ @locations ||=
50
+ case original_locations
51
+ when Hash
52
+ @used_hosts = original_locations.keys
53
+ replace_legacy_locations original_locations
54
+ when Array
55
+ @used_hosts = ['support']
56
+ { 'support' => NoOverrideHash[original_locations.map { |location| [ location, LEGACY_URI_STUB ] }] }
57
+ when String
58
+ @used_hosts = ['support']
59
+ { 'support' => { original_locations => LEGACY_URI_STUB } }
60
+ # TODO: error out for numbers and Booleans
61
+ else # NilClass
62
+ @used_hosts = ['support']
63
+ { 'support' => {} }
64
+ end
65
+ end
66
+
67
+ def unknown_hosts
68
+ @unknown_hosts ||= @used_hosts - Product::PRODUCTS_AVAILABLE.flat_map { |p| [p.name, p.legacy_name] }
69
+ end
70
+
71
+ def iframe_only?
72
+ Gem::Version.new(framework_version) >= Gem::Version.new('2')
73
+ end
74
+
75
+ def parameters
76
+ @parameters ||= begin
77
+ parameter_array = @original_parameters.is_a?(Array) ? @original_parameters : []
78
+ parameter_array.map do |parameter_hash|
79
+ Parameter.new(parameter_hash)
80
+ end
81
+ end
82
+ end
83
+
84
+ def initialize(manifest_text)
85
+ m = parse_json(manifest_text)
86
+ RUBY_TO_JSON.each do |ruby, json|
87
+ instance_variable_set(:"@#{ruby}", m[json])
88
+ end
89
+ @requirements_only ||= false
90
+ @single_install ||= false
91
+ @private = m.fetch('private', true)
92
+ @signed_urls ||= false
93
+ @no_template ||= false
94
+ end
95
+
96
+ private
97
+
98
+ def replace_legacy_locations(original_locations)
99
+ NoOverrideHash.new.tap do |new_locations_obj|
100
+ Product::PRODUCTS_AVAILABLE.each do |product|
101
+ product_key = product.name.to_s
102
+ legacy_key = product.legacy_name.to_s
103
+ value_for_product = original_locations.fetch(product_key, original_locations[legacy_key])
104
+ value_for_product && new_locations_obj[product_key] = value_for_product
105
+ end
106
+ end
107
+ end
108
+
109
+ def parse_json(manifest_text)
110
+ parser_opts = { object_class: Manifest::NoOverrideHash }
111
+ JSON.parse(manifest_text, parser_opts)
112
+ end
113
+ end
114
+ end
@@ -1,19 +1,19 @@
1
+ # frozen_string_literal: true
1
2
  require 'pathname'
2
3
  require 'erubis'
3
4
  require 'json'
4
5
 
5
6
  module ZendeskAppsSupport
6
7
  class Package
8
+ extend Gem::Deprecate
7
9
  include ZendeskAppsSupport::BuildTranslation
8
10
 
9
- REQUIREMENTS_FILENAME = "requirements.json"
11
+ REQUIREMENTS_FILENAME = 'requirements.json'
10
12
 
11
13
  DEFAULT_LAYOUT = Erubis::Eruby.new(File.read(File.expand_path('../assets/default_template.html.erb', __FILE__)))
12
14
  DEFAULT_SCSS = File.read(File.expand_path('../assets/default_styles.scss', __FILE__))
13
15
  SRC_TEMPLATE = Erubis::Eruby.new(File.read(File.expand_path('../assets/src.js.erb', __FILE__)))
14
16
 
15
- LEGACY_URI_STUB = '_legacy'
16
-
17
17
  attr_reader :lib_root, :root, :warnings
18
18
 
19
19
  def initialize(dir, is_cached = true)
@@ -34,7 +34,7 @@ module ZendeskAppsSupport
34
34
  errors << Validations::Source.call(self)
35
35
  errors << Validations::Translations.call(self)
36
36
 
37
- unless manifest_json['requirementsOnly']
37
+ unless manifest.requirements_only?
38
38
  errors << Validations::Templates.call(self)
39
39
  errors << Validations::Stylesheets.call(self)
40
40
  end
@@ -116,40 +116,41 @@ module ZendeskAppsSupport
116
116
 
117
117
  locale = options.fetch(:locale, 'en')
118
118
 
119
- source = iframe_only? ? nil : app_js
120
- version = manifest_json['version']
119
+ source = manifest.iframe_only? ? nil : app_js
121
120
  app_class_name = "app-#{app_id}"
122
- author = manifest_json['author']
123
- framework_version = manifest_json['frameworkVersion']
124
- single_install = manifest_json['singleInstall'] || false
125
- signed_urls = manifest_json['signedUrls'] || false
126
- templates = is_no_template ? {} : compiled_templates(app_id, asset_url_prefix)
121
+ # if no_template is an array, we still need the templates
122
+ templates = manifest.no_template == true ? {} : compiled_templates(app_id, asset_url_prefix)
127
123
 
128
124
  app_settings = {
129
- location: locations,
130
- noTemplate: no_template_locations,
131
- singleInstall: single_install,
132
- signedUrls: signed_urls
125
+ location: manifest.locations,
126
+ noTemplate: manifest.no_template_locations,
127
+ singleInstall: manifest.single_install?,
128
+ signedUrls: manifest.signed_urls?
133
129
  }.select { |_k, v| !v.nil? }
134
130
 
135
131
  SRC_TEMPLATE.result(
136
132
  name: name,
137
- version: version,
133
+ version: manifest.version,
138
134
  source: source,
139
135
  app_settings: app_settings,
140
136
  asset_url_prefix: asset_url_prefix,
141
137
  app_class_name: app_class_name,
142
- author: author,
138
+ author: manifest.author,
143
139
  translations: runtime_translations(translations_for(locale)),
144
- framework_version: framework_version,
140
+ framework_version: manifest.framework_version,
145
141
  templates: templates,
146
142
  modules: commonjs_modules,
147
- iframe_only: iframe_only?
143
+ iframe_only: manifest.iframe_only?
148
144
  )
149
145
  end
150
146
 
151
147
  def manifest_json
152
- @manifest ||= read_json('manifest.json')
148
+ @manifest_json ||= read_json('manifest.json')
149
+ end
150
+ deprecate :manifest_json, :manifest, 2016, 9
151
+
152
+ def manifest
153
+ @manifest ||= Manifest.new(read_file('manifest.json'))
153
154
  end
154
155
 
155
156
  def requirements_json
@@ -158,20 +159,14 @@ module ZendeskAppsSupport
158
159
  end
159
160
 
160
161
  def is_no_template
161
- if manifest_json['noTemplate'].is_a?(Array)
162
- false
163
- else
164
- !!manifest_json['noTemplate']
165
- end
162
+ manifest.no_template?
166
163
  end
164
+ deprecate :is_no_template, 'manifest.no_template?', 2016, 9
167
165
 
168
166
  def no_template_locations
169
- if manifest_json['noTemplate'].is_a?(Array)
170
- manifest_json['noTemplate']
171
- else
172
- !!manifest_json['noTemplate']
173
- end
167
+ manifest.no_template_locations
174
168
  end
169
+ deprecate :no_template_locations, 'manifest.no_template_locations', 2016, 9
175
170
 
176
171
  def compiled_templates(app_id, asset_url_prefix)
177
172
  compiled_css = ZendeskAppsSupport::StylesheetCompiler.new(DEFAULT_SCSS + app_css, app_id, asset_url_prefix).compile
@@ -186,12 +181,13 @@ module ZendeskAppsSupport
186
181
  def translations_for(locale)
187
182
  trans = translations
188
183
  return trans[locale] if trans[locale]
189
- trans[self.manifest_json['defaultLocale']]
184
+ trans[manifest.default_locale]
190
185
  end
191
186
 
192
187
  def has_location?
193
- manifest_json['location']
188
+ manifest.location?
194
189
  end
190
+ deprecate :has_location?, 'manifest.location?', 2016, 9
195
191
 
196
192
  def has_file?(path)
197
193
  File.exist?(path_to(path))
@@ -204,26 +200,17 @@ module ZendeskAppsSupport
204
200
  end
205
201
 
206
202
  def locations
207
- locations = manifest_json['location']
208
- case locations
209
- when Hash
210
- locations
211
- when Array
212
- { 'zendesk' => Hash[locations.map { |location| [ location, LEGACY_URI_STUB ] }] }
213
- when String
214
- { 'zendesk' => { locations => LEGACY_URI_STUB } }
215
- else # NilClass
216
- { 'zendesk' => {} }
217
- end
203
+ manifest.locations
218
204
  end
205
+ deprecate :locations, 'manifest.locations', 2016, 9
219
206
 
220
207
  def iframe_only?
221
- !legacy_non_iframe_app?
208
+ manifest.iframe_only?
222
209
  end
210
+ deprecate :iframe_only?, 'manifest.iframe_only?', 2016, 9
223
211
 
224
212
  private
225
213
 
226
-
227
214
  def runtime_translations(translations)
228
215
  result = translations.dup
229
216
  result.delete('name')
@@ -233,11 +220,6 @@ module ZendeskAppsSupport
233
220
  result
234
221
  end
235
222
 
236
- def legacy_non_iframe_app?
237
- iframe_urls = locations.values.flat_map(&:values)
238
- iframe_urls.all? { |l| l == LEGACY_URI_STUB }
239
- end
240
-
241
223
  def templates
242
224
  templates_dir = File.join(root, 'templates')
243
225
  Dir["#{templates_dir}/*.hdbs"].inject({}) do |memo, file|
@@ -255,13 +237,13 @@ module ZendeskAppsSupport
255
237
  translation_dir = File.join(root, 'translations')
256
238
  return {} unless File.directory?(translation_dir)
257
239
 
258
- locale_path = "#{translation_dir}/#{self.manifest_json['defaultLocale']}.json"
240
+ locale_path = "#{translation_dir}/#{manifest.default_locale}.json"
259
241
  default_translations = process_translations(locale_path)
260
242
 
261
243
  Dir["#{translation_dir}/*.json"].inject({}) do |memo, path|
262
244
  locale = File.basename(path, File.extname(path))
263
245
 
264
- locale_translations = if locale == self.manifest_json['defaultLocale']
246
+ locale_translations = if locale == manifest.default_locale
265
247
  default_translations
266
248
  else
267
249
  deep_merge_hash(default_translations, process_translations(path))
@@ -325,10 +307,10 @@ module ZendeskAppsSupport
325
307
  File.read(path_to(path))
326
308
  end
327
309
 
328
- def read_json(path)
310
+ def read_json(path, parser_opts = {})
329
311
  file = read_file(path)
330
312
  unless file.nil?
331
- JSON.parse(read_file(path))
313
+ JSON.parse(read_file(path), parser_opts)
332
314
  end
333
315
  end
334
316
  end
@@ -4,26 +4,27 @@ require 'uri'
4
4
  module ZendeskAppsSupport
5
5
  module Validations
6
6
  module Manifest
7
- REQUIRED_MANIFEST_FIELDS = %w( author defaultLocale ).freeze
8
- OAUTH_REQUIRED_FIELDS = %w( client_id client_secret authorize_uri access_token_uri ).freeze
9
- TYPES_AVAILABLE = %w( text password checkbox url number multiline hidden ).freeze
7
+ RUBY_TO_JSON = ZendeskAppsSupport::Manifest::RUBY_TO_JSON
8
+ REQUIRED_MANIFEST_FIELDS = RUBY_TO_JSON.select { |k| %i(author default_locale).include? k }.freeze
9
+ OAUTH_REQUIRED_FIELDS = %w(client_id client_secret authorize_uri access_token_uri).freeze
10
10
 
11
11
  class <<self
12
12
  def call(package)
13
13
  return [ValidationError.new(:missing_manifest)] unless package.has_file?('manifest.json')
14
-
15
- manifest = package.manifest_json
14
+ manifest = package.manifest
16
15
 
17
16
  errors = []
18
17
  errors << missing_keys_error(manifest)
19
- errors << default_locale_error(manifest, package)
20
18
  errors << oauth_error(manifest)
21
19
  errors << parameters_error(manifest)
22
20
  errors << invalid_hidden_parameter_error(manifest)
23
21
  errors << invalid_type_error(manifest)
24
22
  errors << name_as_parameter_name_error(manifest)
23
+ errors << no_template_format_error(manifest)
24
+ errors << boolean_error(manifest)
25
+ errors << default_locale_error(manifest, package)
25
26
 
26
- if manifest['requirementsOnly']
27
+ if manifest.requirements_only?
27
28
  errors << ban_location(manifest)
28
29
  errors << ban_framework_version(manifest)
29
30
  else
@@ -31,8 +32,8 @@ module ZendeskAppsSupport
31
32
  errors << invalid_location_error(package)
32
33
  errors << duplicate_location_error(manifest)
33
34
  errors << missing_framework_version(manifest)
35
+ errors << location_framework_mismatch(manifest)
34
36
  errors << invalid_version_error(manifest, package)
35
- errors << framework_version_iframe_only(package, manifest)
36
37
  end
37
38
 
38
39
  errors.flatten.compact
@@ -42,19 +43,42 @@ module ZendeskAppsSupport
42
43
 
43
44
  private
44
45
 
46
+ def boolean_error(manifest)
47
+ booleans = %i(requirements_only single_install signed_urls private)
48
+ errors = []
49
+ RUBY_TO_JSON.each do |ruby, json|
50
+ if booleans.include? ruby
51
+ errors << validate_boolean(manifest.public_send(ruby), json)
52
+ end
53
+ end
54
+ errors.compact
55
+ end
56
+
57
+ def validate_boolean(value, label_for_error)
58
+ unless [true, false].include? value
59
+ ValidationError.new(:unacceptable_boolean, field: label_for_error, value: value)
60
+ end
61
+ end
62
+
63
+ def check_errors(error_types, collector, *checked_objects)
64
+ error_types.each do |error_type|
65
+ collector << send(error_type, *checked_objects)
66
+ end
67
+ end
68
+
45
69
  def ban_location(manifest)
46
- ValidationError.new(:no_location_required) unless manifest['location'].nil?
70
+ ValidationError.new(:no_location_required) if manifest.location?
47
71
  end
48
72
 
49
73
  def ban_framework_version(manifest)
50
- ValidationError.new(:no_framework_version_required) unless manifest['frameworkVersion'].nil?
74
+ ValidationError.new(:no_framework_version_required) unless manifest.framework_version.nil?
51
75
  end
52
76
 
53
77
  def oauth_error(manifest)
54
- return unless manifest['oauth']
78
+ return unless manifest.oauth
55
79
 
56
80
  missing = OAUTH_REQUIRED_FIELDS.select do |key|
57
- manifest['oauth'][key].nil? || manifest['oauth'][key].empty?
81
+ manifest.oauth[key].nil? || manifest.oauth[key].empty?
58
82
  end
59
83
 
60
84
  if missing.any?
@@ -63,78 +87,86 @@ module ZendeskAppsSupport
63
87
  end
64
88
 
65
89
  def parameters_error(manifest)
66
- return unless manifest['parameters']
67
-
68
- unless manifest['parameters'].is_a?(Array)
90
+ original = manifest.original_parameters
91
+ unless original.nil? || original.is_a?(Array)
69
92
  return ValidationError.new(:parameters_not_an_array)
70
93
  end
71
94
 
72
- para_names = manifest['parameters'].collect { |para| para['name'] }
95
+ return unless manifest.parameters.any?
96
+
97
+ para_names = manifest.parameters.map(&:name)
73
98
  duplicate_parameters = para_names.select { |name| para_names.count(name) > 1 }.uniq
74
99
  unless duplicate_parameters.empty?
75
100
  return ValidationError.new(:duplicate_parameters, duplicate_parameters: duplicate_parameters)
76
101
  end
102
+
103
+ invalid_required = manifest.parameters.map do |parameter|
104
+ validate_boolean(parameter.required, "parameters.#{parameter.name}.required")
105
+ end.compact
106
+ return invalid_required if invalid_required.any?
107
+
108
+ invalid_secure = manifest.parameters.map do |parameter|
109
+ validate_boolean(parameter.secure, "parameters.#{parameter.name}.secure")
110
+ end.compact
111
+ return invalid_secure if invalid_secure.any?
77
112
  end
78
113
 
79
114
  def missing_keys_error(manifest)
80
- missing = REQUIRED_MANIFEST_FIELDS.select do |key|
81
- manifest[key].nil?
82
- end
115
+ missing = REQUIRED_MANIFEST_FIELDS.map do |ruby_method, manifest_value|
116
+ manifest_value if manifest.public_send(ruby_method).nil?
117
+ end.compact
83
118
 
84
119
  missing_keys_validation_error(missing) if missing.any?
85
120
  end
86
121
 
87
122
  def default_locale_error(manifest, package)
88
- default_locale = manifest['defaultLocale']
123
+ default_locale = manifest.default_locale
89
124
  unless default_locale.nil?
90
125
  if default_locale !~ Translations::VALID_LOCALE
91
- ValidationError.new(:invalid_default_locale, defaultLocale: default_locale)
126
+ ValidationError.new(:invalid_default_locale, default_locale: default_locale)
92
127
  elsif package.translation_files.detect { |file| file.relative_path == "translations/#{default_locale}.json" }.nil?
93
- ValidationError.new(:missing_translation_file, defaultLocale: default_locale)
128
+ ValidationError.new(:missing_translation_file, default_locale: default_locale)
94
129
  end
95
130
  end
96
131
  end
97
132
 
98
133
  def missing_location_error(package)
99
- missing_keys_validation_error(['location']) unless package.has_location?
134
+ missing_keys_validation_error(['location']) unless package.manifest.location?
100
135
  end
101
136
 
102
137
  def invalid_location_error(package)
103
138
  errors = []
104
- manifest_locations = package.locations
139
+ manifest_locations = package.manifest.locations
105
140
  manifest_locations.find do |host, locations|
106
- # CRUFT: remove when support for legacy names are deprecated
107
- product = Product.find_by(legacy_name: host) || Product.find_by(name: host)
108
- error = if product.nil?
109
- ValidationError.new(:invalid_host, host_name: host)
110
- elsif (invalid_locations = locations.keys - Location.where(product_code: product.code).map(&:name)).any?
111
- ValidationError.new(:invalid_location,
141
+ product = Product.find_by(name: host)
142
+ stub = ZendeskAppsSupport::Manifest::LEGACY_URI_STUB
143
+ locations_allowed = Location.where(product_code: product.code).map(&:name).push(stub)
144
+ if (invalid_locations = locations.keys - locations_allowed).any?
145
+ errors << ValidationError.new(:invalid_location,
112
146
  invalid_locations: invalid_locations.join(', '),
113
147
  host_name: host,
114
148
  count: invalid_locations.length)
115
149
  end
116
150
 
117
- # abort early for invalid host or location name
118
- if error
119
- errors << error
120
- break
121
- end
122
-
123
151
  locations.values.each do |path|
124
152
  errors << invalid_location_uri_error(package, path)
125
153
  end
126
154
  end
155
+
156
+ package.manifest.unknown_hosts.each do |unknown_host|
157
+ errors << ValidationError.new(:invalid_host, host_name: unknown_host)
158
+ end
127
159
  errors
128
160
  end
129
161
 
130
162
  def invalid_location_uri_error(package, path)
131
- return nil if path == Package::LEGACY_URI_STUB
163
+ return nil if path == ZendeskAppsSupport::Manifest::LEGACY_URI_STUB
132
164
  validation_error = ValidationError.new(:invalid_location_uri, uri: path)
133
165
  uri = URI.parse(path)
134
166
  unless uri.absolute? ? valid_absolute_uri?(uri) : valid_relative_uri?(package, uri)
135
167
  validation_error
136
168
  end
137
- rescue URI::InvalidURIError => e
169
+ rescue URI::InvalidURIError
138
170
  validation_error
139
171
  end
140
172
 
@@ -147,7 +179,7 @@ module ZendeskAppsSupport
147
179
  end
148
180
 
149
181
  def duplicate_location_error(manifest)
150
- locations = *manifest['location']
182
+ locations = *manifest.locations
151
183
  duplicate_locations = *locations.select { |location| locations.count(location) > 1 }.uniq
152
184
 
153
185
  unless duplicate_locations.empty?
@@ -156,12 +188,12 @@ module ZendeskAppsSupport
156
188
  end
157
189
 
158
190
  def missing_framework_version(manifest)
159
- missing_keys_validation_error(['frameworkVersion']) if manifest['frameworkVersion'].nil?
191
+ missing_keys_validation_error([RUBY_TO_JSON[:framework_version]]) if manifest.framework_version.nil?
160
192
  end
161
193
 
162
194
  def invalid_version_error(manifest, package)
163
195
  valid_to_serve = AppVersion::TO_BE_SERVED
164
- target_version = manifest['frameworkVersion']
196
+ target_version = manifest.framework_version
165
197
 
166
198
  if target_version == AppVersion::DEPRECATED
167
199
  package.warnings << I18n.t('txt.apps.admin.warning.app_build.deprecated_version')
@@ -173,19 +205,13 @@ module ZendeskAppsSupport
173
205
  end
174
206
 
175
207
  def name_as_parameter_name_error(manifest)
176
- if manifest['parameters'].is_a?(Array)
177
- if manifest['parameters'].any? { |p| p['name'] == 'name' }
178
- ValidationError.new(:name_as_parameter_name)
179
- end
208
+ if manifest.parameters.any? { |p| p.name == 'name' }
209
+ ValidationError.new(:name_as_parameter_name)
180
210
  end
181
211
  end
182
212
 
183
213
  def invalid_hidden_parameter_error(manifest)
184
- invalid_params = []
185
-
186
- if manifest.key?('parameters')
187
- invalid_params = manifest['parameters'].select { |p| p['type'] == 'hidden' && p['required'] }.map { |p| p['name'] }
188
- end
214
+ invalid_params = manifest.parameters.select { |p| p.type == 'hidden' && p.required }.map(&:name)
189
215
 
190
216
  if invalid_params.any?
191
217
  ValidationError.new(:invalid_hidden_parameter, invalid_params: invalid_params.join(', '), count: invalid_params.length)
@@ -193,18 +219,17 @@ module ZendeskAppsSupport
193
219
  end
194
220
 
195
221
  def invalid_type_error(manifest)
196
- return unless manifest['parameters'].is_a?(Array)
197
-
198
222
  invalid_types = []
223
+ manifest.parameters.each do |parameter|
224
+ parameter_type = parameter.type
199
225
 
200
- manifest['parameters'].each do |parameter|
201
- parameter_type = parameter.fetch('type', '')
202
-
203
- invalid_types << parameter_type unless TYPES_AVAILABLE.include?(parameter_type)
226
+ invalid_types << parameter_type unless ZendeskAppsSupport::Manifest::Parameter::TYPES.include?(parameter_type)
204
227
  end
205
228
 
206
229
  if invalid_types.any?
207
- ValidationError.new(:invalid_type_parameter, invalid_types: invalid_types.join(', '), count: invalid_types.length)
230
+ ValidationError.new(:invalid_type_parameter,
231
+ invalid_types: invalid_types.join(', '),
232
+ count: invalid_types.length)
208
233
  end
209
234
  end
210
235
 
@@ -212,14 +237,35 @@ module ZendeskAppsSupport
212
237
  ValidationError.new('manifest_keys.missing', missing_keys: missing_keys.join(', '), count: missing_keys.length)
213
238
  end
214
239
 
215
- def framework_version_iframe_only(package, manifest)
216
- if (package.iframe_only?)
217
- manifest_version = Gem::Version.new (manifest['frameworkVersion'] || 0)
218
- required_version = Gem::Version.new '2.0'
240
+ def location_framework_mismatch(manifest)
241
+ locations = manifest.locations
242
+ stub = ZendeskAppsSupport::Manifest::LEGACY_URI_STUB
243
+ iframe_locations = locations_any?(locations) do |url|
244
+ url != stub
245
+ end
246
+ legacy_locations = (!iframe_locations && manifest.location?) || locations_any?(locations) do |url|
247
+ url != stub
248
+ end
249
+ if manifest.iframe_only?
250
+ return ValidationError.new(:locations_must_be_urls) if legacy_locations
251
+ elsif iframe_locations
252
+ return ValidationError.new(:locations_cant_be_urls)
253
+ end
254
+ end
219
255
 
220
- if (manifest_version < required_version)
221
- ValidationError.new(:old_version)
222
- end
256
+ def locations_any?(locations)
257
+ locations.values.any? do |location_hash|
258
+ location_hash.values.any? { |url| yield(url) }
259
+ end
260
+ end
261
+
262
+ # TODO: support the new location format in the no_template array and check the app actually runs in
263
+ # included locations
264
+ def no_template_format_error(manifest)
265
+ no_template = manifest.no_template
266
+ return if [false, true].include? no_template
267
+ unless no_template.is_a?(Array) && manifest.no_template_locations.all? { |loc| Location.find_by(name: loc) }
268
+ ValidationError.new(:invalid_no_template)
223
269
  end
224
270
  end
225
271
  end
@@ -42,8 +42,8 @@ module ZendeskAppsSupport
42
42
  end
43
43
 
44
44
  def package_needs_app_js?(package)
45
- return false if package.manifest_json['requirementsOnly']
46
- return false if package.iframe_only?
45
+ return false if package.manifest.requirements_only?
46
+ return false if package.manifest.iframe_only?
47
47
  true
48
48
  end
49
49
 
@@ -6,6 +6,7 @@ module ZendeskAppsSupport
6
6
  autoload :BuildTranslation, 'zendesk_apps_support/build_translation'
7
7
  autoload :I18n, 'zendesk_apps_support/i18n'
8
8
  autoload :Location, 'zendesk_apps_support/location'
9
+ autoload :Manifest, 'zendesk_apps_support/manifest'
9
10
  autoload :Product, 'zendesk_apps_support/product'
10
11
  autoload :Package, 'zendesk_apps_support/package'
11
12
  autoload :Installed, 'zendesk_apps_support/installed'
@@ -27,4 +28,9 @@ module ZendeskAppsSupport
27
28
  autoload :Requirements, 'zendesk_apps_support/validations/requirements'
28
29
  autoload :Banner, 'zendesk_apps_support/validations/banner'
29
30
  end
31
+
32
+ class Manifest
33
+ autoload :Parameter, 'zendesk_apps_support/manifest/parameter'
34
+ autoload :NoOverrideHash, 'zendesk_apps_support/manifest/no_override_hash'
35
+ end
30
36
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zendesk_apps_support
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 3.0.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: 2016-08-03 00:00:00.000000000 Z
14
+ date: 2016-08-26 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: i18n
@@ -139,6 +139,20 @@ dependencies:
139
139
  - - "~>"
140
140
  - !ruby/object:Gem::Version
141
141
  version: 0.5.1
142
+ - !ruby/object:Gem::Dependency
143
+ name: faker
144
+ requirement: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - "~>"
147
+ - !ruby/object:Gem::Version
148
+ version: 1.6.6
149
+ type: :development
150
+ prerelease: false
151
+ version_requirements: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - "~>"
154
+ - !ruby/object:Gem::Version
155
+ version: 1.6.6
142
156
  description: Support to help you develop Zendesk Apps.
143
157
  email:
144
158
  - dev@zendesk.com
@@ -165,6 +179,9 @@ files:
165
179
  - lib/zendesk_apps_support/installation.rb
166
180
  - lib/zendesk_apps_support/installed.rb
167
181
  - lib/zendesk_apps_support/location.rb
182
+ - lib/zendesk_apps_support/manifest.rb
183
+ - lib/zendesk_apps_support/manifest/no_override_hash.rb
184
+ - lib/zendesk_apps_support/manifest/parameter.rb
168
185
  - lib/zendesk_apps_support/package.rb
169
186
  - lib/zendesk_apps_support/product.rb
170
187
  - lib/zendesk_apps_support/sass_functions.rb
@@ -198,7 +215,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
198
215
  version: 1.3.6
199
216
  requirements: []
200
217
  rubyforge_project:
201
- rubygems_version: 2.5.1
218
+ rubygems_version: 2.6.4
202
219
  signing_key:
203
220
  specification_version: 4
204
221
  summary: Support to help you develop Zendesk Apps.