contentful-importer 0.0.1

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.
Files changed (112) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +38 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +8 -0
  5. data/CHANGELOG.md +4 -0
  6. data/Gemfile +7 -0
  7. data/Gemfile.lock +87 -0
  8. data/LICENSE +22 -0
  9. data/README.md +327 -0
  10. data/Rakefile +7 -0
  11. data/bin/contentful-importer +85 -0
  12. data/contentful_importer.gemspec +38 -0
  13. data/example_settings/contentful_model.json +288 -0
  14. data/example_settings/contentful_structure.json +78 -0
  15. data/example_settings/files_meaning.txt +15 -0
  16. data/example_settings/settings.yml +16 -0
  17. data/lib/cli.rb +13 -0
  18. data/lib/configuration.rb +36 -0
  19. data/lib/converters/content_types_structure_creator.rb +60 -0
  20. data/lib/converters/contentful_model_to_json.rb +109 -0
  21. data/lib/importer/data_organizer.rb +117 -0
  22. data/lib/importer/mime_content_type.rb +564 -0
  23. data/lib/importer/parallel_importer.rb +429 -0
  24. data/lib/json_schema_validator.rb +64 -0
  25. data/lib/migrator.rb +39 -0
  26. data/lib/version.rb +3 -0
  27. data/spec/fixtures/import_files/assets/image/image_1.json +9 -0
  28. data/spec/fixtures/import_files/assets/image/image_2.json +9 -0
  29. data/spec/fixtures/import_files/assets/image/image_3.json +9 -0
  30. data/spec/fixtures/import_files/assets/image/image_4.json +9 -0
  31. data/spec/fixtures/import_files/collections/job_skills.json +13 -0
  32. data/spec/fixtures/import_files/collections/jobs.json +37 -0
  33. data/spec/fixtures/import_files/collections/profile.json +19 -0
  34. data/spec/fixtures/import_files/collections/user.json +36 -0
  35. data/spec/fixtures/import_files/entries/job_skills/job_skills_1.json +7 -0
  36. data/spec/fixtures/import_files/entries/job_skills/job_skills_10.json +7 -0
  37. data/spec/fixtures/import_files/entries/job_skills/job_skills_2.json +7 -0
  38. data/spec/fixtures/import_files/entries/job_skills/job_skills_3.json +7 -0
  39. data/spec/fixtures/import_files/entries/job_skills/job_skills_4.json +7 -0
  40. data/spec/fixtures/import_files/entries/job_skills/job_skills_5.json +7 -0
  41. data/spec/fixtures/import_files/entries/job_skills/job_skills_6.json +7 -0
  42. data/spec/fixtures/import_files/entries/job_skills/job_skills_7.json +7 -0
  43. data/spec/fixtures/import_files/entries/job_skills/job_skills_8.json +7 -0
  44. data/spec/fixtures/import_files/entries/job_skills/job_skills_9.json +7 -0
  45. data/spec/fixtures/import_files/entries/jobs/jobs_1.json +56 -0
  46. data/spec/fixtures/import_files/entries/jobs/jobs_2.json +55 -0
  47. data/spec/fixtures/import_files/entries/jobs/jobs_4.json +49 -0
  48. data/spec/fixtures/import_files/entries/profile/profile_1.json +12 -0
  49. data/spec/fixtures/import_files/entries/profile/profile_2.json +12 -0
  50. data/spec/fixtures/import_files/entries/user/user_1.json +24 -0
  51. data/spec/fixtures/import_files/entries/user/user_2.json +20 -0
  52. data/spec/fixtures/import_files/logs/log_published_assets.csv +4 -0
  53. data/spec/fixtures/import_files/logs/log_published_entries.csv +23 -0
  54. data/spec/fixtures/import_files/logs/success_assets.csv +4 -0
  55. data/spec/fixtures/import_files/logs/success_published_assets.csv +0 -0
  56. data/spec/fixtures/import_files/logs/success_published_entries.csv +22 -0
  57. data/spec/fixtures/import_files/logs/success_thread_0.csv +12 -0
  58. data/spec/fixtures/import_files/logs/success_thread_1.csv +11 -0
  59. data/spec/fixtures/import_files/threads/0/1TVvxCqoRq0qUYAOQuOqys_2.json +20 -0
  60. data/spec/fixtures/import_files/threads/0/2soCP557HGKoOOK0SqmMOm_1.json +7 -0
  61. data/spec/fixtures/import_files/threads/0/2soCP557HGKoOOK0SqmMOm_10.json +7 -0
  62. data/spec/fixtures/import_files/threads/0/2soCP557HGKoOOK0SqmMOm_2.json +7 -0
  63. data/spec/fixtures/import_files/threads/0/2soCP557HGKoOOK0SqmMOm_3.json +7 -0
  64. data/spec/fixtures/import_files/threads/0/2soCP557HGKoOOK0SqmMOm_4.json +7 -0
  65. data/spec/fixtures/import_files/threads/0/2soCP557HGKoOOK0SqmMOm_5.json +7 -0
  66. data/spec/fixtures/import_files/threads/0/2soCP557HGKoOOK0SqmMOm_6.json +7 -0
  67. data/spec/fixtures/import_files/threads/0/2soCP557HGKoOOK0SqmMOm_7.json +7 -0
  68. data/spec/fixtures/import_files/threads/0/6H6pGAV1PUsuoAW26Iu48W_1.json +9 -0
  69. data/spec/fixtures/import_files/threads/0/6H6pGAV1PUsuoAW26Iu48W_2.json +9 -0
  70. data/spec/fixtures/import_files/threads/0/6H6pGAV1PUsuoAW26Iu48W_3.json +9 -0
  71. data/spec/fixtures/import_files/threads/0/6H6pGAV1PUsuoAW26Iu48W_4.json +9 -0
  72. data/spec/fixtures/import_files/threads/0/6H6pGAV1PUsuoAW26Iu48W_5.json +9 -0
  73. data/spec/fixtures/import_files/threads/1/1TVvxCqoRq0qUYAOQuOqys_1.json +24 -0
  74. data/spec/fixtures/import_files/threads/1/1TVvxCqoRq0qUYAOQuOqys_2.json +20 -0
  75. data/spec/fixtures/import_files/threads/1/2soCP557HGKoOOK0SqmMOm_6.json +7 -0
  76. data/spec/fixtures/import_files/threads/1/2soCP557HGKoOOK0SqmMOm_7.json +7 -0
  77. data/spec/fixtures/import_files/threads/1/2soCP557HGKoOOK0SqmMOm_8.json +7 -0
  78. data/spec/fixtures/import_files/threads/1/2soCP557HGKoOOK0SqmMOm_9.json +7 -0
  79. data/spec/fixtures/import_files/threads/1/4L1bg4WQ5aWQMiE82ouag_1.json +56 -0
  80. data/spec/fixtures/import_files/threads/1/4L1bg4WQ5aWQMiE82ouag_2.json +55 -0
  81. data/spec/fixtures/import_files/threads/1/4L1bg4WQ5aWQMiE82ouag_4.json +49 -0
  82. data/spec/fixtures/import_files/threads/1/4WFZh4MwC4Mc0EQWAeOY8A_1.json +12 -0
  83. data/spec/fixtures/import_files/threads/1/4WFZh4MwC4Mc0EQWAeOY8A_2.json +12 -0
  84. data/spec/fixtures/import_files/threads/assets/0/image_1.json +9 -0
  85. data/spec/fixtures/import_files/threads/assets/0/image_2.json +9 -0
  86. data/spec/fixtures/import_files/threads/assets/0/image_3.json +9 -0
  87. data/spec/fixtures/import_files/threads/assets/0/image_4.json +9 -0
  88. data/spec/fixtures/settings/contentful_model.json +252 -0
  89. data/spec/fixtures/settings/contentful_structure.json +73 -0
  90. data/spec/fixtures/settings/contentful_structure_test.json +66 -0
  91. data/spec/fixtures/settings/settings.yml +12 -0
  92. data/spec/fixtures/vcr_cassettes/create_asset.yml +621 -0
  93. data/spec/fixtures/vcr_cassettes/create_entry.yml +122 -0
  94. data/spec/fixtures/vcr_cassettes/create_space.yml +87 -0
  95. data/spec/fixtures/vcr_cassettes/import_assets.yml +2822 -0
  96. data/spec/fixtures/vcr_cassettes/import_content_types.yml +1915 -0
  97. data/spec/fixtures/vcr_cassettes/import_entries.yml +5066 -0
  98. data/spec/fixtures/vcr_cassettes/import_entry.yml +363 -0
  99. data/spec/fixtures/vcr_cassettes/invalid_credentials.yml +69 -0
  100. data/spec/fixtures/vcr_cassettes/publish_an_entry.yml +214 -0
  101. data/spec/fixtures/vcr_cassettes/publish_asset.yml +895 -0
  102. data/spec/fixtures/vcr_cassettes/publish_entries.yml +5121 -0
  103. data/spec/fixtures/vcr_cassettes/valid_credentials.yml +7360 -0
  104. data/spec/lib/configuration_spec.rb +22 -0
  105. data/spec/lib/importer/parallel_importer_spec.rb +161 -0
  106. data/spec/lib/json_schema_validator_spec.rb +63 -0
  107. data/spec/lib/migrator_spec.rb +99 -0
  108. data/spec/spec_helper.rb +12 -0
  109. data/spec/support/db_rows_json.rb +9 -0
  110. data/spec/support/shared_configuration.rb +22 -0
  111. data/spec/support/vcr.rb +16 -0
  112. metadata +436 -0
