contentful-database-importer 0.1.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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/CONTRIBUTING.md +43 -0
- data/Gemfile +4 -0
- data/Guardfile +14 -0
- data/LICENSE.txt +21 -0
- data/README.md +253 -0
- data/Rakefile +6 -0
- data/contentful-database-importer.gemspec +36 -0
- data/lib/contentful/database_importer/config.rb +12 -0
- data/lib/contentful/database_importer/id_generator/base.rb +37 -0
- data/lib/contentful/database_importer/id_generator/contentful_like.rb +23 -0
- data/lib/contentful/database_importer/id_generator.rb +2 -0
- data/lib/contentful/database_importer/json_generator.rb +82 -0
- data/lib/contentful/database_importer/resource.rb +88 -0
- data/lib/contentful/database_importer/resource_bootstrap_class_methods.rb +113 -0
- data/lib/contentful/database_importer/resource_bootstrap_methods.rb +22 -0
- data/lib/contentful/database_importer/resource_class_methods.rb +126 -0
- data/lib/contentful/database_importer/resource_coercions.rb +113 -0
- data/lib/contentful/database_importer/resource_relationships.rb +69 -0
- data/lib/contentful/database_importer/support.rb +14 -0
- data/lib/contentful/database_importer/version.rb +5 -0
- data/lib/contentful/database_importer.rb +59 -0
- metadata +251 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 98d3aacd7022ac369a2b9b81cd21048d52d1da11
|
4
|
+
data.tar.gz: 063b897d2875b9ac709da9f930035f108b41c135
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9b3a9767b6a65e6e5fc9ad2efb26e68190411b358f35e9fa25e8075cbfbae1943e6851f759447d76a819dd9f1baa91e610e86af3277c970e274e914c2309f585
|
7
|
+
data.tar.gz: 99db145e30c2adf4bfb8b3e07c49d5626f6576f331aaccd7ed41a142eacbe0a336ecd074671667cea63ffb5cb25ee7407d4908275b9ab26ebad12fc6ebedacca
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This code of conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting a project maintainer at david.litvakb@gmail.com. All
|
39
|
+
complaints will be reviewed and investigated and will result in a response that
|
40
|
+
is deemed necessary and appropriate to the circumstances. Maintainers are
|
41
|
+
obligated to maintain confidentiality with regard to the reporter of an
|
42
|
+
incident.
|
43
|
+
|
44
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
45
|
+
version 1.3.0, available at
|
46
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
47
|
+
|
48
|
+
[homepage]: http://contributor-covenant.org
|
49
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Contributing
|
2
|
+
In the spirit of [free software][free-sw], **everyone** is encouraged to help
|
3
|
+
improve this project.
|
4
|
+
|
5
|
+
[free-sw]: http://www.fsf.org/licensing/essays/free-sw.html
|
6
|
+
|
7
|
+
Here are some ways *you* can contribute:
|
8
|
+
|
9
|
+
* by using alpha, beta, and prerelease versions
|
10
|
+
* by reporting bugs
|
11
|
+
* by suggesting new features
|
12
|
+
* by writing or editing documentation
|
13
|
+
* by writing specifications
|
14
|
+
* by writing code ( **no patch is too small** : fix typos, add comments, clean up inconsistent whitespace )
|
15
|
+
* by refactoring code
|
16
|
+
* by closing [issues][]
|
17
|
+
* by reviewing patches
|
18
|
+
|
19
|
+
[issues]: https://github.com/contentful/contentful-database-importer.rb/issues
|
20
|
+
|
21
|
+
## Submitting an Issue
|
22
|
+
We use the [GitHub issue tracker][issues] to track bugs and features. Before
|
23
|
+
submitting a bug report or feature request, check to make sure it hasn't
|
24
|
+
already been submitted. When submitting a bug report, please include a [Gist][]
|
25
|
+
that includes a stack trace and any details that may be necessary to reproduce
|
26
|
+
the bug, including your gem version, Ruby version, and operating system.
|
27
|
+
Ideally, a bug report should include a pull request with failing specs.
|
28
|
+
|
29
|
+
[gist]: https://gist.github.com/
|
30
|
+
|
31
|
+
## Submitting a Pull Request
|
32
|
+
1. [Fork the repository.][fork]
|
33
|
+
2. [Create a topic branch.][branch]
|
34
|
+
3. Add specs for your unimplemented feature or bug fix.
|
35
|
+
4. Run `bundle exec rake test`. If your specs pass, return to step 3.
|
36
|
+
5. Implement your feature or bug fix.
|
37
|
+
6. Run `bundle exec rake test`. If your specs fail, return to step 5.
|
38
|
+
7. Add, commit, and push your changes.
|
39
|
+
8. [Submit a pull request.][pr]
|
40
|
+
|
41
|
+
[fork]: http://help.github.com/fork-a-repo/
|
42
|
+
[branch]: http://learn.github.com/p/branching.html
|
43
|
+
[pr]: http://help.github.com/send-pull-requests/
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
2
|
+
require "guard/rspec/dsl"
|
3
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
4
|
+
|
5
|
+
# RSpec files
|
6
|
+
rspec = dsl.rspec
|
7
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
8
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
9
|
+
watch(rspec.spec_files)
|
10
|
+
|
11
|
+
# Ruby files
|
12
|
+
ruby = dsl.ruby
|
13
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
14
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Contentful GmbH - David Litvak Bruno
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,253 @@
|
|
1
|
+
# Contentful Database Importer
|
2
|
+
|
3
|
+

