Class: Deliver::UploadMetadata

Inherits:
Object
  • Object
show all
Defined in:
deliver/lib/deliver/upload_metadata.rb

Overview

upload description, rating, etc.

Constant Summary collapse

LOCALISED_VERSION_VALUES =

All the localised values attached to the version

[:description, :keywords, :release_notes, :support_url, :marketing_url, :promotional_text]
NON_LOCALISED_VERSION_VALUES =

Everything attached to the version but not being localised

[:copyright]
LOCALISED_APP_VALUES =

Localised app details values

[:name, :subtitle, :privacy_url, :apple_tv_privacy_policy]
NON_LOCALISED_APP_VALUES =

Non localized app details values

[:primary_category, :secondary_category,
:primary_first_sub_category, :primary_second_sub_category,
:secondary_first_sub_category, :secondary_second_sub_category]
TRADE_REPRESENTATIVE_CONTACT_INFORMATION_VALUES =

Trade Representative Contact Information values

{
    trade_representative_trade_name: :trade_name,
    trade_representative_first_name: :first_name,
    trade_representative_last_name: :last_name,
    trade_representative_address_line_1: :address_line1,
    trade_representative_address_line_2: :address_line2,
    trade_representative_address_line_3: :address_line3,
    trade_representative_city_name: :city_name,
    trade_representative_state: :state,
    trade_representative_country: :country,
    trade_representative_postal_code: :postal_code,
    trade_representative_phone_number: :phone_number,
    trade_representative_email: :email_address,
    trade_representative_is_displayed_on_app_store: :is_displayed_on_app_store
}
REVIEW_INFORMATION_VALUES =

Review information values

{
  review_first_name: :first_name,
  review_last_name: :last_name,
  review_phone_number: :phone_number,
  review_email: :email_address,
  review_demo_user: :demo_user,
  review_demo_password: :demo_password,
  review_notes: :notes
}
LOCALISED_LIVE_VALUES =

Localized app details values, that are editable in live state

[:description, :release_notes, :support_url, :marketing_url, :promotional_text, :privacy_url]
NON_LOCALISED_LIVE_VALUES =

Non localized app details values, that are editable in live state

[:copyright]
TRADE_REPRESENTATIVE_CONTACT_INFORMATION_DIR =

Directory name it contains trade representative contact information

"trade_representative_contact_information"
REVIEW_INFORMATION_DIR =

Directory name it contains review information

"review_information"
ALL_META_SUB_DIRS =
[TRADE_REPRESENTATIVE_CONTACT_INFORMATION_DIR, REVIEW_INFORMATION_DIR]

Instance Method Summary collapse

Instance Method Details

#assign_defaults(options) ⇒ Object

If the user is using the ‘default’ language, then assign values where they are needed



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'deliver/lib/deliver/upload_metadata.rb', line 176

def assign_defaults(options)
  # Normalizes languages keys from symbols to strings
  normalize_language_keys(options)

  # Build a complete list of the required languages
  enabled_languages = detect_languages(options)

  # Get all languages used in existing settings
  (LOCALISED_VERSION_VALUES + LOCALISED_APP_VALUES).each do |key|
    current = options[key]
    next unless current && current.kind_of?(Hash)
    current.each do |language, value|
      enabled_languages << language unless enabled_languages.include?(language)
    end
  end

  # Check folder list (an empty folder signifies a language is required)
  ignore_validation = options[:ignore_language_directory_validation]
  Loader.language_folders(options[:metadata_path], ignore_validation).each do |lang_folder|
    next unless File.directory?(lang_folder) # We don't want to read txt as they are non localised
    language = File.basename(lang_folder)
    enabled_languages << language unless enabled_languages.include?(language)
  end

  return unless enabled_languages.include?("default")
  UI.message("Detected languages: " + enabled_languages.to_s)

  (LOCALISED_VERSION_VALUES + LOCALISED_APP_VALUES).each do |key|
    current = options[key]
    next unless current && current.kind_of?(Hash)

    default = current["default"]
    next if default.nil?

    enabled_languages.each do |language|
      value = current[language]
      next unless value.nil?

      current[language] = default
    end
    current.delete("default")
  end
end

#detect_languages(options) ⇒ Object



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'deliver/lib/deliver/upload_metadata.rb', line 220