@@ -0,0 +1,429 @@
1
+ require_relative 'mime_content_type'
2
+ require_relative 'data_organizer'
3
+ require 'contentful/management'
4
+ require 'csv'
5
+ require 'yaml'
6
+ require 'api_cache'
7
+
8
+ module Contentful
9
+ class ParallelImporter
10
+
11
+ Encoding.default_external = 'utf-8'
12
+
13
+ attr_reader :space, :config, :logger, :data_organizer
14
+ attr_accessor :content_type
15
+
16
+ def initialize(settings)
17
+ @config = settings
18
+ @logger = Logger.new(STDOUT)
19
+ @data_organizer = Contentful::DataOrganizer.new(@config)
20
+ Contentful::Management::Client.new(config.config['access_token'], default_locale: config.config['default_locale'] || 'en-US')
21
+ end
22
+
23
+ def create_contentful_model(space)
24
+ initialize_space(space)
25
+ import_content_types
26
+ end
27
+
28
+ def import_data(threads)
29
+ clean_threads_dir_before_import(threads)
30
+ data_organizer.execute(threads)
31
+ import_in_threads
32
+ end
33
+
34
+ def test_credentials
35
+ spaces = Contentful::Management::Space.all
36
+ if spaces.is_a? Contentful::Management::Array
37
+ logger.info 'Contentful Management API credentials: OK'
38
+ end
39
+ rescue NoMethodError => _error
40
+ logger.info 'Contentful Management API credentials: INVALID (check README)'
41
+ end
42
+
43
+ def number_of_threads
44
+ number_of_threads = 0
45
+ Dir.glob("#{config.threads_dir}/*") do |thread|
46
+ number_of_threads += 1 if File.basename(thread).size == 1
47
+ end
48
+ number_of_threads
49
+ end
50
+
51
+ def import_in_threads
52
+ threads = []
53
+ number_of_threads.times do |thread_id|
54
+ threads << Thread.new do
55
+ self.class.new(config).import_entries("#{config.threads_dir}/#{thread_id}", config.space_id)
56
+ end
57
+ end
58
+ threads.each do |thread|
59
+ thread.join
60
+ end
61
+ end
62
+
63
+ def import_entries(path, space_id)
64
+ log_file_name = "success_thread_#{File.basename(path)}"
65
+ create_log_file(log_file_name)
66
+ load_log_files
67
+ Dir.glob("#{path}/*.json") do |entry_path|
68
+ content_type_id = File.basename(entry_path).match(/(.+)_\d+/)[1]
69
+ entry_file_name = File.basename(entry_path)
70
+ import_entry(entry_path, space_id, content_type_id, log_file_name) unless config.imported_entries.flatten.include?(entry_file_name)
71
+ end
72
+ end
73
+
74
+ def import_only_assets
75
+ create_log_file('success_assets')
76
+ assets_ids = Set.new(CSV.read("#{config.data_dir}/logs/success_assets.csv", 'r'))
77
+ Dir.glob("#{config.assets_dir}/**/*json") do |file_path|
78
+ asset_attributes = JSON.parse(File.read(file_path))
79
+ if asset_url_param_start_with_http?(asset_attributes) && asset_not_imported_yet?(asset_attributes, assets_ids)
80
+ import_asset(asset_attributes)
81
+ end
82
+ end
83
+ end
84
+
85
+ def import_asset(asset_attributes)
86
+ logger.info "Import asset - #{asset_attributes['id']} "
87
+ asset_title = asset_attributes['name'].present? ? asset_attributes['name'] : asset_attributes['id']
88
+ asset_file = create_asset_file(asset_title, asset_attributes)
89
+ space = Contentful::Management::Space.find(config.config['space_id'])
90
+ asset = space.assets.create(id: "#{asset_attributes['id']}", title: "#{asset_title}", description: '', file: asset_file)
91
+ asset_status(asset, asset_attributes)
92
+ end
93
+
94
+ def asset_url_param_start_with_http?(asset_attributes)
95
+ asset_attributes['url'] && asset_attributes['url'].start_with?('http')
96
+ end
97
+
98
+ def asset_not_imported_yet?(asset_attributes, assets_ids)
99
+ !assets_ids.to_a.flatten.include?(asset_attributes['id'])
100
+ end
101
+
102
+ def create_asset_file(asset_title, params)
103
+ Contentful::Management::File.new.tap do |file|
104
+ file.properties[:contentType] = file_content_type(params)
105
+ file.properties[:fileName] = asset_title
106
+ file.properties[:upload] = params['url']
107
+ end
108
+ end
109
+
110
+ def asset_status(asset, asset_attributes)
111
+ if asset.is_a?(Contentful::Management::Asset)
112
+ logger.info "Process asset - #{asset.id} "
113
+ asset.process_file
114
+ CSV.open("#{config.log_files_dir}/success_assets.csv", 'a') { |csv| csv << [asset.id] }
115
+ else
116
+ logger.info "Error - #{asset.message} "
117
+ CSV.open("#{config.log_files_dir}/failure_assets.csv", 'a') { |csv| csv << [asset_attributes['id']] }
118
+ end
119
+ end
120
+
121
+ def publish_entries_in_threads
122
+ threads =[]
123
+ number_of_threads.times do |thread_id|
124
+ threads << Thread.new do
125
+ self.class.new(config).publish_all_entries("#{config.threads_dir}/#{thread_id}")
126
+ end
127
+ end
128
+ threads.each do |thread|
129
+ thread.join
130
+ end
131
+ end
132
+
133
+ def publish_assets_in_threads(number_of_threads)
134
+ clean_assets_threads_dir_before_publish(number_of_threads)
135
+ data_organizer.split_assets_to_threads(number_of_threads)
136
+ threads =[]
137
+ number_of_threads.times do |thread_id|
138
+ threads << Thread.new do
139
+ self.class.new(config).publish_assets("#{config.threads_dir}/assets/#{thread_id}")
140
+ end
141
+ end
142
+ threads.each do |thread|
143
+ thread.join
144
+ end
145
+ end
146
+
147
+ def publish_assets(thread_dir)
148
+ create_log_file('success_published_assets')
149
+ config.published_assets << CSV.read("#{config.log_files_dir}/success_published_assets.csv", 'r').flatten
150
+ Dir.glob("#{thread_dir}/*json") do |asset_file|
151
+ asset_id = JSON.parse(File.read(asset_file))['id']
152
+ publish_asset(asset_id) unless config.published_assets.flatten.include?(asset_id)
153
+ end
154
+ end
155
+
156
+ def publish_asset(asset_id)
157
+ logger.info "Publish an Asset - ID: #{asset_id}"
158
+ asset = Contentful::Management::Asset.find(config.config['space_id'], asset_id).publish
159
+ publish_status(asset, asset_id, 'published_assets')
160
+ end
161
+
162
+ def publish_all_entries(thread_dir)
163
+ create_log_file('success_published_entries')
164
+ config.published_entries << CSV.read("#{config.log_files_dir}/success_published_entries.csv", 'r').flatten
165
+ Dir.glob("#{thread_dir}/*json") do |entry_file|
166
+ entry_id = JSON.parse(File.read(entry_file))['id']
167
+ publish_entry(entry_id) unless config.published_entries.flatten.include?(entry_id)
168
+ end
169
+ end
170
+
171
+ def publish_entry(entry_id)
172
+ logger.info "Publish entries for #{entry_id}."
173
+ entry = Contentful::Management::Entry.find(config.config['space_id'], entry_id).publish
174
+ publish_status(entry, entry_id, 'published_entries')
175
+ end
176
+
177
+ private
178
+
179
+ def initialize_space(space)
180
+ fail 'You need to specify \'--space_id\' argument to find an existing Space or \'--space_name\' to create a new Space.' if space[:space_id].nil? && [:space_name].nil?
181
+ @space = space[:space_id].present? ? Contentful::Management::Space.find(space[:space_id]) : create_space(space[:space_name])
182
+ end
183
+
184
+ def create_space(name_space)
185
+ logger.info "Creating a space with name: #{name_space}"
186
+ new_space = Contentful::Management::Space.create(name: name_space, organization_id: config.config['organization_id'])
187
+ logger.info "Space was created successfully! Space id: #{new_space.id}"
188
+ new_space
189
+ end
190
+
191
+ def import_content_types
192
+ Dir.glob("#{config.collections_dir}/*json") do |file_path|
193
+ collection_attributes = JSON.parse(File.read(file_path))
194
+ content_type = create_new_content_type(space, collection_attributes)
195
+ logger.info "Importing content_type: #{content_type.name}"
196
+ create_content_type_fields(collection_attributes, content_type)
197
+ content_type.update(displayField: collection_attributes['displayField']) if collection_attributes['displayField']
198
+ active_status(content_type.activate)
199
+ end
200
+ end
201
+
202
+ def get_id(params)
203
+ File.basename(params['id'] || params['url'])
204
+ end
205
+
206
+ def create_content_type_fields(collection_attributes, content_type)
207
+ fields = collection_attributes['fields'].each_with_object([]) do |field, fields|
208
+ fields << create_field(field)
209
+ end
210
+ content_type.fields = fields
211
+ content_type.save
212
+ end
213
+
214
+ def import_entry(file_path, space_id, content_type_id, log_file)
215
+ entry_attributes = JSON.parse(File.read(file_path))
216
+ logger.info "Creating entry: #{entry_attributes['id']}."
217
+ entry_params = create_entry_parameters(content_type_id, entry_attributes, space_id)
218
+ content_type = content_type(content_type_id, space_id)
219
+ entry = content_type.entries.create(entry_params)
220
+ import_status(entry, file_path, log_file)
221
+ end
222
+
223
+ def create_entry_parameters(content_type_id, entry_attributes, space_id)
224
+ entry_attributes.each_with_object({}) do |(attr, value), entry_params|
225
+ next if attr.start_with?('@')
226
+ entry_param = if value.is_a? Hash
227
+ parse_attributes_from_hash(value, space_id, content_type_id)
228
+ elsif value.is_a? Array
229
+ parse_attributes_from_array(value, space_id, content_type_id)
230
+ else
231
+ value
232
+ end
233
+ entry_params[attr.to_sym] = entry_param unless validate_param(entry_param)
234
+ end
235
+ end
236
+
237
+ def parse_attributes_from_hash(params, space_id, content_type_id)
238
+ type = params['type']
239
+ if type
240
+ case type
241
+ when 'Location'
242
+ create_location_file(params)
243
+ when 'File'
244
+ create_asset(space_id, params)
245
+ else
246
+ create_entry(params, space_id, content_type_id)
247
+ end
248
+ else
249
+ params
250
+ end
251
+ end
252
+
253
+ def parse_attributes_from_array(params, space_id, content_type_id)
254
+ params.each_with_object([]) do |attr, array_attributes|
255
+ value = if attr['type'].present? && attr['type'] != 'File'
256
+ create_entry(attr, space_id, content_type_id)
257
+ elsif attr['type'] == 'File'
258
+ create_asset(space_id, attr)
259
+ else
260
+ attr
261
+ end
262
+ array_attributes << value unless value.nil?
263
+ end
264
+ end
265
+
266
+ def import_status(entry, file_path, log_file)
267
+ if entry.is_a? Contentful::Management::Entry
268
+ entry_file_name = File.basename(file_path)
269
+ logger.info 'Imported successfully!'
270
+ CSV.open("#{config.log_files_dir}/#{log_file}.csv", 'a') { |csv| csv << [entry_file_name] }
271
+ else
272
+ logger.info "### Failure! - #{entry.message} - #{entry.response.raw}###"
273
+ failure_filename = log_file.match(/(thread_\d)/)[1]
274
+ CSV.open("#{config.log_files_dir}/failure_#{failure_filename}.csv", 'a') { |csv| csv << [file_path, entry.message, entry.response.raw] }
275
+ end
276
+ end
277
+
278
+ def content_type(content_type_id, space_id)
279
+ @content_type = APICache.get("content_type_#{content_type_id}", :period => -5) do
280
+ Contentful::Management::ContentType.find(space_id, content_type_id)
281
+ end
282
+ end
283
+
284
+ def create_entry(params, space_id, content_type_id)
285
+ entry_id = get_id(params)
286
+ content_type = content_type(content_type_id, space_id)
287
+ content_type.entries.new.tap do |entry|
288
+ entry.id = entry_id
289
+ end
290
+ end
291
+
292
+ def create_asset(space_id, params)
293
+ if params['id']
294
+ space = Contentful::Management::Space.find(space_id)
295
+ found_asset = space.assets.find(params['id'])
296
+ asset = found_asset.is_a?(Contentful::Management::Asset) ? found_asset : initialize_asset_file(params)
297
+ asset
298
+ end
299
+ end
300
+
301
+ def initialize_asset_file(params)
302
+ Contentful::Management::Asset.new.tap do |asset|
303
+ asset.id = params['id']
304
+ asset.link_type = 'Asset'
305
+ end
306
+ end
307
+
308
+ def create_location_file(params)
309
+ Contentful::Management::Location.new.tap do |file|
310
+ file.lat = params['lat']
311
+ file.lon = params['lng']
312
+ end
313
+ end
314
+
315
+ def create_field(field)
316
+ field_params = {id: field['id'], name: field['name'], required: field['required']}
317
+ field_params.merge!(additional_field_params(field))
318
+ logger.info "Creating field: #{field_params[:type]}"
319
+ create_content_type_field(field_params)
320
+ end
321
+
322
+ def create_content_type_field(field_params)
323
+ Contentful::Management::Field.new.tap do |field|
324
+ field.id = field_params[:id]
325
+ field.name = field_params[:name]
326
+ field.type = field_params[:type]
327
+ field.link_type = field_params[:link_type]
328
+ field.required = field_params[:required]
329
+ field.items = field_params[:items]
330
+ end
331
+ end
332
+
333
+ def active_status(ct_object)
334
+ if ct_object.is_a? Contentful::Management::Error
335
+ logger.info "### Failure! - #{ct_object.message} ! ###"
336
+ else
337
+ logger.info 'Successfully activated!'
338
+ end
339
+ end
340
+
341
+ def publish_status(ct_object, object_id, log_file_name)
342
+ if ct_object.is_a? Contentful::Management::Error
343
+ logger.info "### Failure! - #{ct_object.message} ! ###"
344
+ CSV.open("#{config.log_files_dir}/failure_#{log_file_name}.csv", 'a') { |csv| csv << [object_id] }
345
+ else
346
+ logger.info 'Successfully activated!'
347
+ CSV.open("#{config.log_files_dir}/success_#{log_file_name}.csv", 'a') { |csv| csv << [ct_object.id] }
348
+ end
349
+ end
350
+
351
+ def additional_field_params(field)
352
+ field_type = field['type']
353
+ if field_type == 'Entry' || field_type == 'Asset'
354
+ {type: 'Link', link_type: field_type}
355
+ elsif field_type == 'Array'
356
+ {type: 'Array', items: create_array_field(field)}
357
+ else
358
+ {type: field_type}
359
+ end
360
+ end
361
+
362
+ def validate_param(param)
363
+ if param.is_a? Array
364
+ param.empty?
365
+ else
366
+ param.nil?
367
+ end
368
+ end
369
+
370
+ def create_new_content_type(space, collection_attributes)
371
+ space.content_types.new.tap do |content_type|
372
+ content_type.id = collection_attributes['id']
373
+ content_type.name = collection_attributes['name']
374
+ content_type.description = collection_attributes['description']
375
+ end
376
+ end
377
+
378
+ def file_content_type(params)
379
+ MimeContentType::EXTENSION_LIST[File.extname(params['url'])]
380
+ end
381
+
382
+ def format_json(item)
383
+ JSON.pretty_generate(JSON.parse(item.to_json))
384
+ end
385
+
386
+ def create_array_field(params)
387
+ Contentful::Management::Field.new.tap do |field|
388
+ field.type = params['link'] || 'Link'
389
+ field.link_type = params['link_type']
390
+ end
391
+ end
392
+
393
+ def clean_threads_dir_before_import(threads)
394
+ threads.times do |thread|
395
+ if File.directory?("#{config.threads_dir}/#{thread}")
396
+ logger.info "Remove directory threads/#{thread} from #{config.threads_dir} path."
397
+ FileUtils.rm_r("#{config.threads_dir}/#{thread}")
398
+ end
399
+ end
400
+ end
401
+
402
+ def clean_assets_threads_dir_before_publish(threads)
403
+ threads.times do |thread|
404
+ if File.directory?("#{config.threads_dir}/assets/#{thread}")
405
+ logger.info "Remove directory threads/#{thread} from #{config.threads_dir}/assets path."
406
+ FileUtils.rm_r("#{config.threads_dir}/assets/#{thread}")
407
+ end
408
+ end
409
+ end
410
+
411
+ def create_directory(path)
412
+ FileUtils.mkdir_p(path) unless File.directory?(path)
413
+ end
414
+
415
+ def create_log_file(path)
416
+ create_directory("#{config.data_dir}/logs")
417
+ File.open("#{config.data_dir}/logs/#{path}.csv", 'a') { |file| file.write('') }
418
+ end
419
+
420
+ def load_log_files
421
+ Dir.glob("#{config.log_files_dir}/*.csv") do |log_files|
422
+ file_name = File.basename(log_files)
423
+ imported_ids = CSV.read(log_files, 'r').flatten
424
+ config.imported_entries << imported_ids if file_name.start_with?('success_thread') && !config.imported_entries.include?(imported_ids)
425
+ end
426
+ end
427
+
428
+ end
429
+ end
@@ -0,0 +1,64 @@
1
+ require 'json-schema'
2
+
3
+ module Contentful
4
+ class JsonSchemaValidator
5
+
6
+ attr_reader :config, :logger
7
+
8
+ def initialize(configuration)
9
+ @config = configuration
10
+ @logger = Logger.new(STDOUT)
11
+ end
12
+
13
+ def validate_schemas
14
+ Dir.glob("#{config.collections_dir}/*") do |content_type_file|
15
+ validate_schema(content_type_file)
16
+ end
17
+ end
18
+
19
+ def validate_schema(content_type_file)
20
+ schema = parse_content_type_schema(JSON.parse(File.read(content_type_file)))
21
+ content_type_filename = File.basename(content_type_file, '.*')
22
+ validate_entry(content_type_filename, schema)
23
+ end
24
+
25
+ def validate_entry(content_type_filename, schema)
26
+ Dir.glob("#{config.entries_dir}/#{content_type_filename}/*") do |entry_file|
27
+ entry_schema = JSON.parse(File.read(entry_file))
28
+ begin
29
+ JSON::Validator.validate!(schema, entry_schema)
30
+ rescue JSON::Schema::ValidationError => error
31
+ logger.info "#{error.message}! Path to invalid entry: #{entry_file}"
32
+ end
33
+ end
34
+ end
35
+
36
+ def parse_content_type_schema(ct_file)
37
+ new_hash = base_schema_format
38
+ ct_file['fields'].each do |key|
39
+ type = convert_type(key['type'])
40
+ new_hash['properties'].merge!({key['id'] => {'type' => type}})
41
+ end
42
+ new_hash
43
+ end
44
+
45
+ def base_schema_format
46
+ {'type' => 'object', 'properties' => {}}
47
+ end
48
+
49
+ def convert_type(type)
50
+ case type
51
+ when 'Text', 'Date', 'Symbol'
52
+ 'string'
53
+ when 'Number'
54
+ 'float'
55
+ when 'Asset', 'Entry'
56
+ 'object'
57
+ else
58
+ type.downcase
59
+ end
60
+ end
61
+
62
+ end
63
+ end
64
+