|
4
|
+
|
5
|
+
A simple DSL to define your database schemas, their relation to Contentful and import them to Contentful.
|
6
|
+
|
7
|
+
This gem is intended to be a replacement to [`database_exporter`](https://github.com/contentful/database-exporter.rb). _Warning_: Both gems are incompatible.
|
8
|
+
|
9
|
+
## Contentful
|
10
|
+
|
11
|
+
[Contentful](http://www.contentful.com) is a content management platform for web applications, mobile apps and connected devices.
|
12
|
+
It allows you to create, edit and manage content in the cloud and publish it anywhere via a powerful API.
|
13
|
+
Contentful offers tools for managing editorial teams and enabling cooperation between organizations.
|
14
|
+
|
15
|
+
## What does `contentful-database-importer` do?
|
16
|
+
|
17
|
+
`contentful-database-importer` let's you define mapping classes between your database and Contentful and allows
|
18
|
+
you to generate a JSON file that's a valid [`contentful_bootstrap`](https://github.com/contentful/contentful-bootstrap.rb) JSON Template,
|
19
|
+
or directly import to Contentful, creating a new space and using your data to populate the content.
|
20
|
+
|
21
|
+
|
22
|
+
## Installation
|
23
|
+
|
24
|
+
```bash
|
25
|
+
gem install contentful-database-importer
|
26
|
+
```
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
* Create a new directory for your import configuration:
|
31
|
+
|
32
|
+
```bash
|
33
|
+
mkdir my_importer_dir && cd my_importer_dir
|
34
|
+
```
|
35
|
+
|
36
|
+
* Create a _`Gemfile`_ with the gem:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
source 'https://rubygems.org'
|
40
|
+
|
41
|
+
gem 'contentful-database-importer'
|
42
|
+
```
|
43
|
+
|
44
|
+
* Create your importer file, for example _`import.rb`_:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
require 'contentful/database_importer'
|
48
|
+
|
49
|
+
class MyTable
|
50
|
+
include Contentful::DatabaseImporter::Resource
|
51
|
+
|
52
|
+
# ... your schema definition ... (explained in next section)
|
53
|
+
end
|
54
|
+
|
55
|
+
# ... more table definitions ...
|
56
|
+
|
57
|
+
Contentful::DatabaseImporter.setup do |config|
|
58
|
+
config.space_name = 'My Cool New Space'
|
59
|
+
config.database_connection = 'postgres://user:pass@host:port'
|
60
|
+
end
|
61
|
+
|
62
|
+
Contentful::DatabaseImporter.run!
|
63
|
+
```
|
64
|
+
|
65
|
+
* Run your file:
|
66
|
+
|
67
|
+
```bash
|
68
|
+
bundle exec ruby import.rb
|
69
|
+
```
|
70
|
+
|
71
|
+
### Defining your Schema
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
class MyTable
|
75
|
+
include Contentful::DatabaseImporter::Resource
|
76
|
+
|
77
|
+
self.table_name = 'overrides_table_name' # Optional - By default it's the class name in snake case. E.g. 'my_table'
|
78
|
+
self.content_type_id = 'overrides_content_type_id' # Optional - By default it's the class name in snake case. E.g 'my_table'
|
79
|
+
self.content_type_name = 'Overrides Name' # Optional - By default it's the class name
|
80
|
+
|
81
|
+
id Contentful::DatabaseImporter::IdGenerator::Base, template: '{{content_type_id}}_{{foo}}_{{index}}' # Optional - By default it's the IdGenerator::Base(template: '{{content_type_id}}_{{index}}')
|
82
|
+
|
83
|
+
field :foo, type: :string
|
84
|
+
field :bar, maps_to: :not_bar, type: :string
|
85
|
+
field :image, type: :asset
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
89
|
+
#### Overriding Table and Content Type ID
|
90
|
+
|
91
|
+
The methods `::table_name=` and `::content_type_id=` allow you to override the IDs for either the table or content type.
|
92
|
+
By default, they are a `snake_cased` version of the class name.
|
93
|
+
|
94
|
+
#### Defining the ID generator
|
95
|
+
|
96
|
+
You can define the ID generation strategy, there are 2 classes currently provided:
|
97
|
+
|
98
|
+
- `Contentful::DatabaseImporter::IdGenerator::Base`: Provides a very basic template engine for generating IDs, this is the default strategy.
|
99
|
+
- `Contentful::DatabaseImporter::IdGenerator::ContentfulLike`: Provides a Base62 encode that produces IDs similar to the Contentful provided ones,
|
100
|
+
uses the `Base` strategy, then pads it to a minimum length and then Base62 encode it.
|
101
|
+
|
102
|
+
##### ID Templates
|
103
|
+
|
104
|
+
Theres a minimal template engine provided for the ID Generators.
|
105
|
+
A single template looks like `{{foo}}_{{bar}}` and works by replacing the values enclosed between `{{}}` with the corresponding value for each entry.
|
106
|
+
|
107
|
+
There are a few variables globally provided for every class (and will be looked up before the object fields):
|
108
|
+
|
109
|
+
- `class_name`: The name of the mapping class
|
110
|
+
- `table_name`: The defined table name (or the default)
|
111
|
+
- `content_type_id`: The defined content type ID (or the default)
|
112
|
+
- `index`: The position of the entry (0-based) on the database table
|
113
|
+
|
114
|
+
After those globally provided values, you can use the value for any field on the mapping class (using the `:maps_to` value if present).
|
115
|
+
|
116
|
+
For example, using the example template above, if the DB record looks like:
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
{foo: 'something', bar: 'else', image: 'https://example.com/happycat.jpg'}
|
120
|
+
```
|
121
|
+
|
122
|
+
Then the resulting template will be `something_else`
|
123
|
+
|
124
|
+
**Note**: With relationships, it's useful to use a unique identifier value as part of the ID template.
|
125
|
+
|
126
|
+
#### Defining Fields
|
127
|
+
|
128
|
+
For defining the field you have the `::field(name, options = {})` method. It defines how to retrieve and later serialize the field.
|
129
|
+
|
130
|
+
The options are:
|
131
|
+
|
132
|
+
- `type: type_name`: **Required** for coercions. Types defined below.
|
133
|
+
- `maps_to: name`: **Optional**. Defaults to field name, and defines the field name in Contentful
|
134
|
+
- `pre_process: lambda_or_symbol`: **Optional**. Described below.
|
135
|
+
- `exclude_from_output: boolean`: **Optional**. Defaults to false. Defines if the field will not be uploaded to Contentful. Useful for ID generation.
|
136
|
+
|
137
|
+
##### Regular Field Types
|
138
|
+
|
139
|
+
- `:symbol`, `:string`: Short text field (255 characters maximum) in Contentful.
|
140
|
+
- `:text`: Long text field.
|
141
|
+
- `:number`: Floating point precision number.
|
142
|
+
- `:integer`: Integer number.
|
143
|
+
- `:boolean`: Boolean.
|
144
|
+
- `:location`: Geographical Location (can be coerced from a String, Hash or Array.)
|
145
|
+
- `:date`: An ISO8601 Date (can be coerced from a Date/DateTime object or String).
|
146
|
+
- `:object`: A JSON Object. _Currently not supported_
|
147
|
+
- `:asset`: A File description. A String containing the file URL needs to be provided.
|
148
|
+
- `:array`: An Array of elements.
|
149
|
+
|
150
|
+
In the case of using `:array`, an extra parameter `item_type: type` must be provided.
|
151
|
+
|
152
|
+
##### Relationship Field Types
|
153
|
+
|
154
|
+
If your data has a relationship field, the `type:` value will be the related class, and will require additional parameters specifying
|
155
|
+
the relationship type and keys for retrieving the appropiate data.
|
156
|
+
|
157
|
+
For example:
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
class Foo
|
161
|
+
field :bars, type: Bar, relationship: :many, id_field: :id, key: :foo_id
|
162
|
+
field :baz, type: Baz, relationship: :one, id_field: :id, key: :baz_id
|
163
|
+
field :quxs, type: Qux, relationship: :many_through, through: :foo_qux, primary_id_field: :id, primary_key: :foo_id, foreign_key: :qux_id, foreign_id_field: :id
|
164
|
+
end
|
165
|
+
```
|
166
|
+
|
167
|
+
In Contentful, relationships are unidirectional, and if you want bidirectional relationships, you need to declare them in both classes.
|
168
|
+
|
169
|
+
Relationship fields have the particularity that they don't require the `:maps_to` property, as Contentful will always use
|
170
|
+
the field name for the property in Contentful. You define the name of the field in the database with relationship specific parameters.
|
171
|
+
|
172
|
+
Relationship Types:
|
173
|
+
|
174
|
+
- `:many`: One to Many relationship, looks for all related objects of the associated class that match the value of the `:id_field` via the `:key`.
|
175
|
+
In the example above, it will look for all `Bar` entries which have a `:foo_id` that match the value of `:id` for the current `Foo` entry.
|
176
|
+
- `:one`: One to One relationship, looks for the related object of the associated class that matches the value of the `:key` field in the current entry, with the value of `:id_field` in the related entry.
|
177
|
+
In the example above, it will look for the `Baz` entry which has an ID that matches the value of `:baz_id` in the current entry.
|
178
|
+
- `:many_through`: Many to Many relationship, looks for the related object through an intermediate lookup table, after this it behaves like `:many`.
|
179
|
+
In the example above, it will look for all `Qux` entries found in the intermediate table that match the current entry `:id` and looks it up via the `Qux`s `:id`.
|
180
|
+
|
181
|
+
**Note**: If you're using relationships, use a custom ID Generator template which includes a unique field for each entry,
|
182
|
+
that way, creating the links in Contentful will be successful. This requires including the field in the class definition.
|
183
|
+
|
184
|
+
For example: `'{{content_type_id}}_{{id}}'`
|
185
|
+
|
186
|
+
**Note**: Ruby requires a class to be defined before using it as a parameter, therefore, you should declare all classes that
|
187
|
+
are contained within others, before the one in which you use them. If you want to have circular relationships, you need to define a Merge Class pointing to the same table and content type as the desired class (defined below).
|
188
|
+
|
189
|
+
##### Pre-processing
|
190
|
+
|
191
|
+
If you want to transform your data before uploading to Contentful,
|
192
|
+
you can use the `:pre_process` parameter in the `::field` definition.
|
193
|
+
|
194
|
+
The pre-process value can be a lambda function (E.g. `-> (value) { value + 1 }`) or a symbol (E.g. `:pre_process_foo`).
|
195
|
+
|
196
|
+
If you use a lambda function, it must receive a single parameter and return a single value.
|
197
|
+
|
198
|
+
If you use a Symbol, it must match the name of a method defined within the class you're calling it from.
|
199
|
+
This method must receive a single parameter and return a single value.
|
200
|
+
|
201
|
+
### Merging Tables
|
202
|
+
|
203
|
+
You might want to merge the content of multiple tables into a single content type.
|
204
|
+
|
205
|
+
This is supported by default, but ensure that the classes have the same `content_type_id` defined and if you need to
|
206
|
+
merge the entries as well, that the ID generator template is set in a way that can match the values from the different classes.
|
207
|
+
|
208
|
+
In the case you want to create multiple content types from a single table, the same concepts apply.
|
209
|
+
|
210
|
+
In the case of circular references, you will have to create 2 or more classes pointing to the same table and content type, the same concepts apply.
|
211
|
+
|
212
|
+
**Note**: Merge classes require at least 1 field declared, even if it's excluded from output.
|
213
|
+
|
214
|
+
### Configuration
|
215
|
+
|
216
|
+
```ruby
|
217
|
+
Contentful::DatabaseImporter.setup do |config|
|
218
|
+
config.space_name = 'My Cool New Space' # Required - the destination space name
|
219
|
+
config.database_connection = 'postgres://user:pass@host:port' # Required - the DB Connection string
|
220
|
+
end
|
221
|
+
```
|
222
|
+
|
223
|
+
`database_connection` allows the following Database URI Strings:
|
224
|
+
|
225
|
+
- **[Postgres (Section 31.1.1.2)](https://www.postgresql.org/docs/9.3/static/libpq-connect.html#LIBPQ-CONNSTRING)**: E.g. `'postgres://user:password@host:port/database_name'`.
|
226
|
+
- **SQlite**: E.g. `'sqlite://file_path.db'`.
|
227
|
+
- **MySQL**: E.g. `'mysql://user:password@host:post/database_name'`.
|
228
|
+
|
229
|
+
### Running the Import Tool
|
230
|
+
|
231
|
+
You can do any of the following operations:
|
232
|
+
|
233
|
+
* Generate a JSON Template as a Ruby Hash for reuse within the script:
|
234
|
+
|
235
|
+
```ruby
|
236
|
+
Contentful::DatabaseImporter.generate_json
|
237
|
+
```
|
238
|
+
|
239
|
+
* Generate JSON Template as a prettyfied JSON string:
|
240
|
+
|
241
|
+
```ruby
|
242
|
+
Contentful::DatabaseImporter.generate_json!
|
243
|
+
```
|
244
|
+
|
245
|
+
* Generate the JSON and Import it to Contentful (creates a Space with all the content):
|
246
|
+
|
247
|
+
```ruby
|
248
|
+
Contentful::DatabaseImporter.run!
|
249
|
+
```
|
250
|
+
|
251
|
+
## Contributing
|
252
|
+
|
253
|
+
Feel free to improve this tool by submitting a Pull Request. For more information, please read [CONTRIBUTING.md](./CONTRIBUTING.md)
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'contentful/database_importer/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "contentful-database-importer"
|
8
|
+
spec.version = Contentful::DatabaseImporter::VERSION
|
9
|
+
spec.authors = ["Contentful GmbH (David Litvak Bruno)"]
|
10
|
+
spec.email = ["david.litvak@contentful.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Tool to import content from a Database to Contentful}
|
13
|
+
spec.description = %q{Tool to import content from a Database to Contentful}
|
14
|
+
spec.homepage = "https://contentful.com"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency 'contentful_bootstrap', '~> 3.0'
|
23
|
+
spec.add_dependency 'contentful-management', '~> 0.9'
|
24
|
+
spec.add_dependency 'sequel', '~> 4.39'
|
25
|
+
spec.add_dependency 'base62-rb'
|
26
|
+
spec.add_dependency 'mimemagic'
|
27
|
+
|
28
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
29
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
30
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
31
|
+
spec.add_development_dependency 'rubocop'
|
32
|
+
spec.add_development_dependency 'simplecov'
|
33
|
+
spec.add_development_dependency 'guard'
|
34
|
+
spec.add_development_dependency 'guard-rspec'
|
35
|
+
spec.add_development_dependency 'pry'
|
36
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Contentful
|
2
|
+
module DatabaseImporter
|
3
|
+
module IdGenerator
|
4
|
+
# Base Id Generator
|
5
|
+
class Base
|
6
|
+
attr_reader :options
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def run(entry_data, index)
|
13
|
+
options.fetch(:template, '').gsub(/\{\{([\w|\.]+)\}\}/) do |match|
|
14
|
+
find(entry_data, index, match.gsub('{{', '').gsub('}}', '').to_sym)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def find(entry_data, index, match)
|
19
|
+
return options[match] if options.key?(match)
|
20
|
+
return index.to_s if match == :index
|
21
|
+
|
22
|
+
find_on_entry(entry_data, match)
|
23
|
+
end
|
24
|
+
|
25
|
+
def find_on_entry(entry_data, match)
|
26
|
+
if entry_data.excluded_fields.key?(match)
|
27
|
+
return entry_data.excluded_fields[match]
|
28
|
+
elsif entry_data.bootstrap_fields.key?(match)
|
29
|
+
return entry_data.bootstrap_fields[match]
|
30
|
+
end
|
31
|
+
|
32
|
+
raise "Template could not be resolved, #{match} not found."
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'contentful/database_importer/id_generator/base'
|
2
|
+
require 'base62-rb'
|
3
|
+
|
4
|
+
module Contentful
|
5
|
+
module DatabaseImporter
|
6
|
+
module IdGenerator
|
7
|
+
# Base62 Encoded Id Generator
|
8
|
+
class ContentfulLike < Base
|
9
|
+
def run(entry_data, index)
|
10
|
+
result = ''
|
11
|
+
id = super(entry_data, index)
|
12
|
+
id.each_char do |c|
|
13
|
+
result << c.ord.to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
result << '9' while result.size < 40
|
17
|
+
|
18
|
+
Base62.encode(result.to_i)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Contentful
|
4
|
+
module DatabaseImporter
|
5
|
+
# Json File Generator
|
6
|
+
module JsonGenerator
|
7
|
+
def self.generate_json
|
8
|
+
result = { version: 3, contentTypes: [],
|
9
|
+
assets: [], entries: {} }
|
10
|
+
|
11
|
+
resources.each do |resource|
|
12
|
+
content_type_definition = resource.content_type_definition
|
13
|
+
create_content_type(content_type_definition, result)
|
14
|
+
create_entries(resource, content_type_definition, result)
|
15
|
+
end
|
16
|
+
|
17
|
+
result
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.resources
|
21
|
+
ObjectSpace.each_object(Class).select do |c|
|
22
|
+
c.included_modules.include? Resource
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.create_content_type(content_type_definition, result)
|
27
|
+
prev_ct_definition = result[:contentTypes].find do |ct|
|
28
|
+
ct[:id] == content_type_definition[:id]
|
29
|
+
end
|
30
|
+
|
31
|
+
if prev_ct_definition
|
32
|
+
result[:contentTypes].delete(prev_ct_definition)
|
33
|
+
content_type_definition.merge!(prev_ct_definition)
|
34
|
+
end
|
35
|
+
|
36
|
+
result[:contentTypes] << content_type_definition
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.create_entries(resource, content_type_definition, result)
|
40
|
+
result[:entries][content_type_definition[:id]] ||= []
|
41
|
+
resource.all.each do |entry|
|
42
|
+
create_entry(entry, content_type_definition, result)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.previous_entry(entry_definition, content_type_definition, result)
|
47
|
+
result[:entries][content_type_definition[:id]].find do |e|
|
48
|
+
e[:sys][:id] == entry_definition[:sys][:id]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.merge_entries(entry_definition, content_type_definition, result)
|
53
|
+
prev_entry = previous_entry(
|
54
|
+
entry_definition,
|
55
|
+
content_type_definition,
|
56
|
+
result
|
57
|
+
)
|
58
|
+
|
59
|
+
return unless prev_entry
|
60
|
+
|
61
|
+
result[:entries][content_type_definition[:id]].delete(prev_entry)
|
62
|
+
entry_definition.merge!(prev_entry)
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.create_entry(entry, content_type_definition, result)
|
66
|
+
entry_definition = entry.to_bootstrap
|
67
|
+
|
68
|
+
merge_entries(entry_definition, content_type_definition, result)
|
69
|
+
|
70
|
+
result[:assets].concat(
|
71
|
+
entry.associated_assets
|
72
|
+
) unless entry.associated_assets.empty?
|
73
|
+
|
74
|
+
result[:entries][content_type_definition[:id]] << entry_definition
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.generate_json!
|
78
|
+
JSON.pretty_generate(generate_json)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|