database-exporter 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 (79) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +39 -0
  3. data/.travis.yml +6 -0
  4. data/CHANGELOG.md +4 -0
  5. data/Gemfile +7 -0
  6. data/Gemfile.lock +74 -0
  7. data/LICENSE +22 -0
  8. data/README.md +682 -0
  9. data/Rakefile +7 -0
  10. data/bin/database-exporter +48 -0
  11. data/database_exporter.gemspec +34 -0
  12. data/example_data/contentful_model.json +316 -0
  13. data/example_data/contentful_structure.json +89 -0
  14. data/example_data/example_settings.yml +25 -0
  15. data/example_data/mapping.json +119 -0
  16. data/lib/cli.rb +13 -0
  17. data/lib/configuration.rb +69 -0
  18. data/lib/converters/content_types_structure_creator.rb +58 -0
  19. data/lib/converters/contentful_model_to_json.rb +78 -0
  20. data/lib/database/export.rb +74 -0
  21. data/lib/database/modules/json_export.rb +79 -0
  22. data/lib/database/modules/relations_export.rb +270 -0
  23. data/lib/database/modules/utils.rb +20 -0
  24. data/lib/migrator.rb +29 -0
  25. data/lib/version.rb +3 -0
  26. data/spec/fixtures/database/data/assets/image/image_1.json +9 -0
  27. data/spec/fixtures/database/data/assets/image/image_2.json +9 -0
  28. data/spec/fixtures/database/data/assets/image/image_3.json +9 -0
  29. data/spec/fixtures/database/data/assets/image/image_4.json +9 -0
  30. data/spec/fixtures/database/data/collections/comment.json +18 -0
  31. data/spec/fixtures/database/data/collections/job_skills.json +13 -0
  32. data/spec/fixtures/database/data/collections/jobs.json +44 -0
  33. data/spec/fixtures/database/data/collections/profile.json +19 -0
  34. data/spec/fixtures/database/data/collections/user.json +36 -0
  35. data/spec/fixtures/database/data/entries/comment/comment_1.json +9 -0
  36. data/spec/fixtures/database/data/entries/comment/comment_2.json +9 -0
  37. data/spec/fixtures/database/data/entries/comment/comment_3.json +9 -0
  38. data/spec/fixtures/database/data/entries/comment/comment_4.json +9 -0
  39. data/spec/fixtures/database/data/entries/comment/comment_5.json +9 -0
  40. data/spec/fixtures/database/data/entries/job_skills/job_skills_1.json +7 -0
  41. data/spec/fixtures/database/data/entries/job_skills/job_skills_10.json +7 -0
  42. data/spec/fixtures/database/data/entries/job_skills/job_skills_2.json +7 -0
  43. data/spec/fixtures/database/data/entries/job_skills/job_skills_3.json +7 -0
  44. data/spec/fixtures/database/data/entries/job_skills/job_skills_4.json +7 -0
  45. data/spec/fixtures/database/data/entries/job_skills/job_skills_5.json +7 -0
  46. data/spec/fixtures/database/data/entries/job_skills/job_skills_6.json +7 -0
  47. data/spec/fixtures/database/data/entries/job_skills/job_skills_7.json +7 -0
  48. data/spec/fixtures/database/data/entries/job_skills/job_skills_8.json +7 -0
  49. data/spec/fixtures/database/data/entries/job_skills/job_skills_9.json +7 -0
  50. data/spec/fixtures/database/data/entries/jobs/jobs_1.json +56 -0
  51. data/spec/fixtures/database/data/entries/jobs/jobs_2.json +55 -0
  52. data/spec/fixtures/database/data/entries/jobs/jobs_4.json +49 -0
  53. data/spec/fixtures/database/data/entries/profile/profile_1.json +12 -0
  54. data/spec/fixtures/database/data/entries/profile/profile_2.json +12 -0
  55. data/spec/fixtures/database/data/entries/user/user_1.json +24 -0
  56. data/spec/fixtures/database/data/entries/user/user_2.json +20 -0
  57. data/spec/fixtures/database/data/helpers/job_add_id_comments.json +11 -0
  58. data/spec/fixtures/database/data/helpers/job_add_id_job_add_skills.json +24 -0
  59. data/spec/fixtures/database/data/helpers/user_id_job_adds.json +9 -0
  60. data/spec/fixtures/database/data/helpers/user_id_profiles.json +8 -0
  61. data/spec/fixtures/database/data/table_names.json +10 -0
  62. data/spec/fixtures/database/table_names.json +4 -0
  63. data/spec/fixtures/development.sqlite3 +0 -0
  64. data/spec/fixtures/json_responses/transformed_row.json +7 -0
  65. data/spec/fixtures/json_row/row.json +6 -0
  66. data/spec/fixtures/settings/contentful_model.json +316 -0
  67. data/spec/fixtures/settings/contentful_structure.json +89 -0
  68. data/spec/fixtures/settings/contentful_structure_test.json +82 -0
  69. data/spec/fixtures/settings/mapping.json +119 -0
  70. data/spec/fixtures/settings/settings.yml +27 -0
  71. data/spec/lib/configuration_spec.rb +17 -0
  72. data/spec/lib/database/export_spec.rb +49 -0
  73. data/spec/lib/database/json_export_spec.rb +49 -0
  74. data/spec/lib/database/relations_export_spec.rb +201 -0
  75. data/spec/lib/migrator_spec.rb +112 -0
  76. data/spec/spec_helper.rb +12 -0
  77. data/spec/support/db_rows_json.rb +9 -0
  78. data/spec/support/shared_configuration.rb +27 -0
  79. metadata +358 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 06cd3d41d41504c75c8b22d74cdaee9d77ca79d5