def detect_languages(options)
  # Build a complete list of the required languages
  enabled_languages = options[:languages] || []

  # Get all languages used in existing settings
  (LOCALISED_VERSION_VALUES + LOCALISED_APP_VALUES).each do |key|
    current = options[key]
    next unless current && current.kind_of?(Hash)
    current.each do |language, value|
      enabled_languages << language unless enabled_languages.include?(language)
    end
  end

  # Check folder list (an empty folder signifies a language is required)
  ignore_validation = options[:ignore_language_directory_validation]
  Loader.language_folders(options[:metadata_path], ignore_validation).each do |lang_folder|
    next unless File.directory?(lang_folder) # We don't want to read txt as they are non localised

    language = File.basename(lang_folder)
    enabled_languages << language unless enabled_languages.include?(language)
  end

  # Mapping to strings because :default symbol can be passed in
  enabled_languages
    .map(&:to_s)
    .uniq
end

#load_from_filesystem(options) ⇒ Object

Loads the metadata files and stores them into the options object



286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'deliver/lib/deliver/upload_metadata.rb', line 286

def load_from_filesystem(options)
  return if options[:skip_metadata]

  # Load localised data
  ignore_validation = options[:ignore_language_directory_validation]
  Loader.language_folders(options[:metadata_path], ignore_validation).each do |lang_folder|
    language = File.basename(lang_folder)
    (LOCALISED_VERSION_VALUES + LOCALISED_APP_VALUES).each do |key|
      path = File.join(lang_folder, "#{key}.txt")
      next unless File.exist?(path)

      UI.message("Loading '#{path}'...")
      options[key] ||= {}
      options[key][language] ||= File.read(path)
    end
  end

  # Load non localised data
  (NON_LOCALISED_VERSION_VALUES + NON_LOCALISED_APP_VALUES).each do |key|
    path = File.join(options[:metadata_path], "#{key}.txt")
    next unless File.exist?(path)

    UI.message("Loading '#{path}'...")
    options[key] ||= File.read(path)
  end

  # Load trade representative contact information
  options[:trade_representative_contact_information] ||= {}
  TRADE_REPRESENTATIVE_CONTACT_INFORMATION_VALUES.values.each do |option_name|
    path = File.join(options[:metadata_path], TRADE_REPRESENTATIVE_CONTACT_INFORMATION_DIR, "#{option_name}.txt")
    next unless File.exist?(path)
    next if options[:trade_representative_contact_information][option_name].to_s.length > 0

    UI.message("Loading '#{path}'...")
    options[:trade_representative_contact_information][option_name] ||= File.read(path)
  end

  # Load review information
  options[:app_review_information] ||= {}
  REVIEW_INFORMATION_VALUES.values.each do |option_name|
    path = File.join(options[:metadata_path], REVIEW_INFORMATION_DIR, "#{option_name}.txt")
    next unless File.exist?(path)
    next if options[:app_review_information][option_name].to_s.length > 0

    UI.message("Loading '#{path}'...")
    options[:app_review_information][option_name] ||= File.read(path)
  end
end

#upload(options) ⇒ Object