4
+ data.tar.gz: 5655c082e08d86f9d5e438593102f69a51defed9
5
+ SHA512:
6
+ metadata.gz: a9180c514f3224204ea8da91a38dce8ea9c991ccb72d8f9566cae25f8ce4e86c0f86b20046488c14d4938d31ca0c13d53f5144be45e2568eaf22f7a8a1a629fe
7
+ data.tar.gz: 9e85b77bc5982b7866c82642d6a0744aa5a1c42f31f5872857acd651c75862857dc105c6abdc164b2ab4559efe878a301e706ab80f6753a5d6e4e44bc6134f17
data/.gitignore ADDED
@@ -0,0 +1,39 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ # Gemfile.lock
30
+ # .ruby-version
31
+ # .ruby-gemset
32
+
33
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
+ .rvmrc
35
+
36
+ #RubyMine
37
+ .idea
38
+
39
+ .DS_Store
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 1.9.3
5
+ # - jruby-19mode
6
+ - rbx-2
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ # Change Log
2
+ ## 0.0.1
3
+ ### Other
4
+ * Initial release
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+ # Specify your gem's dependencies in database_exporter.gemspec
3
+ gemspec
4
+
5
+ group :test do
6
+ gem 'simplecov', require: false
7
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,74 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ database-exporter (0.0.1)
5
+ activesupport (~> 4.1)
6
+ escort (~> 0.4.0)
7
+ http (~> 0.6)
8
+ i18n (~> 0.6)
9
+ multi_json (~> 1)
10
+ mysql2 (~> 0.3)
11
+ pg (~> 0.17.0)
12
+ sequel (~> 4.15)
13
+ sqlite3 (~> 1.3.10)
14
+
15
+ GEM
16
+ remote: https://rubygems.org/
17
+ specs:
18
+ activesupport (4.1.8)
19
+ i18n (~> 0.6, >= 0.6.9)
20
+ json (~> 1.7, >= 1.7.7)
21
+ minitest (~> 5.1)
22
+ thread_safe (~> 0.1)
23
+ tzinfo (~> 1.1)
24
+ diff-lcs (1.2.5)
25
+ docile (1.1.5)
26
+ escort (0.4.0)
27
+ nesty
28
+ http (0.6.3)
29
+ http_parser.rb (~> 0.6.0)
30
+ http_parser.rb (0.6.0)
31
+ i18n (0.6.11)
32
+ json (1.8.1)
33
+ minitest (5.5.0)
34
+ multi_json (1.10.1)
35
+ mysql2 (0.3.17)
36
+ nesty (1.0.2)
37
+ pg (0.17.1)
38
+ rake (10.4.2)
39
+ rspec (3.1.0)
40
+ rspec-core (~> 3.1.0)
41
+ rspec-expectations (~> 3.1.0)
42
+ rspec-mocks (~> 3.1.0)
43
+ rspec-core (3.1.7)
44
+ rspec-support (~> 3.1.0)
45
+ rspec-expectations (3.1.2)
46
+ diff-lcs (>= 1.2.0, < 2.0)
47
+ rspec-support (~> 3.1.0)
48
+ rspec-its (1.1.0)
49
+ rspec-core (>= 3.0.0)
50
+ rspec-expectations (>= 3.0.0)
51
+ rspec-mocks (3.1.3)
52
+ rspec-support (~> 3.1.0)
53
+ rspec-support (3.1.2)
54
+ sequel (4.17.0)
55
+ simplecov (0.9.1)
56
+ docile (~> 1.1.0)
57
+ multi_json (~> 1.0)
58
+ simplecov-html (~> 0.8.0)
59
+ simplecov-html (0.8.0)
60
+ sqlite3 (1.3.10)
61
+ thread_safe (0.3.4)
62
+ tzinfo (1.2.2)
63
+ thread_safe (~> 0.1)
64
+
65
+ PLATFORMS
66
+ ruby
67
+
68
+ DEPENDENCIES
69
+ bundler (~> 1.6)
70
+ database-exporter!
71
+ rake
72
+ rspec (~> 3)
73
+ rspec-its (~> 1.1.0)
74
+ simplecov
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Contentful
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,682 @@
1
+ Database to Contentful exporter
2
+ =================
3
+
4
+ ## Description
5
+
6
+ Migrate content from a relational database to [contentful.com](https://www.contentful.com).
7
+
8
+ This tool allows you to fetch content from your database system and prepare it for the import.
9
+
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ gem install database-exporter
15
+ ```
16
+
17
+ This will install the `database-exporter` executable on your system.
18
+
19
+ ## Usage
20
+
21
+ Once you installed the Gem and created the `settings.yml` file, you can invoke the tool using:
22
+
23
+ ```bash
24
+ database-exporter --config-file settings.yml --action
25
+ ```
26
+
27
+ ## Step by Step
28
+
29
+ 1. Create a YAML file with the required parameters (e.g. settings.yml):
30
+
31
+ ```yml
32
+ #PATH to all data, this will create a folder in your current working directory
33
+ data_dir: PATH_TO_ALL_DATA
34
+
35
+ #Connecting to the database
36
+ adapter: postgres
37
+ host: localhost
38
+ database: database_name
39
+ user: username
40
+ password: username
41
+
42
+ # Extract data from models:
43
+ mapped:
44
+ tables:
45
+ - :table_name_1
46
+ - :table_name_2
47
+ - :table_name_3
48
+ - :table_name_4
49
+
50
+ ## MAPPING ##
51
+ mapping_dir: example_data/mapping.json
52
+ contentful_structure_dir: example_data/contentful_structure.json
53
+
54
+ ## CONVERT
55
+ content_model_json: example_data/contentful_model.json
56
+ converted_model_dir: example_data/contentful_structure.json
57
+ ```
58
+
59
+ 2. Create the `contentful_structure.json` file: First you should create a content model using the [Contentful ](www.contentful.com) web application. Then you can download the content model using the content management api and use it as the schema for your imports.
60
+
61
+ ```bash
62
+ curl -X GET \
63
+ -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
64
+ 'https://api.contentful.com/spaces/SPACE_ID/content_types' > contentful_model.json
65
+ ```
66
+
67
+ It will create a `contentful_model.json` file, which you need to transform into the `contentful_structure.json` file using:
68
+
69
+ ```bash
70
+ database-exporter --config-file settings.yml --convert-content-model-to-json
71
+ ```
72
+
73
+ The converted content model will be saved as a JSON file in the `converted_model_dir` path.
74
+
75
+ 3. Create content types files: Based on the ```contentful_structure.json``` file, create content types json files, which represents your contentful structure, use:
76
+
77
+ ```bash
78
+ database-exporter --config-file settings.yml --create-content-model-from-json
79
+ ```
80
+
81
+ It will extract your content types and store it as a separate JSON file in `data_dir/collections` directory.
82
+
83
+ 4. After filling in the required parameters to [connect to the database](https://github.com/contentful/database-adapter#database-connection---define-adapter), the tables we want to fetch the content from need to be specified.
84
+ You can skip `joining table names`, if you do not want to map them to a separate content type.
85
+
86
+ ```bash
87
+ database-exporter --config-file settings.yml --list-tables
88
+ ```
89
+
90
+ It will create the `table_names.json` file with the names of all tables contained in database.
91
+
92
+ Example:
93
+
94
+ ```javascript
95
+ [
96
+ "schema_migrations",
97
+ "skills",
98
+ "comments",
99
+ "images",
100
+ "job_add_skills",
101
+ "users",
102
+ "job_adds",
103
+ "profiles"
104
+ ]
105
+ ```
106
+
107
+ 5. Extract data from the database: Create the `mapping.json` file with the mapped the structure of your database.
108
+
109
+ Example structure for `user` table.
110
+
111
+ ```javascript
112
+ "Users": {
113
+ "content_type": "User",
114
+ "type": "entry",
115
+ "fields": {
116
+ },
117
+ "links": {
118
+ }
119
+ }
120
+ ```
121
+
122
+ After defining the structure for each table you want to extract in the JSON file, use:
123
+
124
+ ```bash
125
+ database-exporter --config-file settings.yml --extract-to-json
126
+ ```
127
+
128
+ This will extract data from tables and store it as JSON. The `data_dir/entries` directory will be created with subdirectories that represent the data from each table.
129
+ The sub-directories name depends on the *content_type* parameter used in the mapping.json file.
130
+
131
+ 6. Mapping data to content types: The `mapping.json` file contains the structure of your database. All the relationships between the models need to be specified there.
132
+ A description of how to build those relationships [can be found here](https://github.com/contentful/database-adapter#relations-types).
133
+
134
+ To begin the mapping procedure, use:
135
+
136
+ ```bash
137
+ database-exporter --config-file settings.yml --prepare-json
138
+ ```
139
+
140
+ It will change the structure of files in the `entries` directory. If the mapping has been done correctly, you can proceed to import the data into Contentful.
141
+
142
+ 7. Use the [contentful-importer](https://github.com/contentful/generic-importer.rb) to import the content to [contentful.com](https://www.contentful.com)
143
+
144
+
145
+ ## Configuration File
146
+
147
+ You need to create a configuration file and fill in the following information:
148
+
149
+ ```yml
150
+ #PATH to all data
151
+ data_dir: PATH_TO_ALL_DATA
152
+
153
+ #Connecting to a database
154
+ adapter: postgres
155
+ host: localhost
156
+ database: database_name
157
+ user: username
158
+ password: password
159
+
160
+ # Extract data from models:
161
+ mapped:
162
+ tables:
163
+ - :table_name_1
164
+ - :table_name_2
165
+ - :table_name_3
166
+
167
+ # Mapping
168
+ mapping_dir: PATH_TO_MAPPING_FILE/mapping.json
169
+ contentful_structure_dir: PATH_TO_CONTENTFUL_STURCTURE_FILE/contentful_structure.json
170
+
171
+ # Convert
172
+ content_model_json: PATH_TO_CONTENT_MODEL/contentful_model.json
173
+ converted_model_dir: PATH_TO_CONVERTED_CONTENT_MODEL_FILE/contentful_structure.json
174
+
175
+ ```
176
+
177
+ ## Actions
178
+
179
+ To display all actions use the `-h` option:
180
+
181
+ ```bash
182
+ database-exporter -h
183
+ ```
184
+
185
+ #### --list-tables
186
+
187
+ This action will create a JSON file including all table names from your database and write them to `data_dir/table_names.json`. The table names are needed to extract the content from the database.
188
+
189
+ #### --extract-to-json
190
+
191
+ In the [settings.yml](https://github.com/contentful/database-adapter#setting-file) file, you need to define the table names that should be exported from the database.
192
+
193
+ The recommended way to get the table names, is using [--list-tables](https://github.com/contentful/database-adapter#--list-tables).
194
+
195
+ After specifying the table names you want to extract in your settings, run the `--extract-to-json` command.
196
+ This will save each object from the database into its own JSON file, ready to be transformed and imported.
197
+
198
+ Path to JSON data: ***data_dir/entries/content_type_name_defined_in_mapping_json_file***
199
+
200
+ #### --prepare-json
201
+
202
+ Prepares the generated JSON files so they can be imported to Contentful.
203
+
204
+
205
+ ### FIELDS
206
+
207
+ To change the name of a field in the database to a new one in the content type, we need to add a new mapping for that field:
208
+ ```javascript
209
+ "fields": {
210
+ "model_name": "new_api_contentful_field_name",
211
+ "model_name": "new_api_contentful_field_name",
212
+ "model_name": "new_api_contentful_field_name"
213
+ },
214
+ ```
215
+
216
+
217
+ ### Relation Types/Joins
218
+
219
+ The following relational associations behave similar to the Active Record associations.
220
+
221
+ #### belongs_to
222
+
223
+ The `belongs_to` method should only be used if this table contains the foreign key. If the other table contains the foreign key, then you should use `has_one` instead.
224
+
225
+ At the beginning and we are looking for `type` and `id` of the linked object in file `contentful_structure.json`.
226
+ It's very important to maintain consistency for the content type names in `mapping.json` and `contentful_structure.json`.
227
+ The next step is to check if the object has defined a foreign key itself. After that an object with type and ID is created.
228
+
229
+ Example:
230
+
231
+ ```javascript
232
+ "Comments": {
233
+ "content_type": "Comments",
234
+ "type": "entry",
235
+ "fields": {
236
+ },
237
+ "links": {
238
+ "belongs_to": [
239
+ {
240
+ "relation_to": "ModelName",
241
+ "foreign_id": "model_foreign_id"
242
+ }
243
+ ]
244
+ }
245
+ }
246
+ ```
247
+
248
+ It will assign the associated object and save its ID `(model_name + id)` in the JSON file.
249
+
250
+ Result:
251
+
252
+ ```javascript
253
+ {
254
+ "id": "model_name_ID",
255
+ ...
256
+ "job_add_id": {
257
+ "type": "Entry",
258
+ "id": "model_name_3"
259
+ },
260
+ }
261
+ ```
262
+
263
+ #### has_one
264
+
265
+ The `has_one` method should be used if the other table contains the foreign key. If the current table contains the foreign key, then you should use belongs_to instead.
266
+
267
+ At the beginning the tool builds a helper file which contains the primary id as key and the foreign id as values. This file lives in `data_dir/helpers`.
268
+
269
+ After that we modify only those files whose ID is located in the helper file as a key. Value is written as a Hash value.
270
+
271
+ Example:
272
+
273
+ ```javascript
274
+ "Users": {
275
+ "content_type": "Users",
276
+ "type": "entry",
277
+ "fields": {
278
+ ...
279
+ },
280
+ "links": {
281
+ "has_one": [
282
+ {
283
+ "relation_to": "ModelName",
284
+ "primary_id": "primary_key_name"
285
+ }
286
+ ]
287
+ }
288
+ }
289
+ ```
290
+
291
+ Result:
292
+
293
+ It will assign the associated object, save his ID ```(model_name + id)``` in JSON file.
294
+
295
+ ```javascript
296
+ ...
297
+ "model_name": {
298
+ "type": "profiles",
299
+ "id": "content_type_id_3"
300
+ }
301
+ ```
302
+
303
+
304
+ #### many
305
+
306
+ The resulting file will be generated in a similar way as for the `has_one` relation.
307
+ At the beginning the tool builds a helper file which contains the primary id as key and the foreign id as values. This file lives in `data_dir/helpers`.
308
+
309
+ After that we modify only those files whose ID is located in the helper file as a key. Related objects are written always as an Array.
310
+
311
+ Example:
312
+
313
+ ```javascript
314
+ "ModelName": {
315
+ ...
316
+ },
317
+ "links": {
318
+ "many": [
319
+ {
320
+ "relation_to": "related_model_name",
321
+ "primary_id": "primary_key_name"
322
+ }
323
+ ],
324
+ }
325
+ }
326
+ ```
327
+
328
+ It will assign the associated objects, save its ID ```(model_name + id)``` in JSON file.
329
+
330
+ Result:
331
+
332
+ ```javascript
333
+ {
334
+ "id": "content_type_id",
335
+ "comments": [
336
+ {
337
+ "type": "related_content_type_name",
338
+ "id": "related_model_name_id"
339
+ },
340
+ {
341
+ "type": "related_content_type_name",
342
+ "id": "related_model_name_id"
343
+ },
344
+ {
345
+ "type": "related_content_type_name",
346
+ "id": "related_model_name_id"
347
+ },
348
+ {
349
+ "type": "related_content_type_name",
350
+ "id": "related_model_name_id"
351
+ }
352
+ ]
353
+ }
354
+ ```
355
+
356
+ #### many_through
357
+
358
+ The resulting file will be generated in a similar way as for the `has_one` relation.
359
+ After that we modify only those files whose ID is located in the helper file as a key. Related objects are written always as an Array.
360
+
361
+ Attributes:
362
+
363
+ ```
364
+ relation_to: Name of the related model, defined in the mapping.json file as a key.
365
+ primary_id: Name of the primary key located in the joining table.
366
+ foreign_id: Name of the foreign key, located in the joining table. The object with this ID will be added to the mapped object.
367
+ through: Name of the joining model.
368
+ ```
369
+
370
+
371
+ Example:
372
+
373
+ ```javascript
374
+ "ModelName": {
375
+ ...
376
+ "links": {
377
+ "many_through": [
378
+ {
379
+ "relation_to": "related_model_name",
380
+ "primary_id": "primary_key_name",
381
+ "foreign_id": "foreign_key_name",
382
+ "through": "join_table_name"
383
+ }
384
+ ]
385
+ }
386
+ }
387
+ ```
388
+
389
+ It will map the join table and save the objects IDs in the current model.
390
+
391
+ Result:
392
+
393
+ ```javascript
394
+ "content_type_name": [
395
+ {
396
+ "type": "content_type_name",
397
+ "id": "related_model_foreign_id"
398
+ },
399
+ {
400
+ "type": "content_type_name",
401
+ "id": "related_model_foreign_id"
402
+ },
403
+ {
404
+ "type": "content_type_name",
405
+ "id": "related_model_foreign_id"
406
+ }
407
+ ]
408
+ ```
409
+
410
+ #### aggregate_belongs
411
+
412
+ `aggregate_belongs` allows to fetch a value from a related model.
413
+ To add the value, the table must have the `foreign_id` to the related table. Through this key the object is found and the related data is extracted.
414
+
415
+ Attributes:
416
+
417
+ ```
418
+ relation_to: Name of the related model, defined in the mapping.json file as a key.
419
+ primary_id: Name of the primary key in the model.
420
+ field: Name of the attribute, which you want to add.
421
+ save_as: Name of the attribute whose value is assigned.
422
+ ```
423
+
424
+ Example:
425
+
426
+ ```javascript
427
+ "links": {
428
+ "aggregate_belongs": [
429
+ {
430
+ "relation_to": "related_model_name",
431
+ "primary_id": "primary_key_name",
432
+ "field": "aggregated_field_name",
433
+ "save_as": "name_of_field"
434
+ }
435
+ ]
436
+ }
437
+ ```
438
+
439
+ Result:
440
+
441
+ ```javascript
442
+ {
443
+ "id": "model_name_id",
444
+ "name_of_field": "aggregated_value"
445
+ }
446
+ ```
447
+
448
+ #### aggregate_has_one
449
+
450
+ It will save the value with the key of the related model. To add `has_one` value, the table must have the `primary_id` of the related table.
451
+
452
+ Attributes:
453
+
454
+ ```
455
+ relation_to: Name of the related model, defined in the mapping.json file as a key.
456
+ primary_id: Name of the primary key in the model.
457
+ field: Name of the attribute, which you want to add.
458
+ save_as: Name of the attribute whose value is assigned.
459
+ ```
460
+
461
+ Example:
462
+
463
+ ```javascript
464
+ "links": {
465
+ "aggregate_has_one": [
466
+ {
467
+ "primary_id": "primary_id",
468
+ "relation_to": "related_model_name",
469
+ "field": "name_of_field_to_aggregate",
470
+ "save_as": "save_as_field_name"
471
+ }
472
+ ]
473
+ }
474
+ ```
475
+
476
+ Result:
477
+
478
+ ```javascript
479
+ {
480
+ "id": "model_name_id",
481
+ "name_of_field": "aggregated_value"
482
+ }
483
+ ```
484
+
485
+ #### aggregate_many
486
+
487
+ It will save the value with the key of the related table. To add the `has_many` value, the related table must have the `primary_id` of the related model. This will create a new attribute in the model with the type `Array`.
488
+
489
+ Example:
490
+
491
+ ```javascript
492
+ "links": {
493
+ "aggregate_many": [
494
+ {
495
+ "primary_id": "primary_id",
496
+ "relation_to": "related_model_name",
497
+ "field": "name_of_field_to_aggregate",
498
+ "save_as": "save_as_field_name"
499
+ }
500
+ ]
501
+ }
502
+ ```
503
+
504
+ Result:
505
+
506
+ ```javascript
507
+ {
508
+ "id": "model_name_id",
509
+ "name_of_field": [
510
+ "aggregated_value1",
511
+ "aggregated_value2",
512
+ "aggregated_value3",
513
+ "aggregated_value4"
514
+ ]
515
+ }
516
+ ```
517
+
518
+ #### aggregate_through
519
+
520
+ It will save the value with the key of the related model.
521
+ To add the `has_many, through` value, you need to define the `join model` which contains the `primary_id` and `foreign_id`. Through the `foreign_id` the desired object can be found.
522
+
523
+ Attributes:
524
+
525
+ ```
526
+ relation_to: Name of related model, defined in mapping.json file as a key.
527
+ primary_id: Name of primary key located in joining table.
528
+ foreign_id: Name of foreign key, located in joining table. Object with this ID will be added mapped object.
529
+ through: Name of joining model.
530
+ ```
531
+
532
+ Example:
533
+
534
+ ```javascript
535
+ "links": {
536
+ "aggregate_through": [
537
+ {
538
+ "relation_to": "related_model_name",
539
+ "primary_id": "primary_key_name",
540
+ "foreign_id": "foreign_key_name",
541
+ "through": "join_table_name",
542
+ "field": '"name_of_field_to_aggregate",
543
+ "save_as": "save_as_field_name"
544
+ }
545
+ ]
546
+ }
547
+ ```
548
+
549
+ Result:
550
+
551
+ ```javascript
552
+ {
553
+ "id": "model_name_id",
554
+ "name_of_field": ["aggregated_value1",
555
+ "aggregated_value2",
556
+ "aggregated_value3",
557
+ "aggregated_value4"
558
+ ]
559
+ }
560
+ ```
561
+
562
+ ## Contentful Structure
563
+
564
+ This file represents our Contentful structure, it defines the remote data types and how they are formed.
565
+
566
+ Example:
567
+
568
+ ```javascript
569
+ {
570
+ "Comments": {
571
+ "id": "comment",
572
+ "description": "",
573
+ "displayField": "title",
574
+ "fields": {
575
+ "title": "Text",
576
+ "content": "Text"
577
+ }
578
+ },
579
+ "JobAdd": {
580
+ "id": "job_add",
581
+ "description": "Add new job form",
582
+ "displayField": "name",
583
+ "fields": {
584
+ "name": "Text",
585
+ "specification": "Text",
586
+ "Images": {
587
+ "id": "image",
588
+ "link_type": "Asset"
589
+ },
590
+ "Comments": {
591
+ "id": "comments",
592
+ "link_type": "Array",
593
+ "type": "Entry"
594
+ },
595
+ "Skills": {
596
+ "id": "skills",
597
+ "link_type": "Array",
598
+ "type": "Entry"
599
+ }
600
+ }
601
+ }
602
+ ```
603
+ They keys "Images", "Comments", "Skills" are the equivalent of the content types IDs specified in the file **mapping.json**.
604
+
605
+ Example:
606
+ ```javascript
607
+ "SkillsTableName": {
608
+ "content_type": "Skills",
609
+ "type": "entry",
610
+ "fields": { ... }
611
+ ```
612
+
613
+ **IMPORTANT**
614
+
615
+ To create any relationship between tables, we must remember that the content names given in the **mapping.json** file, must be equal with names in the **contentful_structure.json** file.
616
+
617
+ ## Settings file
618
+
619
+ To be able to extract any content you need to create a `settings.yml` file and define all needed parameters.
620
+
621
+ #### Database Connection - Define Adapter
622
+
623
+ Assuming we are going to work with a MySQL, SQLite or PostgreSQL database we need to setup the credentials:
624
+ Following is the example of connecting to a MySQL database `test_import`.
625
+
626
+ ```yml
627
+ adapter: mysql2
628
+ user: username
629
+ host: localhost
630
+ database: test_import
631
+ password: secret_password
632
+ ```
633
+
634
+ **Available Adapters**
635
+
636
+ ```
637
+ PostgreSQL => postgres
638
+ MySQL => mysql2
639
+ SQlite => sqlite
640
+ ```
641
+
642
+ #### Mapped tables
643
+
644
+ Before we can start exporting the data from the database, the to be used tables need to be specified.
645
+ The fastest way to get the names is using the [--list-tables](https://github.com/contentful/generic-importer.rb#--list-tables) action.
646
+
647
+ Add those to the `settings.yml` file in the following manner:
648
+
649
+ ```yml
650
+ mapped:
651
+ tables:
652
+ ```
653
+ Example:
654
+
655
+ ```yml
656
+ mapped:
657
+ tables:
658
+ - :example_1
659
+ - :example_2
660
+ - :example_3
661
+ - :example_4
662
+ ```
663
+
664
+ There is no need to specify the names of a join table unless you want to save them as a separate content types.
665
+
666
+ ### Mapping
667
+
668
+ * JSON file with mapping structure that defines relations between models.
669
+
670
+ ```yml
671
+ mapping_dir: example_path/mapping.json
672
+ ```
673
+
674
+ * JSON file with contentful structure
675
+ ```yml
676
+ contentful_structure_dir: contentful_import_files/contentful_structure.json
677
+ ```
678
+ * [Dump JSON file](https://github.com/contentful/generic-importer.rb#--convert-content-model-to-json) with content types from content model:
679
+
680
+ ```yml
681
+ converted_model_dir: contentful_import_files/contentful_structure.json
682
+ ```