Make sure to call ‘load_from_filesystem` before calling upload



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'deliver/lib/deliver/upload_metadata.rb', line 67

def upload(options)
  return if options[:skip_metadata]
  # it is not possible to create new languages, because
  # :keywords is not write-able on published versions
  # therefore skip it.
  verify_available_languages!(options) unless options[:edit_live]

  app = options[:app]

  details = app.details
  if options[:edit_live]
    # not all values are editable when using live_version
    v = app.live_version(platform: options[:platform])
    localised_options = LOCALISED_LIVE_VALUES
    non_localised_options = NON_LOCALISED_LIVE_VALUES

    if v.nil?
      UI.message("Couldn't find live version, editing the current version on App Store Connect instead")
      v = app.edit_version(platform: options[:platform])
      # we don't want to update the localised_options and non_localised_options
      # as we also check for `options[:edit_live]` at other areas in the code
      # by not touching those 2 variables, deliver is more consistent with what the option says
      # in the documentation
    end
  else
    v = app.edit_version(platform: options[:platform])
    localised_options = (LOCALISED_VERSION_VALUES + LOCALISED_APP_VALUES)
    non_localised_options = (NON_LOCALISED_VERSION_VALUES + NON_LOCALISED_APP_VALUES)
  end

  individual = options[:individual_metadata_items] || []
  localised_options.each do |key|
    current = options[key]
    next unless current

    unless current.kind_of?(Hash)
      UI.error("Error with provided '#{key}'. Must be a hash, the key being the language.")
      next
    end

    current.each do |language, value|
      next unless value.to_s.length > 0
      strip_value = value.to_s.strip
      if individual.include?(key.to_s)
        upload_individual_item(app, v, language, key, strip_value)
      else
        v.send(key)[language] = strip_value if LOCALISED_VERSION_VALUES.include?(key)
        details.send(key)[language] = strip_value if LOCALISED_APP_VALUES.include?(key)
      end
    end
  end

  non_localised_options.each do |key|
    current = options[key].to_s.strip
    next unless current.to_s.length > 0
    v.send("#{key}=", current) if NON_LOCALISED_VERSION_VALUES.include?(key)
    details.send("#{key}=", current) if NON_LOCALISED_APP_VALUES.include?(key)
  end

  v.release_on_approval = options[:automatic_release]
  v.auto_release_date = options[:auto_release_date] unless options[:auto_release_date].nil?
  v.toggle_phased_release(enabled: !!options[:phased_release]) unless options[:phased_release].nil?

  set_trade_representative_contact_information(v, options)
  set_review_information(v, options)
  set_app_rating(v, options)
  v.ratings_reset = options[:reset_ratings] unless options[:reset_ratings].nil?

  set_review_attachment_file(v, options)

  Helper.show_loading_indicator("Uploading metadata to App Store Connect")
  v.save!
  Helper.hide_loading_indicator
  begin
    details.save!
    UI.success("Successfully uploaded set of metadata to App Store Connect")
  rescue Spaceship::TunesClient::ITunesConnectError => e
    # This makes sure that we log invalid app names as user errors
    # If another string needs to be checked here we should
    # figure out a more generic way to handle these cases.
    if e.message.include?('App Name cannot be longer than 50 characters') || e.message.include?('The app name you entered is already being used')
      UI.error("Error in app name.  Try using 'individual_metadata_items' to identify the problem language.")
      UI.user_error!(e.message)
    else
      raise e
    end
  end
end

#upload_individual_item(app, version, language, key, value) ⇒ Object

Uploads metadata individually by language to help identify exactly which items have issues



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'deliver/lib/deliver/upload_metadata.rb', line 157

def upload_individual_item(app, version, language, key, value)
  details = app.details
  version.send(key)[language] = value if LOCALISED_VERSION_VALUES.include?(key)
  details.send(key)[language] = value if LOCALISED_APP_VALUES.include?(key)
  Helper.show_loading_indicator("Uploading #{language} #{key} to App Store Connect")
  version.save!
  Helper.hide_loading_indicator
  begin
    details.save!
    UI.success("Successfully uploaded #{language} #{key} to App Store Connect")
  rescue Spaceship::TunesClient::ITunesConnectError => e
    UI.error("Error in #{language} #{key}: \n#{value}")
    UI.error(e.message) # Don't use user_error to allow all values to get checked
  end
end

#verify_available_languages!(options) ⇒ Object

Makes sure all languages we need are actually created



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'deliver/lib/deliver/upload_metadata.rb', line 249

def verify_available_languages!(options)
  return if options[:skip_metadata]

  # Collect all languages we need
  # We only care about languages from user provided values
  # as the other languages are on iTC already anyway
  v = options[:app].edit_version(platform: options[:platform])
  UI.user_error!("Could not find a version to edit for app '#{options[:app].name}', the app metadata is read-only currently") unless v

  enabled_languages = options[:languages] || []
  LOCALISED_VERSION_VALUES.each do |key|
    current = options[key]
    next unless current && current.kind_of?(Hash)
    current.each do |language, value|
      language = language.to_s
      enabled_languages << language unless enabled_languages.include?(language)
    end
  end

  # Reject "default" language from getting enabled
  # because "default" is not an iTC language
  enabled_languages = enabled_languages.reject do |lang|
    lang == "default"
  end.uniq

  if enabled_languages.count > 0
    v.create_languages(enabled_languages)
    lng_text = "language"
    lng_text += "s" if enabled_languages.count != 1
    Helper.show_loading_indicator("Activating #{lng_text} #{enabled_languages.join(', ')}...")
    v.save!
    Helper.hide_loading_indicator
  end
  true
end