├── spec
├── fixtures
│ ├── ini_fixtures
│ │ ├── no_token.ini
│ │ ├── no_global.ini
│ │ ├── no_org.ini
│ │ ├── contentfulrc.ini
│ │ ├── orgid.ini
│ │ └── sections.ini
│ ├── json_fixtures
│ │ ├── invalid.json
│ │ ├── high.json
│ │ ├── low.json
│ │ ├── ok_version.json
│ │ ├── links.json
│ │ ├── asset_no_transform.json
│ │ ├── no_ids.json
│ │ ├── update_space_localized.json
│ │ ├── object.json
│ │ ├── simple.json
│ │ ├── display_field.json
│ │ ├── f3abi4dqvrhg_preview.json
│ │ ├── assets_draft.json
│ │ ├── environment_template.json
│ │ ├── processed.json
│ │ ├── issue_22.json
│ │ ├── wl1z0pal05vy_content_types_only.json
│ │ ├── cfexampleapi_cat.json
│ │ └── cfexampleapi_cat_human.json
│ └── vcr_fixtures
│ │ ├── check_staging_environment_status.yml
│ │ ├── generate_json_content_types_only.yml
│ │ ├── check_created_space.yml
│ │ ├── create_space.yml
│ │ ├── check_update_space_localized.yml
│ │ ├── check_original_staging_environment_status.yml
│ │ └── multiple_organizations.yml
├── contentful
│ └── bootstrap
│ │ ├── support_spec.rb
│ │ ├── templates
│ │ ├── links
│ │ │ ├── asset_spec.rb
│ │ │ ├── entry_spec.rb
│ │ │ └── base_spec.rb
│ │ ├── blog_spec.rb
│ │ ├── gallery_spec.rb
│ │ ├── catalogue_spec.rb
│ │ └── base_spec.rb
│ │ ├── generator_spec.rb
│ │ ├── commands
│ │ ├── generate_token_spec.rb
│ │ ├── generate_json_spec.rb
│ │ ├── base_spec.rb
│ │ ├── update_space_spec.rb
│ │ └── create_space_spec.rb
│ │ ├── server_spec.rb
│ │ ├── command_runner_spec.rb
│ │ └── token_spec.rb
└── spec_helper.rb
├── Gemfile
├── lib
└── contentful
│ ├── bootstrap
│ ├── templates
│ │ ├── links.rb
│ │ ├── links
│ │ │ ├── asset.rb
│ │ │ ├── entry.rb
│ │ │ └── base.rb
│ │ ├── blog.rb
│ │ ├── gallery.rb
│ │ ├── json_template.rb
│ │ ├── base.rb
│ │ └── catalogue.rb
│ ├── version.rb
│ ├── templates.rb
│ ├── commands.rb
│ ├── constants.rb
│ ├── support.rb
│ ├── commands
│ │ ├── generate_token.rb
│ │ ├── generate_json.rb
│ │ ├── update_space.rb
│ │ ├── base.rb
│ │ └── create_space.rb
│ ├── command_runner.rb
│ ├── token.rb
│ ├── server.rb
│ └── generator.rb
│ └── bootstrap.rb
├── Rakefile
├── Guardfile
├── .gitignore
├── .travis.yml
├── .rubocop.yml
├── LICENSE
├── contentful_bootstrap.gemspec
├── CONTRIBUTING.md
├── .rubocop_todo.yml
├── bin
└── contentful_bootstrap
├── examples
└── templates
│ └── catalogue.json
└── CHANGELOG.md
/spec/fixtures/ini_fixtures/no_token.ini:
--------------------------------------------------------------------------------
1 | [global]
2 |
--------------------------------------------------------------------------------
/spec/fixtures/ini_fixtures/no_global.ini:
--------------------------------------------------------------------------------
1 | [other_section]
2 | SPACE_ID = blah
3 |
--------------------------------------------------------------------------------
/spec/fixtures/ini_fixtures/no_org.ini:
--------------------------------------------------------------------------------
1 | [global]
2 | CONTENTFUL_MANAGEMENT_ACCESS_TOKEN = foo
3 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # A sample Gemfile
2 | source "https://rubygems.org"
3 |
4 | gemspec
5 |
6 | gem 'pry'
7 |
--------------------------------------------------------------------------------
/spec/fixtures/ini_fixtures/contentfulrc.ini:
--------------------------------------------------------------------------------
1 | [global]
2 | CONTENTFUL_MANAGEMENT_ACCESS_TOKEN = foobar
3 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/invalid.json:
--------------------------------------------------------------------------------
1 | {
2 | "content_types": [],
3 | "assets": [],
4 | "entries": {}
5 | }
6 |
--------------------------------------------------------------------------------
/spec/fixtures/ini_fixtures/orgid.ini:
--------------------------------------------------------------------------------
1 | [global]
2 | CONTENTFUL_MANAGEMENT_ACCESS_TOKEN = foobar
3 | CONTENTFUL_ORGANIZATION_ID = my_org
4 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/high.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 4,
3 | "content_types": [],
4 | "assets": [],
5 | "entries": {}
6 | }
7 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/low.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "content_types": [],
4 | "assets": [],
5 | "entries": {}
6 | }
7 |
--------------------------------------------------------------------------------
/spec/fixtures/ini_fixtures/sections.ini:
--------------------------------------------------------------------------------
1 | [global]
2 | CONTENTFUL_MANAGEMENT_ACCESS_TOKEN = foo
3 |
4 | [other_section]
5 | SPACE_ID = blah
6 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/ok_version.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "content_types": [],
4 | "assets": [],
5 | "entries": {}
6 | }
7 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/templates/links.rb:
--------------------------------------------------------------------------------
1 | require 'contentful/bootstrap/templates/links/entry'
2 | require 'contentful/bootstrap/templates/links/asset'
3 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/version.rb:
--------------------------------------------------------------------------------
1 | module Contentful
2 | module Bootstrap
3 | VERSION = '3.12.0'.freeze
4 |
5 | def self.major_version
6 | VERSION.split('.').first.to_i
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 |
3 | require 'rspec/core/rake_task'
4 | RSpec::Core::RakeTask.new do |t|
5 | t.rspec_opts = '--format documentation --color'
6 | end
7 |
8 | task test: :spec
9 | task default: :spec
10 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/templates.rb:
--------------------------------------------------------------------------------
1 | require 'contentful/bootstrap/templates/blog'
2 | require 'contentful/bootstrap/templates/catalogue'
3 | require 'contentful/bootstrap/templates/gallery'
4 | require 'contentful/bootstrap/templates/json_template'
5 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/commands.rb:
--------------------------------------------------------------------------------
1 | require 'contentful/bootstrap/commands/create_space'
2 | require 'contentful/bootstrap/commands/update_space'
3 | require 'contentful/bootstrap/commands/generate_token'
4 | require 'contentful/bootstrap/commands/generate_json'
5 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/constants.rb:
--------------------------------------------------------------------------------
1 | module Contentful
2 | module Bootstrap
3 | module Constants
4 | OAUTH_APP_ID = 'a19770bea126e6d596d8599ff42e14173409e3e252895b78d0cb289c10586276'.freeze
5 | OAUTH_CALLBACK_URL = 'http://localhost:5123/oauth_callback'.freeze
6 | end
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap.rb:
--------------------------------------------------------------------------------
1 | require 'contentful/bootstrap/token'
2 | require 'contentful/bootstrap/server'
3 | require 'contentful/bootstrap/version'
4 | require 'contentful/bootstrap/commands'
5 | require 'contentful/bootstrap/templates'
6 | require 'contentful/bootstrap/command_runner'
7 | require 'contentful/bootstrap/templates/links'
8 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/links.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "content_types": [],
4 | "assets": [],
5 | "entries": {
6 | "cat": [
7 | {
8 | "sys": { "id": "foo" },
9 | "fields": {
10 | "link": {
11 | "id": "foobar",
12 | "linkType": "Entry"
13 | }
14 | }
15 | }
16 | ]
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/templates/links/asset.rb:
--------------------------------------------------------------------------------
1 | require 'contentful/management'
2 | require 'contentful/bootstrap/templates/links/base'
3 |
4 | module Contentful
5 | module Bootstrap
6 | module Templates
7 | module Links
8 | class Asset < Base
9 | def management_class
10 | Contentful::Management::Asset
11 | end
12 | end
13 | end
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/templates/links/entry.rb:
--------------------------------------------------------------------------------
1 | require 'contentful/management'
2 | require 'contentful/bootstrap/templates/links/base'
3 |
4 | module Contentful
5 | module Bootstrap
6 | module Templates
7 | module Links
8 | class Entry < Base
9 | def management_class
10 | Contentful::Management::Entry
11 | end
12 | end
13 | end
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/asset_no_transform.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "contentTypes": [],
4 | "entries": {},
5 | "assets": [
6 | {
7 | "id": "my_asset",
8 | "title": "My Asset",
9 | "file": {
10 | "filename": "some_filename.svg",
11 | "contentType": "image/svg+xml",
12 | "url": "https://upload.wikimedia.org/wikipedia/commons/3/30/Vector-based_example.svg"
13 | }
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/no_ids.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "contentTypes": [
4 | {
5 | "id": "cat",
6 | "name": "Cat",
7 | "displayField": "name",
8 | "fields": [
9 | {
10 | "id": "name",
11 | "name": "Name",
12 | "type": "Symbol"
13 | }
14 | ]
15 | }
16 | ],
17 | "entries": {
18 | "cat": [
19 | {
20 | "name": "Nyan Cat"
21 | }
22 | ]
23 | }
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/spec/contentful/bootstrap/support_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | class Double
4 | def write
5 | $stderr.write('foo\n')
6 | end
7 |
8 | def muted_write
9 | Contentful::Bootstrap::Support.silence_stderr do
10 | write
11 | end
12 | end
13 | end
14 |
15 | describe Contentful::Bootstrap::Support do
16 | subject { Double.new }
17 |
18 | describe 'module methods' do
19 | it '#silence_stderr' do
20 | expect { subject.write }.to output('foo\n').to_stderr
21 |
22 | expect { subject.muted_write }.to_not output.to_stderr
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/Guardfile:
--------------------------------------------------------------------------------
1 | group :green_red_refactor, halt_on_fail: true do
2 | guard :rspec, cmd: "bundle exec rspec" do
3 | require "guard/rspec/dsl"
4 | dsl = Guard::RSpec::Dsl.new(self)
5 |
6 | # Feel free to open issues for suggestions and improvements
7 |
8 | # RSpec files
9 | rspec = dsl.rspec
10 | watch(rspec.spec_helper) { rspec.spec_dir }
11 | watch(rspec.spec_support) { rspec.spec_dir }
12 | watch(rspec.spec_files)
13 |
14 | # Ruby files
15 | ruby = dsl.ruby
16 | dsl.watch_spec_files_for(ruby.lib_files)
17 | end
18 |
19 | guard :rubocop, cmd: "rubocop" do
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
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 | /vendor/bundle
26 | /lib/bundler/man/
27 |
28 | # for a library or gem, you might want to ignore these files since the code is
29 | # intended to run in multiple environments; otherwise, check them in:
30 | Gemfile.lock
31 | # .ruby-version
32 | # .ruby-gemset
33 |
34 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
35 | .rvmrc
36 |
37 | .contentful_token
38 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | rvm:
3 | - 2.5.1
4 | - 2.6.1
5 | - 2.7.0
6 | notifications:
7 | slack:
8 | secure: Pw7UFAHD2fSAUtjf0P/W0Wg91TVTMAV8Gl04B/XhFrYpjVtifsGxWipOBDvJr9S7ep2UFbR3YbBABRWAJAigoDHvwEaf6eytEFT9LGsMwmPQOtb5NlJt0+Qhvys3sZUM0KskBORQ+Etz1M0O4e9baC5Tz1V8GzI/lNxIbpIM7dwQU18st+9sUTjBh6UFJDRXd0Qm28VAHk7fjlhpdKVH+5F4iEnFfRw414wVja8kPVmVDHCEqHPWS1JyHIj4eU8E50yBi3WjC60eE2TjAsCA9wc9gzvxb4tN5vXyuIVtqopPW4JPIBCaIv+j07biNAiLrTccQ5vv9RD9NTPmXANX9LyF/qD9I+itFl8xLatnwy1JXtPpi/S+OlQj6lluF17F+3c8g64vVgTYXyCDgCqpzSTj/KsTfwC9PkVGh8bmZMLtfUyd4IiMw8qiXt2opEmv90K1aodcixrpL2Db/OS2aSo4djRfMrjMlUN7tGM7hg9yQfAaZQKCPjof4C/XGG9me9FX+BvnNf9Xl6/t6uHFnkBfF969UX7gk7tzkwyyqpOp/FQ+6c+EiZF7wnVotbW49FBgEmZN62JsXSquDBlDfWrALeX4y9dZ+uYrWBd4q47N+kZUtogwf3f3gj0pE+K1XmKV549HkiHZ9MHcFtOwNu7KrCFMmUSPGIUKSotbg/c=
9 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/update_space_localized.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "contentTypes": [
4 | {
5 | "id": "test",
6 | "name": "Test",
7 | "displayField": "text",
8 | "fields": [
9 | {
10 | "id": "text",
11 | "name": "Text",
12 | "type": "Symbol"
13 | }
14 | ]
15 | }
16 | ],
17 | "assets": [
18 |
19 | ],
20 | "entries": {
21 | "test": [
22 | {
23 | "sys": {
24 | "id": "test_1"
25 | },
26 | "fields": {
27 | "text": "Foo"
28 | }
29 | },
30 | {
31 | "sys": {
32 | "id": "test_2"
33 | },
34 | "fields": {
35 | "text": "Bar"
36 | }
37 | }
38 | ]
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/object.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "contentTypes": [
4 | {
5 | "id": "test",
6 | "name": "Test",
7 | "displayField": "name",
8 | "fields": [
9 | {
10 | "id": "name",
11 | "name": "Name",
12 | "type": "Symbol"
13 | },
14 | {
15 | "id": "object",
16 | "name": "Object",
17 | "type": "Object"
18 | }
19 | ]
20 | }
21 | ],
22 | "assets": [],
23 | "entries": {
24 | "test": [
25 | {
26 | "sys": { "id": "foo" },
27 | "fields": {
28 | "name": "test",
29 | "object": {
30 | "foo": "bar",
31 | "baz": "qux"
32 | }
33 | }
34 | }
35 | ]
36 | }
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/simple.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "contentTypes": [
4 | {
5 | "id": "cat",
6 | "name": "Cat",
7 | "displayField": "name",
8 | "fields": [
9 | {
10 | "id": "name",
11 | "name": "Name",
12 | "type": "Symbol"
13 | }
14 | ]
15 | }
16 | ],
17 | "assets": [
18 | {
19 | "id": "cat_asset",
20 | "title": "Cat",
21 | "file": {
22 | "filename": "cat",
23 | "url": "https://somecat.com/my_cat.jpeg"
24 | }
25 | }
26 | ],
27 | "entries": {
28 | "cat": [
29 | {
30 | "sys": {
31 | "id": "nyancat"
32 | },
33 | "fields": {
34 | "name": "Nyan Cat"
35 | }
36 | }
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/display_field.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "contentTypes": [
4 | {
5 | "id": "cat",
6 | "name": "Cat",
7 | "display_field": "name",
8 | "fields": [
9 | {
10 | "id": "name",
11 | "name": "Name",
12 | "type": "Symbol"
13 | }
14 | ]
15 | }
16 | ],
17 | "assets": [
18 | {
19 | "id": "cat_asset",
20 | "title": "Cat",
21 | "file": {
22 | "filename": "cat",
23 | "url": "https://somecat.com/my_cat.jpeg"
24 | }
25 | }
26 | ],
27 | "entries": {
28 | "cat": [
29 | {
30 | "sys": {
31 | "id": "nyancat"
32 | },
33 | "fields": {
34 | "name": "Nyan Cat"
35 | }
36 | }
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/f3abi4dqvrhg_preview.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "contentTypes": [
4 | {
5 | "id": "testContentType",
6 | "name": "Test Content Type",
7 | "displayField": "title",
8 | "fields": [
9 | {
10 | "id": "title",
11 | "name": "Title",
12 | "type": "Symbol"
13 | }
14 | ]
15 | }
16 | ],
17 | "assets": [
18 |
19 | ],
20 | "entries": {
21 | "testContentType": [
22 | {
23 | "sys": {
24 | "id": "1uT4AXWJ1me40QsAgAeSIi"
25 | },
26 | "fields": {
27 | "title": "Published Entry"
28 | }
29 | },
30 | {
31 | "sys": {
32 | "id": "49iT3aM9aUkK44KwuuyQow"
33 | },
34 | "fields": {
35 | "title": "Draft Entry"
36 | }
37 | }
38 | ]
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/support.rb:
--------------------------------------------------------------------------------
1 | require 'stringio'
2 |
3 | module Contentful
4 | module Bootstrap
5 | module Support
6 | def self.camel_case(a_string)
7 | a_string.split('_').each_with_object([]) { |e, a| a.push(a.empty? ? e : e.capitalize) }.join
8 | end
9 |
10 | def self.silence_stderr
11 | old_stderr = $stderr
12 | $stderr = StringIO.new
13 | yield
14 | ensure
15 | $stderr = old_stderr
16 | end
17 |
18 | def self.output(text = nil, quiet = false)
19 | if text.nil?
20 | puts unless quiet
21 | return
22 | end
23 |
24 | puts text unless quiet
25 | end
26 |
27 | def self.input(prompt_text, no_input = false)
28 | return if no_input
29 |
30 | print prompt_text
31 | answer = gets.chomp
32 | yield answer if block_given?
33 | end
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/assets_draft.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "contentTypes": [
4 | {
5 | "id": "testContentType",
6 | "name": "Test Content Type",
7 | "displayField": "title",
8 | "fields": [
9 | {
10 | "id": "title",
11 | "name": "Title",
12 | "type": "Symbol"
13 | }
14 | ]
15 | }
16 | ],
17 | "assets": [
18 | {
19 | "id": "3JX5vcHRhmAmaYwWq0S0wa",
20 | "title": "Cat",
21 | "file": {
22 | "filename": "cat",
23 | "url": "https://images.contentful.com/f3abi4dqvrhg/3JX5vcHRhmAmaYwWq0S0wa/9ba45c1084d8530702bcd0de5617c59b/cat.jpg"
24 | }
25 | }
26 | ],
27 | "entries": {
28 | "testContentType": [
29 | {
30 | "sys": {
31 | "id": "1uT4AXWJ1me40QsAgAeSIi"
32 | },
33 | "fields": {
34 | "title": "Published Entry"
35 | }
36 | }
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/templates/links/base.rb:
--------------------------------------------------------------------------------
1 | require 'contentful/management'
2 |
3 | module Contentful
4 | module Bootstrap
5 | module Templates
6 | module Links
7 | class Base
8 | attr_reader :id
9 | def initialize(id)
10 | @id = id
11 | end
12 |
13 | def link_type
14 | self.class.name.split('::').last
15 | end
16 |
17 | def type
18 | Contentful::Management::ContentType::LINK
19 | end
20 |
21 | def to_management_object
22 | object = management_class.new
23 | object.id = id
24 | object
25 | end
26 |
27 | def management_class
28 | raise 'must implement'
29 | end
30 |
31 | def ==(other)
32 | return false unless other.is_a? self.class
33 | other.id == id
34 | end
35 | end
36 | end
37 | end
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/environment_template.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "contentTypes": [
4 | {
5 | "id": "foo",
6 | "name": "Foo",
7 | "displayField": "name",
8 | "fields": [
9 | {
10 | "id": "name",
11 | "name": "Name",
12 | "type": "Symbol"
13 | },
14 | {
15 | "id": "content",
16 | "name": "Content",
17 | "type": "Text"
18 | }
19 | ]
20 | }
21 | ],
22 | "assets": [
23 |
24 | ],
25 | "entries": {
26 | "foo": [
27 | {
28 | "sys": {
29 | "id": "6yVdruR4GsKO2iKOqQS2CS"
30 | },
31 | "fields": {
32 | "name": "Test",
33 | "content": "Some content"
34 | }
35 | },
36 | {
37 | "sys": {
38 | "id": "foo_update"
39 | },
40 | "fields": {
41 | "name": "Test updated",
42 | "content": "Some content"
43 | }
44 | }
45 | ]
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | inherit_from: .rubocop_todo.yml
2 |
3 | AllCops:
4 | Exclude:
5 | - contentful_bootstrap.gemspec
6 | - spec/**/*
7 | - examples/**/*
8 | - Guardfile
9 | - Gemfile
10 | - Rakefile
11 |
12 | Style/Documentation:
13 | Exclude:
14 | - 'spec/**/*'
15 | - 'test/**/*'
16 | - 'lib/contentful/bootstrap/commands.rb'
17 | - 'lib/contentful/bootstrap/constants.rb'
18 | - 'lib/contentful/bootstrap/generator.rb'
19 | - 'lib/contentful/bootstrap/server.rb'
20 | - 'lib/contentful/bootstrap/support.rb'
21 | - 'lib/contentful/bootstrap/templates/base.rb'
22 | - 'lib/contentful/bootstrap/templates/blog.rb'
23 | - 'lib/contentful/bootstrap/templates/catalogue.rb'
24 | - 'lib/contentful/bootstrap/templates/gallery.rb'
25 | - 'lib/contentful/bootstrap/templates/json_template.rb'
26 | - 'lib/contentful/bootstrap/templates/links/asset.rb'
27 | - 'lib/contentful/bootstrap/templates/links/base.rb'
28 | - 'lib/contentful/bootstrap/templates/links/entry.rb'
29 | - 'lib/contentful/bootstrap/token.rb'
30 | - 'lib/contentful/bootstrap/version.rb'
31 |
--------------------------------------------------------------------------------
/spec/contentful/bootstrap/templates/links/asset_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Contentful::Bootstrap::Templates::Links::Asset do
4 | subject { Contentful::Bootstrap::Templates::Links::Asset.new 'foo' }
5 |
6 | describe 'instance methods' do
7 | it '#link_type' do
8 | expect(subject.link_type).to eq 'Asset'
9 | end
10 |
11 | it '#type' do
12 | expect(subject.type).to eq 'Link'
13 | end
14 |
15 | it '#to_management_object' do
16 | expect(subject.to_management_object).to be_a Contentful::Management::Asset
17 | end
18 |
19 | it '#management_class' do
20 | expect(subject.management_class).to eq Contentful::Management::Asset
21 | end
22 |
23 | describe '#==' do
24 | it 'false when different type' do
25 | expect(subject == 2).to be_falsey
26 | end
27 |
28 | it 'false when different id' do
29 | expect(subject == described_class.new('bar')).to be_falsey
30 | end
31 |
32 | it 'true when same id' do
33 | expect(subject == described_class.new('foo')).to be_truthy
34 | end
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/spec/contentful/bootstrap/templates/links/entry_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Contentful::Bootstrap::Templates::Links::Entry do
4 | subject { Contentful::Bootstrap::Templates::Links::Entry.new 'foo' }
5 |
6 | describe 'instance methods' do
7 | it '#link_type' do
8 | expect(subject.link_type).to eq 'Entry'
9 | end
10 |
11 | it '#type' do
12 | expect(subject.type).to eq 'Link'
13 | end
14 |
15 | it '#to_management_object' do
16 | expect(subject.to_management_object).to be_a Contentful::Management::Entry
17 | end
18 |
19 | it '#management_class' do
20 | expect(subject.management_class).to eq Contentful::Management::Entry
21 | end
22 |
23 | describe '#==' do
24 | it 'false when different type' do
25 | expect(subject == 2).to be_falsey
26 | end
27 |
28 | it 'false when different id' do
29 | expect(subject == described_class.new('bar')).to be_falsey
30 | end
31 |
32 | it 'true when same id' do
33 | expect(subject == described_class.new('foo')).to be_truthy
34 | end
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015
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 |
23 |
--------------------------------------------------------------------------------
/spec/contentful/bootstrap/templates/blog_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Contentful::Bootstrap::Templates::Blog do
4 | let(:space) { Contentful::Management::Space.new }
5 | subject { described_class.new(space, 'master') }
6 |
7 | before :each do
8 | environment_proxy = Object.new
9 | allow(space).to receive(:environments) { environment_proxy }
10 |
11 | environment = Object.new
12 | allow(environment_proxy).to receive(:find) { environment }
13 | end
14 |
15 | describe 'content types' do
16 | it 'has Post content type' do
17 | expect(subject.content_types.detect { |ct| ct['id'] == 'post' }).to be_truthy
18 | end
19 |
20 | it 'has Author content type' do
21 | expect(subject.content_types.detect { |ct| ct['id'] == 'author' }).to be_truthy
22 | end
23 | end
24 |
25 | describe 'entries' do
26 | it 'has 2 posts' do
27 | expect(subject.entries['post'].size).to eq 2
28 | end
29 |
30 | it 'has 2 authors' do
31 | expect(subject.entries['author'].size).to eq 2
32 | end
33 | end
34 |
35 | describe 'assets' do
36 | it 'has no assets' do
37 | expect(subject.assets.empty?).to be_truthy
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/spec/contentful/bootstrap/templates/links/base_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Contentful::Bootstrap::Templates::Links::Base do
4 | subject { described_class.new 'foo' }
5 |
6 | describe 'instance methods' do
7 | it '#link_type' do
8 | expect(subject.link_type).to eq 'Base'
9 | end
10 |
11 | it '#type' do
12 | expect(subject.type).to eq 'Link'
13 | end
14 |
15 | describe 'abstract methods' do
16 | it '#to_management_object' do
17 | expect { subject.to_management_object }.to raise_error "must implement"
18 | end
19 |
20 | it '#management_class' do
21 | expect { subject.management_class }.to raise_error "must implement"
22 | end
23 |
24 | describe '#==' do
25 | it 'false when different type' do
26 | expect(subject == 2).to be_falsey
27 | end
28 |
29 | it 'false when different id' do
30 | expect(subject == described_class.new('bar')).to be_falsey
31 | end
32 |
33 | it 'true when same id' do
34 | expect(subject == described_class.new('foo')).to be_truthy
35 | end
36 | end
37 | end
38 | end
39 |
40 | describe 'properties' do
41 | it ':id' do
42 | expect(subject.id).to eq 'foo'
43 | end
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/spec/contentful/bootstrap/templates/gallery_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Contentful::Bootstrap::Templates::Gallery do
4 | let(:space) { Contentful::Management::Space.new }
5 | subject { described_class.new(space) }
6 |
7 | before :each do
8 | environment_proxy = Object.new
9 | allow(space).to receive(:environments) { environment_proxy }
10 |
11 | environment = Object.new
12 | allow(environment_proxy).to receive(:find) { environment }
13 | end
14 |
15 | describe 'content types' do
16 | it 'has Author content type' do
17 | expect(subject.content_types.detect { |ct| ct['id'] == 'author' }).to be_truthy
18 | end
19 |
20 | it 'has Image content type' do
21 | expect(subject.content_types.detect { |ct| ct['id'] == 'image' }).to be_truthy
22 | end
23 |
24 | it 'has Gallery content type' do
25 | expect(subject.content_types.detect { |ct| ct['id'] == 'gallery' }).to be_truthy
26 | end
27 | end
28 |
29 | describe 'entries' do
30 | it 'has 1 author' do
31 | expect(subject.entries['author'].size).to eq 1
32 | end
33 |
34 | it 'has 2 images' do
35 | expect(subject.entries['image'].size).to eq 2
36 | end
37 |
38 | it 'has 1 gallery' do
39 | expect(subject.entries['gallery'].size).to eq 1
40 | end
41 | end
42 |
43 | describe 'assets' do
44 | it 'has 2 assets' do
45 | expect(subject.assets.size).to eq 2
46 | end
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/spec/contentful/bootstrap/templates/catalogue_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Contentful::Bootstrap::Templates::Catalogue do
4 | let(:space) { Contentful::Management::Space.new }
5 | subject { described_class.new(space, 'master') }
6 |
7 | before :each do
8 | environment_proxy = Object.new
9 | allow(space).to receive(:environments) { environment_proxy }
10 |
11 | environment = Object.new
12 | allow(environment_proxy).to receive(:find) { environment }
13 | end
14 |
15 | describe 'content types' do
16 | it 'has Brand content type' do
17 | expect(subject.content_types.detect { |ct| ct['id'] == 'brand' }).to be_truthy
18 | end
19 |
20 | it 'has Category content type' do
21 | expect(subject.content_types.detect { |ct| ct['id'] == 'category' }).to be_truthy
22 | end
23 |
24 | it 'has Product content type' do
25 | expect(subject.content_types.detect { |ct| ct['id'] == 'product' }).to be_truthy
26 | end
27 | end
28 |
29 | describe 'entries' do
30 | it 'has 2 brands' do
31 | expect(subject.entries['brand'].size).to eq 2
32 | end
33 |
34 | it 'has 2 categories' do
35 | expect(subject.entries['category'].size).to eq 2
36 | end
37 |
38 | it 'has 2 products' do
39 | expect(subject.entries['product'].size).to eq 2
40 | end
41 | end
42 |
43 | describe 'assets' do
44 | it 'has 6 assets' do
45 | expect(subject.assets.size).to eq 6
46 | end
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/processed.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "contentTypes": [
4 | {
5 | "id": "cat",
6 | "name": "Cat",
7 | "displayField": "name",
8 | "fields": [
9 | {
10 | "id": "name",
11 | "name": "Name",
12 | "type": "Symbol"
13 | }
14 | ]
15 | },
16 | {
17 | "id": "dog",
18 | "bootstrapProcessed": true,
19 | "name": "Dog",
20 | "displayField": "name",
21 | "fields": [
22 | {
23 | "id": "name",
24 | "name": "Name",
25 | "type": "Symbol"
26 | }
27 | ]
28 | }
29 | ],
30 | "assets": [
31 | {
32 | "id": "cat_asset",
33 | "title": "Cat",
34 | "file": {
35 | "filename": "cat",
36 | "url": "https://somecat.com/my_cat.jpeg"
37 | }
38 | },
39 | {
40 | "id": "dog_asset",
41 | "bootstrapProcessed": true,
42 | "title": "Dog",
43 | "file": {
44 | "filename": "Dog",
45 | "url": "https://somedog.com/my_dog.jpeg"
46 | }
47 | }
48 | ],
49 | "entries": {
50 | "cat": [
51 | {
52 | "sys": {
53 | "id": "nyancat"
54 | },
55 | "fields": {
56 | "name": "Nyan Cat"
57 | }
58 | }
59 | ],
60 | "dog": [
61 | {
62 | "sys": {
63 | "id": "doge",
64 | "bootstrapProcessed": true
65 | },
66 | "fields": {
67 | "name": "Doge"
68 | }
69 | }
70 | ]
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/spec/contentful/bootstrap/templates/base_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Contentful::Bootstrap::Templates::Base do
4 | let(:space) { Contentful::Management::Space.new }
5 | subject { described_class.new(space, 'master', true) }
6 |
7 | before :each do
8 | environment_proxy = Object.new
9 | allow(space).to receive(:environments) { environment_proxy }
10 |
11 | environment = Object.new
12 | allow(environment_proxy).to receive(:find) { environment }
13 | end
14 |
15 | describe 'instance methods' do
16 | it '#content_types' do
17 | expect(subject.content_types).to eq []
18 | end
19 |
20 | it '#entries' do
21 | expect(subject.entries).to eq({})
22 | end
23 |
24 | it '#assets' do
25 | expect(subject.assets).to eq []
26 | end
27 |
28 | describe '#run' do
29 | it 'calls create for each kind of object' do
30 | ['content_types', 'assets', 'entries'].each do |name|
31 | expect(subject).to receive("create_#{name}".to_sym)
32 | end
33 |
34 | subject.run
35 | end
36 |
37 | it 'calls after_run when done' do
38 | expect(subject).to receive(:after_run)
39 |
40 | subject.run
41 | end
42 |
43 | context 'with skip_content_types set to true' do
44 | subject { described_class.new(space, 'master', true, true) }
45 |
46 | it 'doesnt call create_content_type if skip_content_types is sent' do
47 | expect(subject).to receive(:create_entries)
48 | expect(subject).to receive(:create_assets)
49 | expect(subject).not_to receive(:create_content_types)
50 |
51 | subject.run
52 | end
53 | end
54 | end
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/contentful_bootstrap.gemspec:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | lib = File.expand_path('../lib', __FILE__)
3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4 | require 'contentful/bootstrap/version'
5 |
6 | Gem::Specification.new do |spec|
7 | spec.name = "contentful_bootstrap"
8 | spec.version = Contentful::Bootstrap::VERSION
9 | spec.authors = ["David Litvak Bruno"]
10 | spec.email = ["david.litvakb@gmail.com"]
11 | spec.summary = %q{Contentful CLI tool for getting started with Contentful}
12 | spec.homepage = "https://www.contentful.com"
13 | spec.license = "MIT"
14 |
15 | spec.files = `git ls-files -z`.split("\x0")
16 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18 | spec.require_paths = ["lib"]
19 |
20 | spec.add_development_dependency 'bundler'
21 | spec.add_development_dependency 'rake', '>= 12.3.3'
22 | spec.add_development_dependency 'public_suffix', '< 1.5'
23 | spec.add_development_dependency 'rspec', '~> 3'
24 | spec.add_development_dependency 'vcr', '~> 2.9'
25 | spec.add_development_dependency 'webmock', '~> 1.24'
26 | spec.add_development_dependency 'rr'
27 | spec.add_development_dependency 'simplecov'
28 | spec.add_development_dependency 'guard'
29 | spec.add_development_dependency 'guard-rspec'
30 | spec.add_development_dependency 'listen', '~> 3.0'
31 | spec.add_development_dependency 'guard-rubocop'
32 | spec.add_development_dependency 'rubocop', '~> 0.49'
33 |
34 | spec.add_runtime_dependency 'launchy'
35 | spec.add_runtime_dependency 'contentful-management', '~> 2.0', '>= 2.0.2'
36 | spec.add_runtime_dependency 'contentful', '~> 2.6.0'
37 | spec.add_runtime_dependency 'inifile'
38 | end
39 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/commands/generate_token.rb:
--------------------------------------------------------------------------------
1 | require 'contentful/management'
2 | require 'contentful/management/error'
3 | require 'contentful/management/request'
4 | require 'contentful/bootstrap/version'
5 | require 'contentful/bootstrap/commands/base'
6 |
7 | module Contentful
8 | module Bootstrap
9 | module Commands
10 | class GenerateToken < Base
11 | attr_reader :token_name
12 | def initialize(token, space, options = {})
13 | @token_name = options.fetch(:token_name, 'Bootstrap Token')
14 |
15 | super(token, space, options)
16 | end
17 |
18 | def run
19 | output 'Creating Delivery API Token'
20 |
21 | fetch_space
22 |
23 | access_token = fetch_access_token
24 |
25 | output "Token '#{token_name}' created! - '#{access_token}'"
26 | Support.input('Do you want to write the Delivery Token to your configuration file? (Y/n): ', no_input) do |answer|
27 | unless answer.casecmp('n').zero?
28 | @token.write_access_token(@actual_space.name, access_token)
29 | @token.write_space_id(@actual_space.name, @actual_space.id)
30 | end
31 | end
32 |
33 | access_token
34 | end
35 |
36 | def fetch_space
37 | @actual_space = if @space.is_a?(String)
38 | client.spaces.find(@space)
39 | else
40 | @space
41 | end
42 | end
43 |
44 | def fetch_access_token
45 | @actual_space.api_keys.create(
46 | name: token_name,
47 | description: "Created with 'contentful_bootstrap.rb v#{Contentful::Bootstrap::VERSION}'"
48 | ).access_token
49 | end
50 | end
51 | end
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
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-labs/contentful_bootstrap.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/
44 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/command_runner.rb:
--------------------------------------------------------------------------------
1 | require 'contentful/bootstrap/token'
2 | require 'contentful/bootstrap/commands'
3 |
4 | module Contentful
5 | module Bootstrap
6 | class CommandRunner
7 | attr_reader :config_path, :token
8 |
9 | def initialize(config_path = '')
10 | @config_path = config_path
11 | @token = Token.new(config_path)
12 | end
13 |
14 | def create_space(space_name, options = {})
15 | Contentful::Bootstrap::Commands::CreateSpace.new(
16 | @token, space_name, options
17 | ).run
18 | end
19 |
20 | def update_space(space_id, options = {})
21 | Contentful::Bootstrap::Commands::UpdateSpace.new(
22 | @token, space_id, options
23 | ).run
24 | end
25 |
26 | def generate_token(space, options = {})
27 | Contentful::Bootstrap::Commands::GenerateToken.new(
28 | @token, space, options
29 | ).run
30 | end
31 |
32 | def generate_json(space_id, options = {})
33 | filename = options.fetch(:filename, nil)
34 | access_token = options.fetch(:access_token, nil)
35 | environment = options.fetch(:environment, 'master')
36 | content_types_only = options.fetch(:content_types_only, false)
37 | quiet = options.fetch(:quiet, false)
38 | use_preview = options.fetch(:use_preview, false)
39 | content_type_ids = options.fetch(:content_type_ids, [])
40 |
41 | raise 'Access Token required' if access_token.nil?
42 |
43 | Contentful::Bootstrap::Commands::GenerateJson.new(
44 | space_id,
45 | access_token,
46 | environment,
47 | filename,
48 | content_types_only,
49 | quiet,
50 | use_preview,
51 | content_type_ids
52 | ).run
53 | end
54 | end
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/spec/contentful/bootstrap/generator_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Contentful::Bootstrap::Generator do
4 | subject { described_class.new('wl1z0pal05vy', '48d7db7d4cd9d09df573c251d456f4acc72141b92f36e57f8684b36cf5cfff6e', 'master', false, false, []) }
5 |
6 | describe 'user agent headers' do
7 | it 'client has proper integration data' do
8 | expect(subject.client.app_info).to eq(name: 'bootstrap', version: Contentful::Bootstrap::VERSION)
9 | end
10 | end
11 |
12 | describe 'JSON template generator' do
13 | it 'can generate a JSON template for a given space' do
14 | vcr('generate_json') {
15 | json_fixture('wl1z0pal05vy') { |json|
16 | expect(subject.generate_json).to eq JSON.pretty_generate(json)
17 | }
18 | }
19 | end
20 |
21 | context 'with content_types_only set to true' do
22 | subject { described_class.new('wl1z0pal05vy', '48d7db7d4cd9d09df573c251d456f4acc72141b92f36e57f8684b36cf5cfff6e', 'master', true, false, []) }
23 |
24 | it 'can generate a JSON template for a given space with only Content Types' do
25 | vcr('generate_json_content_types_only') {
26 | json_fixture('wl1z0pal05vy_content_types_only') { |json|
27 | expect(subject.generate_json).to eq JSON.pretty_generate(json)
28 | }
29 | }
30 | end
31 | end
32 | end
33 |
34 | describe 'integration' do
35 | subject { described_class.new('9utsm1g0t7f5', 'a67d4d9011f6d6c1dfe4169d838114d3d3849ab6df6fb1d322cf3ee91690fae4', 'staging', false, false, []) }
36 | it 'can generate templates from an environment' do
37 | vcr('generate_json_environments') {
38 | unparsed_json = subject.generate_json
39 | json = JSON.load(unparsed_json)
40 |
41 | json_fixture('environment_template') { |fixture|
42 | expect(json).to eq fixture
43 | }
44 | expect(json['entries']['foo'].size).to eq 2
45 | }
46 | end
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'simplecov'
2 | SimpleCov.start
3 |
4 | $LOAD_PATH.unshift File.expand_path('lib', __FILE__)
5 |
6 | require 'contentful/bootstrap'
7 | require 'vcr'
8 | require 'json'
9 |
10 | RSpec.configure do |config|
11 | config.filter_run :focus => true
12 | config.run_all_when_everything_filtered = true
13 | end
14 |
15 | VCR.configure do |config|
16 | config.cassette_library_dir = File.join('spec', 'fixtures', 'vcr_fixtures')
17 | config.hook_into :webmock
18 | end
19 |
20 | def json_path(name)
21 | File.join('spec', 'fixtures', 'json_fixtures', "#{name}.json")
22 | end
23 |
24 | def json_fixture(name)
25 | json = JSON.load(File.read(File.expand_path(json_path(name))))
26 | yield json if block_given?
27 | json
28 | end
29 |
30 | def vcr(cassette)
31 | VCR.use_cassette(cassette) do
32 | yield if block_given?
33 | end
34 | end
35 |
36 | def ini(ini_file)
37 | file = IniFile.load(ini_file)
38 | yield file if block_given?
39 | file
40 | end
41 |
42 | class ApiKeyDouble
43 | attr_reader :name, :description, :access_token
44 |
45 | def initialize(name, description)
46 | @name = name
47 | @description = description
48 | @access_token = 'random_api_key'
49 | end
50 | end
51 |
52 | class ApiKeysHandlerDouble
53 | def create(options)
54 | ApiKeyDouble.new(options[:name], options[:description])
55 | end
56 | end
57 |
58 | class SpaceDouble
59 | def id
60 | 'foobar'
61 | end
62 |
63 | def name
64 | 'foobar'
65 | end
66 |
67 | def api_keys
68 | ApiKeysHandlerDouble.new
69 | end
70 | end
71 |
72 | class ServerDouble
73 | def [](key)
74 | end
75 | end
76 |
77 | class ErrorRequestDouble
78 | def request; self; end
79 | def endpoint; self; end
80 | def error_message; self; end
81 | def raw; self; end
82 | def body; self; end
83 | def status; self; end
84 | end
85 |
86 | class RequestDouble
87 | attr_accessor :query
88 | end
89 |
90 | class ResponseDouble
91 | attr_accessor :body, :status, :content_type
92 | end
93 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/commands/generate_json.rb:
--------------------------------------------------------------------------------
1 | require 'contentful/bootstrap/generator'
2 | require 'contentful/bootstrap/commands/base'
3 |
4 | module Contentful
5 | module Bootstrap
6 | module Commands
7 | class GenerateJson
8 | attr_reader :space_id, :filename, :access_token, :content_types_only, :use_preview, :content_type_ids, :environment
9 | def initialize(
10 | space_id,
11 | access_token,
12 | environment = 'master',
13 | filename = nil,
14 | content_types_only = false,
15 | quiet = false,
16 | use_preview = false,
17 | content_type_ids = []
18 | )
19 | @space_id = space_id
20 | @access_token = access_token
21 | @environment = environment
22 | @filename = filename
23 | @content_types_only = content_types_only
24 | @quiet = quiet
25 | @use_preview = use_preview
26 | @content_type_ids = content_type_ids
27 | end
28 |
29 | def run
30 | if access_token.nil?
31 | output 'Access Token not specified'
32 | output 'Exiting!'
33 | exit(1)
34 | end
35 |
36 | output "Generating JSON Template for Space: '#{space_id}'"
37 | output
38 |
39 | json = Contentful::Bootstrap::Generator.new(
40 | space_id,
41 | access_token,
42 | environment,
43 | content_types_only,
44 | use_preview,
45 | content_type_ids
46 | ).generate_json
47 |
48 | write(json)
49 | end
50 |
51 | def write(json)
52 | if filename.nil?
53 | output "#{json}\n"
54 | else
55 | output "Saving JSON template to '#{filename}'"
56 | ::File.write(filename, json)
57 | end
58 | end
59 |
60 | protected
61 |
62 | def output(text = nil)
63 | Support.output(text, @quiet)
64 | end
65 | end
66 | end
67 | end
68 | end
69 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/commands/update_space.rb:
--------------------------------------------------------------------------------
1 | require 'contentful/management'
2 | require 'contentful/management/error'
3 | require 'contentful/bootstrap/templates'
4 | require 'contentful/bootstrap/commands/base'
5 |
6 | module Contentful
7 | module Bootstrap
8 | module Commands
9 | class UpdateSpace < Base
10 | attr_reader :json_template
11 | def initialize(token, space_id, options = {})
12 | @json_template = options.fetch(:json_template, nil)
13 | @mark_processed = options.fetch(:mark_processed, false)
14 | @skip_content_types = options.fetch(:skip_content_types, false)
15 | @no_publish = options.fetch(:no_publish, false)
16 | @quiet = options.fetch(:quiet, false)
17 | @environment = options.fetch(:environment, 'master')
18 |
19 | super(token, space_id, options)
20 | end
21 |
22 | def run
23 | if @json_template.nil?
24 | output 'JSON Template not found. Exiting!'
25 | exit(1)
26 | end
27 |
28 | output "Updating Space '#{@space}'"
29 |
30 | update_space = fetch_space
31 |
32 | update_json_template(update_space)
33 |
34 | output
35 | output "Successfully updated Space #{@space}"
36 |
37 | update_space
38 | end
39 |
40 | protected
41 |
42 | def fetch_space
43 | client.spaces.find(@space)
44 | rescue Contentful::Management::NotFound
45 | output 'Space Not Found. Exiting!'
46 | exit(1)
47 | end
48 |
49 | private
50 |
51 | def update_json_template(space)
52 | if ::File.exist?(@json_template)
53 | output "Updating from JSON Template '#{@json_template}'"
54 | Templates::JsonTemplate.new(space, @json_template, @environment, @mark_processed, true, @quiet, @skip_content_types, @no_publish).run
55 | output "JSON Template '#{@json_template}' updated!"
56 | else
57 | output "JSON Template '#{@json_template}' does not exist. Please check that you specified the correct file name."
58 | exit(1)
59 | end
60 | end
61 | end
62 | end
63 | end
64 | end
65 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/token.rb:
--------------------------------------------------------------------------------
1 | require 'inifile'
2 |
3 | module Contentful
4 | module Bootstrap
5 | class Token
6 | CONFIG_ENV = 'CONTENTFUL_ENV'.freeze
7 | DEFAULT_SECTION = 'global'.freeze
8 | DEFAULT_PATH = '.contentfulrc'.freeze
9 | MANAGEMENT_TOKEN = 'CONTENTFUL_MANAGEMENT_ACCESS_TOKEN'.freeze
10 | DELIVERY_TOKEN = 'CONTENTFUL_DELIVERY_ACCESS_TOKEN'.freeze
11 | ORGANIZATION_ID = 'CONTENTFUL_ORGANIZATION_ID'.freeze
12 | SPACE_ID = 'SPACE_ID'.freeze
13 |
14 | attr_reader :config_path
15 |
16 | def initialize(config_path = '')
17 | @config_path = config_path
18 | end
19 |
20 | def ==(other)
21 | return false unless other.is_a?(Contentful::Bootstrap::Token)
22 | other.config_path == @config_path
23 | end
24 |
25 | def present?
26 | return false unless ::File.exist? filename
27 | config_file[config_section].key? MANAGEMENT_TOKEN
28 | end
29 |
30 | def read
31 | config_file[config_section].fetch(MANAGEMENT_TOKEN)
32 | rescue KeyError
33 | raise 'Token not found'
34 | end
35 |
36 | def read_organization_id
37 | config_file[config_section].fetch(ORGANIZATION_ID, nil)
38 | end
39 |
40 | def write(value, section = nil, key = MANAGEMENT_TOKEN)
41 | file = config_file
42 | file[section || config_section][key] = value
43 | file.save
44 | end
45 |
46 | def write_organization_id(organization_id)
47 | write(organization_id, nil, ORGANIZATION_ID)
48 | end
49 |
50 | def write_access_token(space_name, token)
51 | write(token, space_name, DELIVERY_TOKEN)
52 | end
53 |
54 | def write_space_id(space_name, space_id)
55 | write(space_id, space_name, SPACE_ID)
56 | end
57 |
58 | def filename
59 | return config_path unless config_path.empty?
60 | ::File.join(ENV['HOME'], DEFAULT_PATH)
61 | end
62 |
63 | def config_section
64 | return ENV[CONFIG_ENV] if config_file.has_section? ENV[CONFIG_ENV]
65 | DEFAULT_SECTION
66 | end
67 |
68 | def config_file
69 | @config_file ||= ::File.exist?(filename) ? IniFile.load(filename) : IniFile.new(filename: filename)
70 | end
71 | end
72 | end
73 | end
74 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/templates/blog.rb:
--------------------------------------------------------------------------------
1 | require 'contentful/bootstrap/templates/base'
2 | require 'contentful/bootstrap/templates/links'
3 |
4 | module Contentful
5 | module Bootstrap
6 | module Templates
7 | class Blog < Base
8 | def content_types
9 | [
10 | {
11 | 'id' => 'author',
12 | 'name' => 'Author',
13 | 'displayField' => 'name',
14 | 'fields' => [
15 | {
16 | 'id' => 'name',
17 | 'name' => 'Author Name',
18 | 'type' => 'Symbol'
19 | }
20 | ]
21 | },
22 | {
23 | 'id' => 'post',
24 | 'name' => 'Post',
25 | 'displayField' => 'title',
26 | 'fields' => [
27 | {
28 | 'id' => 'title',
29 | 'name' => 'Post Title',
30 | 'type' => 'Symbol'
31 | },
32 | {
33 | 'id' => 'content',
34 | 'name' => 'Content',
35 | 'type' => 'Text'
36 | },
37 | {
38 | 'id' => 'author',
39 | 'name' => 'Author',
40 | 'type' => 'Link',
41 | 'linkType' => 'Entry'
42 | }
43 | ]
44 | }
45 | ]
46 | end
47 |
48 | def entries
49 | {
50 | 'author' => [
51 | {
52 | 'id' => 'dan_brown',
53 | 'name' => 'Dan Brown'
54 | },
55 | {
56 | 'id' => 'pablo_neruda',
57 | 'name' => 'Pablo Neruda'
58 | }
59 | ],
60 | 'post' => [
61 | {
62 | 'id' => 'inferno',
63 | 'title' => 'Inferno',
64 | 'content' => 'Inferno is the last book in Dan Brown\'s collection...',
65 | 'author' => Links::Entry.new('dan_brown')
66 | },
67 | {
68 | 'id' => 'alturas',
69 | 'title' => 'Alturas de Macchu Picchu',
70 | 'content' => 'Alturas de Macchu Picchu is one of Pablo Neruda\'s most famous poetry books...',
71 | 'author' => Links::Entry.new('pablo_neruda')
72 | }
73 | ]
74 | }
75 | end
76 | end
77 | end
78 | end
79 | end
80 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/issue_22.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "contentTypes": [
4 | {
5 | "id": "1VX60jHERm6yuem8I2agWG",
6 | "name": "yolo",
7 | "displayField": "title",
8 | "fields": [
9 | {
10 | "id": "title",
11 | "name": "title",
12 | "type": "Symbol"
13 | },
14 | {
15 | "id": "assets",
16 | "name": "assets",
17 | "type": "Array",
18 | "items": {
19 | "type": "Link",
20 | "linkType": "Asset"
21 | }
22 | }
23 | ]
24 | }
25 | ],
26 | "assets": [
27 | {
28 | "id": "2cMU9UUngYMqAEmw4Cm8Iw",
29 | "title": "Screen Shot 2015-11-18 at 11.42.12",
30 | "file": {
31 | "filename": "Screen Shot 2015-11-18 at 11.42.12",
32 | "url": "https://images.contentful.com/o2n5bdfcee61/2cMU9UUngYMqAEmw4Cm8Iw/466d36d31bb59d487115b7be995438e1/Screen_Shot_2015-11-18_at_11.42.12.png"
33 | }
34 | },
35 | {
36 | "id": "4D5sv49qaAoMIkWgAW8g0I",
37 | "title": "Screen Shot 2015-11-16 at 21.37.19",
38 | "file": {
39 | "filename": "Screen Shot 2015-11-16 at 21.37.19",
40 | "url": "https://images.contentful.com/o2n5bdfcee61/4D5sv49qaAoMIkWgAW8g0I/b61adc1bc26a74ac9b65e19365747cf9/Screen_Shot_2015-11-16_at_21.37.19.png"
41 | }
42 | },
43 | {
44 | "id": "4eX9aQsDdeqweg6Si00KaW",
45 | "title": "Screen Shot 2015-11-17 at 17.57.00",
46 | "file": {
47 | "filename": "Screen Shot 2015-11-17 at 17.57.00",
48 | "url": "https://images.contentful.com/o2n5bdfcee61/4eX9aQsDdeqweg6Si00KaW/16646165346d59974fbfd4e0223ea89e/Screen_Shot_2015-11-17_at_17.57.00.png"
49 | }
50 | },
51 | {
52 | "id": "7i1Ck3BG4o4KeIEUGosEeU",
53 | "title": "Screen Shot 2015-11-17 at 18.04.02",
54 | "file": {
55 | "filename": "Screen Shot 2015-11-17 at 18.04.02",
56 | "url": "https://images.contentful.com/o2n5bdfcee61/7i1Ck3BG4o4KeIEUGosEeU/4db1a207ec69f99f0d795c48a59cbcd1/Screen_Shot_2015-11-17_at_18.04.02.png"
57 | }
58 | }
59 | ],
60 | "entries": {
61 | "1VX60jHERm6yuem8I2agWG": [
62 | {
63 | "sys": {
64 | "id": "5nQB3z8RgW8CUG6uGyCsEw"
65 | },
66 | "fields": {
67 | "title": "yolo",
68 | "assets": [
69 | {
70 | "linkType": "Asset",
71 | "id": "4eX9aQsDdeqweg6Si00KaW"
72 | },
73 | {
74 | "linkType": "Asset",
75 | "id": "4D5sv49qaAoMIkWgAW8g0I"
76 | }
77 | ]
78 | }
79 | }
80 | ]
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/commands/base.rb:
--------------------------------------------------------------------------------
1 | require 'net/http'
2 | require 'contentful/management'
3 | require 'contentful/bootstrap/server'
4 | require 'contentful/bootstrap/support'
5 |
6 | module Contentful
7 | module Bootstrap
8 | module Commands
9 | class Base
10 | attr_reader :space, :token, :options, :quiet, :no_input
11 |
12 | def initialize(token, space, options = {})
13 | trigger_oauth = options.fetch(:trigger_oauth, true)
14 |
15 | @token = token
16 | @options = options
17 | @quiet = options.fetch(:quiet, false)
18 | @no_input = options.fetch(:no_input, false)
19 |
20 | configuration if trigger_oauth
21 | client if trigger_oauth
22 | @space = space
23 | end
24 |
25 | def run
26 | raise 'must implement'
27 | end
28 |
29 | def client
30 | @client ||= ::Contentful::Management::Client.new(
31 | @token.read,
32 | default_locale: options.fetch(:locale, 'en-US'),
33 | raise_errors: true,
34 | application_name: 'bootstrap',
35 | application_version: ::Contentful::Bootstrap::VERSION
36 | )
37 | end
38 |
39 | protected
40 |
41 | def output(text = nil)
42 | Support.output(text, @quiet)
43 | end
44 |
45 | private
46 |
47 | def configuration
48 | if @token.present?
49 | output 'OAuth token found, moving on!'
50 | return
51 | end
52 |
53 | Support.input('OAuth Token not found, do you want to create a new configuration file? (Y/n): ', no_input) do |answer|
54 | if answer.casecmp('n').zero?
55 | output 'Exiting!'
56 | exit
57 | end
58 | end
59 |
60 | raise 'OAuth token required to proceed' if no_input
61 |
62 | output "Configuration will be saved on #{@token.filename}"
63 | output 'A new tab on your browser will open for requesting OAuth permissions'
64 | token_server
65 | output
66 | end
67 |
68 | def token_server
69 | Support.silence_stderr do # Don't show any WEBrick related stuff
70 | server = Contentful::Bootstrap::Server.new(@token)
71 |
72 | server.start
73 |
74 | sleep(1) until server.running? # Wait for Server Init
75 |
76 | Net::HTTP.get(URI('http://localhost:5123'))
77 |
78 | sleep(1) until @token.present? # Wait for User to do OAuth cycle
79 |
80 | server.stop
81 | end
82 | end
83 | end
84 | end
85 | end
86 | end
87 |
--------------------------------------------------------------------------------
/.rubocop_todo.yml:
--------------------------------------------------------------------------------
1 | # This configuration was generated by
2 | # `rubocop --auto-gen-config`
3 | # on 2018-04-17 14:00:32 +0200 using RuboCop version 0.55.0.
4 | # The point is for the user to remove these configuration records
5 | # one by one as the offenses are removed from the code base.
6 | # Note that changes in the inspected code, or installation of new
7 | # versions of RuboCop, may require this file to be generated again.
8 |
9 | Documentation:
10 | Enabled: false
11 |
12 | Metrics/AbcSize:
13 | Max: 62
14 |
15 | Metrics/BlockLength:
16 | Max: 39
17 | CountComments: false
18 |
19 | Metrics/ClassLength:
20 | Max: 176
21 | CountComments: false
22 |
23 | Metrics/CyclomaticComplexity:
24 | Max: 7
25 |
26 | Metrics/LineLength:
27 | Max: 167
28 | Exclude:
29 | - 'lib/contentful/bootstrap/templates/blog.rb'
30 | - 'lib/contentful/bootstrap/templates/catalogue.rb'
31 | - 'lib/contentful/bootstrap/templates/gallery.rb'
32 | - 'bin/contentful_bootstrap'
33 |
34 | Metrics/MethodLength:
35 | Max: 88
36 | CountComments: false
37 |
38 | Metrics/ParameterLists:
39 | Max: 8
40 |
41 | Metrics/PerceivedComplexity:
42 | Max: 8
43 |
44 | Naming/MethodName:
45 | EnforcedStyle: snake_case
46 | Exclude:
47 | - 'lib/contentful/bootstrap/server.rb'
48 |
49 | # Offense count: 6
50 | Style/Documentation:
51 | Exclude:
52 | - 'spec/**/*'
53 | - 'test/**/*'
54 | - 'lib/contentful/bootstrap/commands.rb'
55 | - 'lib/contentful/bootstrap/constants.rb'
56 | - 'lib/contentful/bootstrap/generator.rb'
57 | - 'lib/contentful/bootstrap/server.rb'
58 | - 'lib/contentful/bootstrap/support.rb'
59 | - 'lib/contentful/bootstrap/templates/base.rb'
60 | - 'lib/contentful/bootstrap/templates/blog.rb'
61 | - 'lib/contentful/bootstrap/templates/catalogue.rb'
62 | - 'lib/contentful/bootstrap/templates/gallery.rb'
63 | - 'lib/contentful/bootstrap/templates/json_template.rb'
64 | - 'lib/contentful/bootstrap/templates/links/asset.rb'
65 | - 'lib/contentful/bootstrap/templates/links/base.rb'
66 | - 'lib/contentful/bootstrap/templates/links/entry.rb'
67 | - 'lib/contentful/bootstrap/token.rb'
68 | - 'lib/contentful/bootstrap/version.rb'
69 | - 'lib/contentful/bootstrap/command_runner.rb'
70 | - 'lib/contentful/bootstrap/commands/base.rb'
71 | - 'lib/contentful/bootstrap/commands/create_space.rb'
72 | - 'lib/contentful/bootstrap/commands/generate_json.rb'
73 | - 'lib/contentful/bootstrap/commands/generate_token.rb'
74 | - 'lib/contentful/bootstrap/commands/update_space.rb'
75 |
76 | Style/FormatString:
77 | Exclude:
78 | - 'lib/contentful/bootstrap/commands/create_space.rb'
79 |
80 | Style/FormatStringToken:
81 | EnforcedStyle: unannotated
82 |
--------------------------------------------------------------------------------
/spec/fixtures/vcr_fixtures/check_staging_environment_status.yml:
--------------------------------------------------------------------------------
1 | ---
2 | http_interactions:
3 | - request:
4 | method: get
5 | uri: https://cdn.contentful.com/spaces/9utsm1g0t7f5/environments/staging/entries
6 | body:
7 | encoding: US-ASCII
8 | string: ''
9 | headers:
10 | X-Contentful-User-Agent:
11 | - sdk contentful.rb/2.6.0; platform ruby/2.5.1; os macOS/16;
12 | Authorization:
13 | - Bearer a67d4d9011f6d6c1dfe4169d838114d3d3849ab6df6fb1d322cf3ee91690fae4
14 | Content-Type:
15 | - application/vnd.contentful.delivery.v1+json
16 | Accept-Encoding:
17 | - gzip
18 | Connection:
19 | - close
20 | Host:
21 | - cdn.contentful.com
22 | User-Agent:
23 | - http.rb/2.2.2
24 | response:
25 | status:
26 | code: 200
27 | message: OK
28 | headers:
29 | Access-Control-Allow-Headers:
30 | - Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation,X-Contentful-User-Agent,X-Contentful-Enable-Alpha-Feature
31 | Access-Control-Allow-Methods:
32 | - GET,HEAD,OPTIONS
33 | Access-Control-Allow-Origin:
34 | - "*"
35 | Access-Control-Expose-Headers:
36 | - Etag
37 | Access-Control-Max-Age:
38 | - '86400'
39 | Cache-Control:
40 | - max-age=0
41 | Content-Encoding:
42 | - gzip
43 | Content-Type:
44 | - application/vnd.contentful.delivery.v1+json
45 | Etag:
46 | - W/"10a24d49d1e7c0a015867c7b311521f5"
47 | Server:
48 | - Contentful
49 | X-Content-Type-Options:
50 | - nosniff
51 | X-Contentful-Request-Id:
52 | - '089705eade747eb544acfdd4be14dd87'
53 | Content-Length:
54 | - '408'
55 | Accept-Ranges:
56 | - bytes
57 | Date:
58 | - Wed, 18 Apr 2018 12:13:17 GMT
59 | Via:
60 | - 1.1 varnish
61 | Age:
62 | - '0'
63 | Connection:
64 | - close
65 | X-Served-By:
66 | - cache-hhn1535-HHN
67 | X-Cache:
68 | - MISS
69 | X-Cache-Hits:
70 | - '0'
71 | X-Timer:
72 | - S1524053597.392641,VS0,VE255
73 | Vary:
74 | - Accept-Encoding
75 | body:
76 | encoding: ASCII-8BIT
77 | string: !binary |-
78 | H4sIAAAAAAAAA91UTWvDMAy991cEn9diZ0k/ciul7NBB2dLtsDFGSNximthZ7BZC6X+fP5LUDYM0x84nS0h6z0+STwPHAbzkIHBO8ioNUeZYWmBeFFEJpO/8oGIEE1Eq/a62+J7k0oDaSElGhLQQNDYROFMFP3VBU7aFopF4HsUKqo4wTouLcmhnzemZ0D1QmJcj0el+U3EOdcVWAEnUc2YHwTO0g2Ky9dWr6nNu7vqd5gCTs2Xs+5AnkbBrNmSWVBSlBQbiAsvQZK60AC5E0yH0hmi6QSjwxoGPRu7Y/7ATTO0eCZgeScFohqnC6NbNPIOLaEforq3LraIuLdBO4Qp8JJwwqsbB0jNmVEjSVZ+6id/KbWHV/bPtsoXd3U5ZHKV66DEdvoV1QjMQYEtwmlx2RA0JoFGmUzaYC6fq5NU0GGoqJGQZdioJmuJaygrijndkXL4nxeHVe+KrtUtW65+X0F2EthB1L/vsizsL/McRnLh99gWOoAevEu5qX/TPWv0//3xf+u+JlOZrcB78AiLrrpavBgAA
79 | http_version:
80 | recorded_at: Wed, 18 Apr 2018 12:13:17 GMT
81 | recorded_with: VCR 2.9.3
82 |
--------------------------------------------------------------------------------
/spec/contentful/bootstrap/commands/generate_token_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | class ResponseDouble
4 | def object
5 | {'accessToken' => 'foo'}
6 | end
7 | end
8 |
9 | class ErrorDouble < Contentful::Management::Error
10 | def initialize
11 | end
12 |
13 | def object
14 | self.class.new
15 | end
16 | end
17 |
18 | describe Contentful::Bootstrap::Commands::GenerateToken do
19 | let(:path) { File.expand_path(File.join('spec', 'fixtures', 'ini_fixtures', 'contentfulrc.ini')) }
20 | let(:token) { Contentful::Bootstrap::Token.new path }
21 | subject { described_class.new token, 'foo', token_name: 'bar', trigger_oauth: false, quiet: true }
22 | let(:space_double) { SpaceDouble.new }
23 |
24 | describe 'instance methods' do
25 | before do
26 | subject.send(:client)
27 | end
28 |
29 | describe '#run' do
30 | it 'fetches space from api if space is a string' do
31 | allow(Contentful::Bootstrap::Support).to receive(:gets) { 'n' }
32 | expect_any_instance_of(Contentful::Management::ClientSpaceMethodsFactory).to receive(:find).with('foo') { space_double }
33 |
34 | subject.run
35 | end
36 |
37 | it 'uses space if space is not a string' do
38 | allow(Contentful::Bootstrap::Support).to receive(:gets) { 'n' }
39 | expect_any_instance_of(Contentful::Management::ClientSpaceMethodsFactory).not_to receive(:find).with('foo') { space_double }
40 | subject.instance_variable_set(:@space, space_double)
41 |
42 | subject.run
43 | end
44 |
45 | it 'returns access token' do
46 | allow(Contentful::Bootstrap::Support).to receive(:gets) { 'n' }
47 | allow_any_instance_of(Contentful::Management::ClientSpaceMethodsFactory).to receive(:find).with('foo') { space_double }
48 | expect(space_double).to receive(:api_keys).and_call_original
49 |
50 | expect(subject.run).to eq 'random_api_key'
51 | end
52 |
53 | it 'fails if API returns an error' do
54 | allow(Contentful::Bootstrap::Support).to receive(:gets) { 'n' }
55 | allow_any_instance_of(Contentful::Management::ClientSpaceMethodsFactory).to receive(:find).with('foo') { space_double }
56 | expect(space_double).to receive(:api_keys).and_raise(ErrorDouble.new)
57 |
58 | expect { subject.run }.to raise_error ErrorDouble
59 | end
60 |
61 | it 'token gets written if user input is other than no' do
62 | allow(Contentful::Bootstrap::Support).to receive(:gets) { 'y' }
63 | allow_any_instance_of(Contentful::Management::ClientSpaceMethodsFactory).to receive(:find).with('foo') { space_double }
64 | expect(space_double).to receive(:api_keys).and_call_original
65 |
66 | expect(token).to receive(:write_access_token).with('foobar', 'random_api_key')
67 | expect(token).to receive(:write_space_id).with('foobar', 'foobar')
68 |
69 | subject.run
70 | end
71 | end
72 | end
73 |
74 | describe 'attributes' do
75 | it ':token_name' do
76 | expect(subject.token_name).to eq 'bar'
77 | end
78 | end
79 |
80 | describe 'integration' do
81 | before do
82 | allow(Contentful::Bootstrap::Support).to receive(:gets).and_return('n')
83 | end
84 |
85 | it 'generates a token for a given space' do
86 | command = described_class.new token, 'zred3m25k5em', token_name: 'foo', quiet: true
87 |
88 | vcr('generate_token') {
89 | command.run
90 | }
91 | end
92 | end
93 | end
94 |
--------------------------------------------------------------------------------
/spec/fixtures/vcr_fixtures/generate_json_content_types_only.yml:
--------------------------------------------------------------------------------
1 | ---
2 | http_interactions:
3 | - request:
4 | method: get
5 | uri: https://cdn.contentful.com/spaces/wl1z0pal05vy/environments/master/content_types
6 | body:
7 | encoding: US-ASCII
8 | string: ''
9 | headers:
10 | User-Agent:
11 | - RubyContentfulGem/0.7.0
12 | Authorization:
13 | - Bearer foobar
14 | Content-Type:
15 | - application/vnd.contentful.delivery.v1+json
16 | Accept-Encoding:
17 | - gzip
18 | Connection:
19 | - close
20 | Host:
21 | - cdn.contentful.com
22 | response:
23 | status:
24 | code: 200
25 | message: OK
26 | headers:
27 | Access-Control-Allow-Headers:
28 | - Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation
29 | Access-Control-Allow-Methods:
30 | - GET,HEAD,OPTIONS
31 | Access-Control-Allow-Origin:
32 | - "*"
33 | Access-Control-Expose-Headers:
34 | - Etag
35 | Access-Control-Max-Age:
36 | - '86400'
37 | Cache-Control:
38 | - max-age=0
39 | Content-Encoding:
40 | - gzip
41 | Content-Type:
42 | - application/vnd.contentful.delivery.v1+json
43 | Server:
44 | - nginx
45 | X-Contentful-Request-Id:
46 | - c40-1967993529
47 | Content-Length:
48 | - '986'
49 | Accept-Ranges:
50 | - bytes
51 | Date:
52 | - Thu, 19 Nov 2015 19:42:11 GMT
53 | Via:
54 | - 1.1 varnish
55 | Age:
56 | - '0'
57 | Connection:
58 | - close
59 | X-Served-By:
60 | - cache-iad2140-IAD
61 | X-Cache:
62 | - MISS
63 | X-Cache-Hits:
64 | - '0'
65 | Vary:
66 | - Accept-Encoding
67 | body:
68 | encoding: ASCII-8BIT
69 | string: !binary |-
70 | H4sIAAAAAAAAA91XS2/iMBC+91dE2euWJuFRyo3tUqnbh7oiVR+rHkxiqEVe
71 | 2E7ZtOp/XztOwE6MCGy1KzUHhGfsGdsz883ntwPDMElGzIHxxv6yAc0SyEbm
72 | EGOQmUz2/pXPoTEFAZO38xGZo4QNrHwQoBBRNrItMUYUhtzgr9ygMMumTREM
73 | /LVc1uWOIxDmjm9w7KceNfIxd1B+JvK5PhH665q63LkLf1N1IYaLFGHIl1Oc
74 | wpXN/GjiK7epbGUcpDPdFkhNXvoeZ+EkDvi1iW+bh++QeBglFMWRzlFx1o2z
75 | lCM3djpGr/DIZXE+Oo2DGOscEzaFG/fqE/Y96nkIZtqAorpCTUI1CYrskgMm
76 | pe0liubKgfIEjeb8uHlaEwLpOj4svRtfmwtmRHdXtCbff/vV/Nllf6eAwlmM
77 | EdTu0tug3X+v2656FFGcVWPxAgLkA57vKhTUq1BI8uCdxhGFES1iKHClrLBi
78 | Wu9+mbhgjNC5M5w7d0srRr0FkAMtJj5JgMIl6/Dzkaxtnhg3GHnazE7qivK6
79 | r9NwAnFzoPiGQeTrsm9SV5Q+agFSKkGEp3H2/0xBRBFVA1pA8kKrK7dxzoI3
80 | 2+Ws44tb3UnJPFXFpYNq0WwD3eELQAGYBNAAlUYhOswSTghrYg2dFTf4VKJU
81 | tZGtzCh9lmebSRLAEqdsvUUmy91YiHaAt3FuUQZMtrg4VWC/WgkIrO5L3tfL
82 | T5vnKxogl97abGHSuVlM729/LEf9xfjhIr2ybnvL/pXk3sTwBRHe3Bg9sKX1
83 | HoYMj/wh5w2mY9mdQ9s5tC3X7gzs40HXah332o+yoTRhqCEt6B5a9mGbLWgP
84 | 2vag3W/1T04ey2OtEsD0pf7KPLnPiBieQBODp4/hwymKIDHoMzQIIwceTTE0
85 | 4qkBjKL5Ggx5JBwzfUSSAGRnnM5U+Uh+rYX3nYnPaRwmIMo2Eh9P6P8F8bmM
86 | Z7GuBIOavBnWVLrutgLdyHdEJhcXsXFWuaecCDYGuDtd1e8DCFtIpbtElDI4
87 | 1FBbqlPti3KjkKGczgusK/b1cfMcR9D4ovOScJWqKL2Ip8UurK6K8Aot2gjA
88 | as/8fPBLzl7dx8k4vep78WiZzeDtg8L9ZPTtNQNfpzuwei3HaQy+tj1w+i3r
89 | xPlg8M1pjXG0AuEQROkU5OiMtyGyDJR/hcguooGW2DEiVFUooKPk9r6vz3PW
90 | qHSVhWry/wLC4j2R7YrCG6u1eL6sCebnK9gNjxSpNCW+1GlYsb1Bu93qdvpN
91 | 6RKr2M5xq9M7/uCKLelS8dCU4lhlTKJ6RGWy36eD94M/hymdIIgSAAA=
92 | http_version:
93 | recorded_at: Thu, 19 Nov 2015 19:42:11 GMT
94 | recorded_with: VCR 2.9.3
95 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/templates/gallery.rb:
--------------------------------------------------------------------------------
1 | require 'contentful/bootstrap/templates/base'
2 | require 'contentful/bootstrap/templates/links'
3 |
4 | module Contentful
5 | module Bootstrap
6 | module Templates
7 | class Gallery < Base
8 | def content_types
9 | [
10 | {
11 | 'id' => 'author',
12 | 'name' => 'Author',
13 | 'displayField' => 'name',
14 | 'fields' => [
15 | {
16 | 'name' => 'Name',
17 | 'id' => 'name',
18 | 'type' => 'Symbol'
19 | }
20 | ]
21 | },
22 | {
23 | 'id' => 'image',
24 | 'name' => 'Image',
25 | 'displayField' => 'title',
26 | 'fields' => [
27 | {
28 | 'id' => 'title',
29 | 'name' => 'Title',
30 | 'type' => 'Symbol'
31 | },
32 | {
33 | 'id' => 'photo',
34 | 'name' => 'Photo',
35 | 'type' => 'Link',
36 | 'linkType' => 'Asset'
37 | }
38 | ]
39 | },
40 | {
41 | 'id' => 'gallery',
42 | 'name' => 'Gallery',
43 | 'displayField' => 'title',
44 | 'fields' => [
45 | {
46 | 'id' => 'title',
47 | 'name' => 'Title',
48 | 'type' => 'Symbol'
49 | },
50 | {
51 | 'id' => 'author',
52 | 'name' => 'Author',
53 | 'type' => 'Link',
54 | 'linkType' => 'Entry'
55 | },
56 | {
57 | 'id' => 'images',
58 | 'name' => 'Images',
59 | 'type' => 'Array',
60 | 'items' => {
61 | 'type' => 'Link',
62 | 'linkType' => 'Entry'
63 | }
64 | }
65 | ]
66 | }
67 | ]
68 | end
69 |
70 | def assets
71 | [
72 | {
73 | 'id' => 'pie',
74 | 'title' => 'Pie in the Sky',
75 | 'file' => create_file('pie.jpg', 'https://c2.staticflickr.com/6/5245/5335909339_d307a7cbcf_b.jpg')
76 | },
77 | {
78 | 'id' => 'flower',
79 | 'title' => 'The Flower',
80 | 'file' => create_file('flower.jpg', 'http://c2.staticflickr.com/4/3922/15045568809_b24591e318_b.jpg')
81 | }
82 | ]
83 | end
84 |
85 | def entries
86 | {
87 | 'author' => [
88 | {
89 | 'id' => 'dave',
90 | 'name' => 'Dave'
91 | }
92 | ],
93 | 'image' => [
94 | {
95 | 'id' => 'pie_entry',
96 | 'title' => 'A Pie in the Sky',
97 | 'photo' => Links::Asset.new('pie')
98 | },
99 | {
100 | 'id' => 'flower_entry',
101 | 'title' => 'The Flower',
102 | 'photo' => Links::Asset.new('flower')
103 | }
104 | ],
105 | 'gallery' => [
106 | {
107 | 'id' => 'gallery',
108 | 'title' => 'Photo Gallery',
109 | 'author' => Links::Entry.new('dave'),
110 | 'images' => [Links::Entry.new('pie_entry'), Links::Entry.new('flower_entry')]
111 | }
112 | ]
113 | }
114 | end
115 | end
116 | end
117 | end
118 | end
119 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/server.rb:
--------------------------------------------------------------------------------
1 | require 'thread'
2 | require 'webrick'
3 | require 'launchy'
4 | require 'contentful/bootstrap/constants'
5 | require 'contentful/bootstrap/token'
6 |
7 | module Contentful
8 | module Bootstrap
9 | class OAuthEchoView
10 | def render
11 | <<-JS
12 |
13 |
14 |
15 |
16 |
22 |
23 | JS
24 | end
25 | end
26 |
27 | class ThanksView
28 | def render
29 | <<-HTML
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
Contentful Bootstrap
38 |
Thanks! The OAuth Token has been generated
39 |
The Space you specified will now start to create. You can close this window freely
40 |
41 |
42 |
43 | HTML
44 | end
45 | end
46 |
47 | class IndexController < WEBrick::HTTPServlet::AbstractServlet
48 | def do_GET(_request, response)
49 | client_id = Contentful::Bootstrap::Constants::OAUTH_APP_ID
50 | redirect_uri = Contentful::Bootstrap::Constants::OAUTH_CALLBACK_URL
51 | scope = 'content_management_manage'
52 | Launchy.open("https://be.contentful.com/oauth/authorize?response_type=token&client_id=#{client_id}&redirect_uri=#{redirect_uri}&scope=#{scope}")
53 | response.status = 200
54 | response.body = ''
55 | end
56 | end
57 |
58 | class OAuthCallbackController < WEBrick::HTTPServlet::AbstractServlet
59 | def do_GET(_request, response)
60 | response.status = 200
61 | response.content_type = 'text/html'
62 | response.body = OAuthEchoView.new.render
63 | end
64 | end
65 |
66 | class SaveTokenController < WEBrick::HTTPServlet::AbstractServlet
67 | attr_reader :token
68 |
69 | def initialize(server, token, *options)
70 | super(server, options)
71 | @token = token
72 | end
73 |
74 | def do_GET(request, response)
75 | @token.write(request.query['token'])
76 | response.status = 200
77 | response.content_type = 'text/html'
78 | response.body = ThanksView.new.render
79 | end
80 | end
81 |
82 | class Server
83 | attr_reader :server
84 |
85 | def initialize(token)
86 | @server = WEBrick::HTTPServer.new(Port: 5123)
87 | @server.mount '/', IndexController
88 | @server.mount '/oauth_callback', OAuthCallbackController
89 | @server.mount '/save_token', SaveTokenController, token
90 | end
91 |
92 | def start
93 | Thread.new { @server.start }
94 | end
95 |
96 | def stop
97 | @server.shutdown
98 | end
99 |
100 | def running?
101 | @server.status != :Stop
102 | end
103 | end
104 | end
105 | end
106 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/wl1z0pal05vy_content_types_only.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "contentTypes": [
4 | {
5 | "id": "2PqfXUJwE8qSYKuM0U6w8M",
6 | "name": "Product",
7 | "displayField": "productName",
8 | "fields": [
9 | {
10 | "id": "productName",
11 | "name": "Product name",
12 | "type": "Text"
13 | },
14 | {
15 | "id": "slug",
16 | "name": "Slug",
17 | "type": "Symbol"
18 | },
19 | {
20 | "id": "productDescription",
21 | "name": "Description",
22 | "type": "Text"
23 | },
24 | {
25 | "id": "sizetypecolor",
26 | "name": "Size/Type/Color",
27 | "type": "Symbol"
28 | },
29 | {
30 | "id": "image",
31 | "name": "Image",
32 | "type": "Array",
33 | "items": {
34 | "type": "Link",
35 | "linkType": "Asset"
36 | }
37 | },
38 | {
39 | "id": "tags",
40 | "name": "Tags",
41 | "type": "Array",
42 | "items": {
43 | "type": "Symbol"
44 | }
45 | },
46 | {
47 | "id": "categories",
48 | "name": "Categories",
49 | "type": "Array",
50 | "items": {
51 | "type": "Link",
52 | "linkType": "Entry"
53 | }
54 | },
55 | {
56 | "id": "price",
57 | "name": "Price",
58 | "type": "Number"
59 | },
60 | {
61 | "id": "brand",
62 | "name": "Brand",
63 | "type": "Link",
64 | "linkType": "Entry"
65 | },
66 | {
67 | "id": "quantity",
68 | "name": "Quantity",
69 | "type": "Integer"
70 | },
71 | {
72 | "id": "sku",
73 | "name": "SKU",
74 | "type": "Symbol"
75 | },
76 | {
77 | "id": "website",
78 | "name": "Available at",
79 | "type": "Symbol"
80 | }
81 | ]
82 | },
83 | {
84 | "id": "6XwpTaSiiI2Ak2Ww0oi6qa",
85 | "name": "Category",
86 | "displayField": "title",
87 | "fields": [
88 | {
89 | "id": "title",
90 | "name": "Title",
91 | "type": "Text"
92 | },
93 | {
94 | "id": "icon",
95 | "name": "Icon",
96 | "type": "Link",
97 | "linkType": "Asset"
98 | },
99 | {
100 | "id": "categoryDescription",
101 | "name": "Description",
102 | "type": "Text"
103 | }
104 | ]
105 | },
106 | {
107 | "id": "sFzTZbSuM8coEwygeUYes",
108 | "name": "Brand",
109 | "displayField": "companyName",
110 | "fields": [
111 | {
112 | "id": "companyName",
113 | "name": "Company name",
114 | "type": "Text"
115 | },
116 | {
117 | "id": "logo",
118 | "name": "Logo",
119 | "type": "Link",
120 | "linkType": "Asset"
121 | },
122 | {
123 | "id": "companyDescription",
124 | "name": "Description",
125 | "type": "Text"
126 | },
127 | {
128 | "id": "website",
129 | "name": "Website",
130 | "type": "Symbol"
131 | },
132 | {
133 | "id": "twitter",
134 | "name": "Twitter",
135 | "type": "Symbol"
136 | },
137 | {
138 | "id": "email",
139 | "name": "Email",
140 | "type": "Symbol"
141 | },
142 | {
143 | "id": "phone",
144 | "name": "Phone #",
145 | "type": "Array",
146 | "items": {
147 | "type": "Symbol"
148 | }
149 | }
150 | ]
151 | }
152 | ],
153 | "assets": [
154 | ],
155 | "entries": {
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/spec/contentful/bootstrap/commands/generate_json_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Contentful::Bootstrap::Commands::GenerateJson do
4 | subject { described_class.new('foo', 'bar') }
5 |
6 | describe 'instance methods' do
7 | describe '#run' do
8 | it 'exits if access_token is nil' do
9 | subject.instance_variable_set(:@access_token, nil)
10 | subject.instance_variable_set(:@quiet, true)
11 |
12 | expect { subject.run }.to raise_error SystemExit
13 | end
14 |
15 | it 'calls generator' do
16 | allow(subject).to receive(:write).with('json')
17 |
18 | expect_any_instance_of(Contentful::Bootstrap::Generator).to receive(:generate_json) { 'json' }
19 |
20 | subject.run
21 | end
22 |
23 | it 'writes json' do
24 | vcr('generate_json') {
25 | subject.instance_variable_set(:@space_id, 'wl1z0pal05vy')
26 | subject.instance_variable_set(:@access_token, '48d7db7d4cd9d09df573c251d456f4acc72141b92f36e57f8684b36cf5cfff6e')
27 | subject.instance_variable_set(:@quiet, true)
28 |
29 | json_fixture('wl1z0pal05vy') { |json|
30 | expect(subject).to receive(:write).with(JSON.pretty_generate(json))
31 | }
32 |
33 | subject.run
34 | }
35 | end
36 |
37 | it 'can use the preview api' do
38 | vcr('generate_json_preview') {
39 | subject.instance_variable_set(:@space_id, 'f3abi4dqvrhg')
40 | subject.instance_variable_set(:@access_token, '06c28ef41823bb636714dfd812066fa026a49e95041a0e94903d6cf016bba50e')
41 | subject.instance_variable_set(:@use_preview, true)
42 | subject.instance_variable_set(:@quiet, true)
43 |
44 | json_fixture('f3abi4dqvrhg_preview') { |json|
45 | expect(subject).to receive(:write).with(JSON.pretty_generate(json))
46 | }
47 |
48 | subject.run
49 | }
50 | end
51 |
52 | describe 'specific content types' do
53 | it 'can select a single content type' do
54 | vcr('generate_json_single_ct') {
55 | subject.instance_variable_set(:@space_id, 'cfexampleapi')
56 | subject.instance_variable_set(:@access_token, 'b4c0n73n7fu1')
57 | subject.instance_variable_set(:@content_type_ids, ['cat'])
58 | subject.instance_variable_set(:@quiet, true)
59 |
60 | json_fixture('cfexampleapi_cat') { |json|
61 | expect(subject).to receive(:write).with(JSON.pretty_generate(json))
62 | }
63 |
64 | subject.run
65 | }
66 | end
67 |
68 | it 'can select multiple content types' do
69 | vcr('generate_json_multi_ct') {
70 | subject.instance_variable_set(:@space_id, 'cfexampleapi')
71 | subject.instance_variable_set(:@access_token, 'b4c0n73n7fu1')
72 | subject.instance_variable_set(:@content_type_ids, ['cat', 'human'])
73 | subject.instance_variable_set(:@quiet, true)
74 |
75 | json_fixture('cfexampleapi_cat_human') { |json|
76 | expect(subject).to receive(:write).with(JSON.pretty_generate(json))
77 | }
78 |
79 | subject.run
80 | }
81 | end
82 | end
83 | end
84 |
85 | describe '#write' do
86 | it 'outputs json to stoud if no filename provided' do
87 | expect { subject.write('json') }.to output("json\n").to_stdout
88 | end
89 |
90 | it 'writes to file if filename provided' do
91 | subject.instance_variable_set(:@filename, 'foo')
92 |
93 | expect(::File).to receive(:write).with('foo', 'json')
94 |
95 | expect { subject.write('json') }.to output("Saving JSON template to 'foo'\n").to_stdout
96 | end
97 | end
98 | end
99 |
100 | describe 'attributes' do
101 | it ':space_id' do
102 | expect(subject.space_id).to eq 'foo'
103 | end
104 |
105 | it ':access_token' do
106 | expect(subject.access_token).to eq 'bar'
107 | end
108 |
109 | it ':filename' do
110 | expect(subject.filename).to eq nil
111 | end
112 | end
113 | end
114 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/cfexampleapi_cat.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "contentTypes": [
4 | {
5 | "id": "cat",
6 | "name": "Cat",
7 | "displayField": "name",
8 | "fields": [
9 | {
10 | "id": "name",
11 | "name": "Name",
12 | "type": "Text"
13 | },
14 | {
15 | "id": "likes",
16 | "name": "Likes",
17 | "type": "Array",
18 | "items": {
19 | "type": "Symbol"
20 | }
21 | },
22 | {
23 | "id": "color",
24 | "name": "Color",
25 | "type": "Symbol"
26 | },
27 | {
28 | "id": "bestFriend",
29 | "name": "Best Friend",
30 | "type": "Link",
31 | "linkType": "Entry"
32 | },
33 | {
34 | "id": "birthday",
35 | "name": "Birthday",
36 | "type": "Date"
37 | },
38 | {
39 | "id": "lifes",
40 | "name": "Lifes left",
41 | "type": "Integer"
42 | },
43 | {
44 | "id": "lives",
45 | "name": "Lives left",
46 | "type": "Integer"
47 | },
48 | {
49 | "id": "image",
50 | "name": "Image",
51 | "type": "Link",
52 | "linkType": "Asset"
53 | }
54 | ]
55 | }
56 | ],
57 | "assets": [
58 | {
59 | "id": "1x0xpXu4pSGS4OukSyWGUK",
60 | "title": "Doge",
61 | "file": {
62 | "filename": "doge",
63 | "url": "https://images.contentful.com/cfexampleapi/1x0xpXu4pSGS4OukSyWGUK/cc1239c6385428ef26f4180190532818/doge.jpg"
64 | }
65 | },
66 | {
67 | "id": "happycat",
68 | "title": "Happy Cat",
69 | "file": {
70 | "filename": "happycatw",
71 | "url": "https://images.contentful.com/cfexampleapi/3MZPnjZTIskAIIkuuosCss/382a48dfa2cb16c47aa2c72f7b23bf09/happycatw.jpg"
72 | }
73 | },
74 | {
75 | "id": "jake",
76 | "title": "Jake",
77 | "file": {
78 | "filename": "jake",
79 | "url": "https://images.contentful.com/cfexampleapi/4hlteQAXS8iS0YCMU6QMWg/2a4d826144f014109364ccf5c891d2dd/jake.png"
80 | }
81 | },
82 | {
83 | "id": "nyancat",
84 | "title": "Nyan Cat",
85 | "file": {
86 | "filename": "Nyan_cat_250px_frame",
87 | "url": "https://images.contentful.com/cfexampleapi/4gp6taAwW4CmSgumq2ekUm/9da0cd1936871b8d72343e895a00d611/Nyan_cat_250px_frame.png"
88 | }
89 | }
90 | ],
91 | "entries": {
92 | "cat": [
93 | {
94 | "sys": {
95 | "id": "nyancat"
96 | },
97 | "fields": {
98 | "name": "Nyan Cat",
99 | "likes": [
100 | "rainbows",
101 | "fish"
102 | ],
103 | "color": "rainbow",
104 | "bestFriend": {
105 | "linkType": "Entry",
106 | "id": "happycat"
107 | },
108 | "birthday": "2011-04-04T22:00:00+00:00",
109 | "lives": 1337,
110 | "image": {
111 | "linkType": "Asset",
112 | "id": "nyancat"
113 | }
114 | }
115 | },
116 | {
117 | "sys": {
118 | "id": "happycat"
119 | },
120 | "fields": {
121 | "name": "Happy Cat",
122 | "likes": [
123 | "cheezburger"
124 | ],
125 | "color": "gray",
126 | "bestFriend": {
127 | "linkType": "Entry",
128 | "id": "nyancat"
129 | },
130 | "birthday": "2003-10-28T23:00:00+00:00",
131 | "lives": 1,
132 | "image": {
133 | "linkType": "Asset",
134 | "id": "happycat"
135 | }
136 | }
137 | },
138 | {
139 | "sys": {
140 | "id": "garfield"
141 | },
142 | "fields": {
143 | "name": "Garfield",
144 | "likes": [
145 | "lasagna"
146 | ],
147 | "color": "orange",
148 | "birthday": "1979-06-18T23:00:00+00:00",
149 | "lives": 9
150 | }
151 | }
152 | ]
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/spec/contentful/bootstrap/commands/base_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'net/http'
3 | require 'contentful/bootstrap/commands/base'
4 |
5 | describe Contentful::Bootstrap::Commands::Base do
6 | let(:path) { File.expand_path(File.join('spec', 'fixtures', 'ini_fixtures', 'contentfulrc.ini')) }
7 | let(:no_token_path) { File.expand_path(File.join('spec', 'fixtures', 'ini_fixtures', 'no_token.ini')) }
8 | let(:token) { Contentful::Bootstrap::Token.new path }
9 | let(:non_management_token) { Contentful::Bootstrap::Token.new no_token_path }
10 | subject { described_class.new(token, 'foo', trigger_oauth: false, quiet: true) }
11 |
12 | describe 'abstract methods' do
13 | it '#run' do
14 | expect { subject.run }.to raise_error 'must implement'
15 | end
16 | end
17 |
18 | describe 'initialize' do
19 | describe 'configuration' do
20 | it 'runs configuration when trigger_oauth is true' do
21 | expect_any_instance_of(described_class).to receive(:configuration)
22 | described_class.new(token, 'foo', quiet: true)
23 | end
24 |
25 | it 'doesnt run configuration when trigger_oauth is false' do
26 | expect_any_instance_of(described_class).not_to receive(:configuration)
27 | described_class.new(token, 'foo', trigger_oauth: false, quiet: true)
28 | end
29 | end
30 | end
31 |
32 | describe 'instance methods' do
33 | it '#client' do
34 | allow_any_instance_of(described_class).to receive(:configuration)
35 | expect(Contentful::Management::Client).to receive(:new).with(
36 | token.read,
37 | default_locale: 'en-US',
38 | raise_errors: true,
39 | application_name: 'bootstrap',
40 | application_version: Contentful::Bootstrap::VERSION
41 | )
42 |
43 | described_class.new(token, 'foo', quiet: true)
44 | end
45 |
46 | describe '#configuration' do
47 | before do
48 | allow_any_instance_of(subject.class).to receive(:client)
49 | end
50 |
51 | it 'passes if token is found' do
52 | expect { described_class.new(token, 'foo') }.to output("OAuth token found, moving on!\n").to_stdout
53 | end
54 |
55 | describe 'token not found' do
56 | it 'exits if answer is no' do
57 | expect(Contentful::Bootstrap::Support).to receive(:gets) { "n" }
58 | expect { described_class.new(non_management_token, 'foo', quiet: true) }.to raise_error SystemExit
59 | end
60 |
61 | it 'runs token_server if other answer' do
62 | expect(Contentful::Bootstrap::Support).to receive(:gets) { "y" }
63 | expect_any_instance_of(described_class).to receive(:token_server)
64 |
65 | described_class.new(non_management_token, 'foo', quiet: true)
66 | end
67 | end
68 | end
69 |
70 | it '#token_server' do
71 | allow_any_instance_of(described_class).to receive(:client)
72 |
73 | expect(Contentful::Bootstrap::Support).to receive(:gets) { "y" }
74 | expect_any_instance_of(Contentful::Bootstrap::Server).to receive(:start) {}
75 | expect_any_instance_of(Contentful::Bootstrap::Server).to receive(:running?) { true }
76 | expect(Net::HTTP).to receive(:get).with(URI('http://localhost:5123')) {}
77 | expect(non_management_token).to receive(:present?).and_return(false, true)
78 | expect_any_instance_of(Contentful::Bootstrap::Server).to receive(:stop) {}
79 |
80 | described_class.new(non_management_token, 'foo', quiet: true)
81 | end
82 | end
83 |
84 | describe 'attributes' do
85 | it ':space' do
86 | expect(subject.space).to eq 'foo'
87 | end
88 |
89 | it ':token' do
90 | expect(subject.token == Contentful::Bootstrap::Token.new(path)).to be_truthy
91 | end
92 | end
93 |
94 | describe 'additional options' do
95 | describe ':no_input' do
96 | it ':no_input will disallow all input operations' do
97 | expect_any_instance_of(described_class).not_to receive(:token_server)
98 | expect(Contentful::Bootstrap::Support).not_to receive(:gets)
99 | expect { described_class.new(non_management_token, 'foo', quiet: true, no_input: true) }.to raise_error "OAuth token required to proceed"
100 | end
101 |
102 | it 'without :no_input input operations are allowed' do
103 | expect_any_instance_of(described_class).to receive(:token_server)
104 | expect(Contentful::Bootstrap::Support).to receive(:gets) { "y" }
105 | allow(non_management_token).to receive(:read) { true }
106 | described_class.new(non_management_token, 'foo', quiet: true, no_input: false)
107 | end
108 | end
109 | end
110 | end
111 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/commands/create_space.rb:
--------------------------------------------------------------------------------
1 | require 'contentful/management'
2 | require 'contentful/management/error'
3 | require 'contentful/bootstrap/templates'
4 | require 'contentful/bootstrap/commands/base'
5 | require 'contentful/bootstrap/commands/generate_token'
6 |
7 | module Contentful
8 | module Bootstrap
9 | module Commands
10 | class CreateSpace < Base
11 | attr_reader :template_name, :json_template
12 | def initialize(token, space, options = {})
13 | @template_name = options.fetch(:template, nil)
14 | @json_template = options.fetch(:json_template, nil)
15 | @mark_processed = options.fetch(:mark_processed, false)
16 | @locale = options.fetch(:locale, 'en-US')
17 | @no_publish = options.fetch(:no_publish, false)
18 | @environment = 'master' # Can only add content to a new space through the master environment by default
19 |
20 | super(token, space, options)
21 | end
22 |
23 | def run
24 | output "Creating Space '#{@space}'"
25 |
26 | new_space = fetch_space
27 |
28 | output "Space '#{@space}' created!"
29 | output
30 |
31 | create_template(new_space) unless @template_name.nil?
32 | create_json_template(new_space) unless @json_template.nil?
33 |
34 | access_token = generate_token(new_space)
35 | output
36 | output "Space ID: '#{new_space.id}'"
37 | output "Access Token: '#{access_token}'"
38 | output
39 | output 'You can now insert those values into your configuration blocks'
40 | output "Manage your content at https://app.contentful.com/spaces/#{new_space.id}"
41 |
42 | new_space
43 | end
44 |
45 | protected
46 |
47 | def fetch_space
48 | new_space = nil
49 | begin
50 | options = {
51 | name: @space,
52 | defaultLocale: @locale
53 | }
54 | options[:organization_id] = @token.read_organization_id unless @token.read_organization_id.nil?
55 | new_space = client.spaces.create(options)
56 | rescue Contentful::Management::NotFound
57 | raise 'Organization ID is required, provide it in Configuration File' if no_input
58 |
59 | output 'Your account has multiple organizations:'
60 | output organizations.join("\n")
61 | Support.input('Please insert the Organization ID you\'d want to create the spaces for: ', no_input) do |answer|
62 | organization_id = answer
63 | @token.write_organization_id(organization_id)
64 | output 'Your Organization ID has been stored as the default organization.'
65 | new_space = client.spaces.create(
66 | name: @space,
67 | defaultLocale: @locale,
68 | organization_id: organization_id
69 | )
70 | end
71 | end
72 |
73 | new_space
74 | end
75 |
76 | private
77 |
78 | def organizations
79 | organizations = client.organizations.all
80 | organization_ids = organizations.map do |organization|
81 | sprintf('%-30s %s', organization.name, organization.id)
82 | end
83 | organization_ids.sort
84 | end
85 |
86 | def templates
87 | {
88 | blog: Templates::Blog,
89 | gallery: Templates::Gallery,
90 | catalogue: Templates::Catalogue
91 | }
92 | end
93 |
94 | def create_template(space)
95 | if templates.key? @template_name.to_sym
96 | output "Creating Template '#{@template_name}'"
97 |
98 | templates[@template_name.to_sym].new(space, @environment, quiet).run
99 | output "Template '#{@template_name}' created!"
100 | else
101 | output "Template '#{@template_name}' not found. Valid templates are '#{templates.keys.map(&:to_s).join('\', \'')}'"
102 | end
103 | output
104 | end
105 |
106 | def create_json_template(space)
107 | if ::File.exist?(@json_template)
108 | output "Creating JSON Template '#{@json_template}'"
109 | Templates::JsonTemplate.new(space, @json_template, @environment, @mark_processed, true, quiet, false, @no_publish).run
110 | output "JSON Template '#{@json_template}' created!"
111 | else
112 | output "JSON Template '#{@json_template}' does not exist. Please check that you specified the correct file name."
113 | end
114 | output
115 | end
116 |
117 | def generate_token(space)
118 | Contentful::Bootstrap::Commands::GenerateToken.new(
119 | @token, space, options
120 | ).run
121 | end
122 | end
123 | end
124 | end
125 | end
126 |
--------------------------------------------------------------------------------
/spec/contentful/bootstrap/server_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Contentful::Bootstrap::OAuthEchoView do
4 | describe 'instance methods' do
5 | it '#render' do
6 | expect(subject.render).to include(
7 | 'html',
8 | 'script',
9 | 'access_token',
10 | 'window.location.replace',
11 | 'save_token'
12 | )
13 | end
14 | end
15 | end
16 |
17 | describe Contentful::Bootstrap::ThanksView do
18 | describe 'instance methods' do
19 | it '#render' do
20 | expect(subject.render).to include(
21 | 'html',
22 | 'Contentful Bootstrap',
23 | 'Thanks!'
24 | )
25 | end
26 | end
27 | end
28 |
29 | describe Contentful::Bootstrap::IndexController do
30 | subject { Contentful::Bootstrap::IndexController.new(ServerDouble.new) }
31 |
32 | describe 'instance methods' do
33 | describe '#do_GET' do
34 | it 'launches browser' do
35 | expect(Launchy).to receive(:open)
36 | subject.do_GET(RequestDouble.new, ResponseDouble.new)
37 | end
38 | end
39 | end
40 | end
41 |
42 | describe Contentful::Bootstrap::OAuthCallbackController do
43 | subject { Contentful::Bootstrap::OAuthCallbackController.new(ServerDouble.new) }
44 | let(:response_double) { ResponseDouble.new }
45 |
46 | describe 'instance methods' do
47 | describe '#do_GET' do
48 | it 'renders OAuthEchoView' do
49 | subject.do_GET(RequestDouble.new, response_double)
50 | expect(response_double.body).to eq Contentful::Bootstrap::OAuthEchoView.new.render
51 | end
52 | end
53 | end
54 | end
55 |
56 | describe Contentful::Bootstrap::SaveTokenController do
57 | let(:path) { File.expand_path(File.join('spec', 'fixtures', 'ini_fixtures', 'contentfulrc.ini')) }
58 | let(:token) { Contentful::Bootstrap::Token.new path }
59 | subject { Contentful::Bootstrap::SaveTokenController.new(ServerDouble.new, token) }
60 | let(:response_double) { ResponseDouble.new }
61 | let(:request_double) { RequestDouble.new }
62 |
63 | describe 'instance methods' do
64 | describe '#do_GET' do
65 | it 'writes token' do
66 | request_double.query = {'token' => 'foo'}
67 |
68 | expect(token).to receive(:write).with('foo')
69 |
70 | subject.do_GET(request_double, response_double)
71 | end
72 |
73 | it 'renders ThanksView' do
74 | request_double.query = {'token' => 'foo'}
75 | allow(token).to receive(:write)
76 |
77 | subject.do_GET(request_double, response_double)
78 |
79 | expect(response_double.body).to eq Contentful::Bootstrap::ThanksView.new.render
80 | end
81 | end
82 | end
83 |
84 | describe 'attributes' do
85 | it ':token' do
86 | expect(subject.token).to eq token
87 | end
88 | end
89 | end
90 |
91 | describe Contentful::Bootstrap::Server do
92 | let(:path) { File.expand_path(File.join('spec', 'fixtures', 'ini_fixtures', 'contentfulrc.ini')) }
93 | let(:token) { Contentful::Bootstrap::Token.new path }
94 | subject { Contentful::Bootstrap::Server.new(token) }
95 |
96 | before do
97 | allow_any_instance_of(WEBrick::HTTPServer).to receive(:start)
98 | allow(WEBrick::Utils).to receive(:create_listeners) { [] } # Mock TCP Port binding
99 | end
100 |
101 | describe 'instance methods' do
102 | describe '#start' do
103 | it 'runs server in a new thread' do
104 | expect(Thread).to receive(:new)
105 | subject.start
106 | end
107 | end
108 |
109 | describe '#stop' do
110 | it 'shutdowns webrick' do
111 | expect(subject.server).to receive(:shutdown)
112 | subject.stop
113 | end
114 | end
115 |
116 | describe '#running?' do
117 | it 'returns true if webrick is running' do
118 | expect(subject.server).to receive(:status) { :Running }
119 | expect(subject.running?).to be_truthy
120 | end
121 |
122 | it 'returns false if webrick is not running' do
123 | expect(subject.server).to receive(:status) { :Stop }
124 | expect(subject.running?).to be_falsey
125 | end
126 | end
127 | end
128 |
129 | describe 'attributes' do
130 | describe ':server' do
131 | it 'creates the webrick server' do
132 | expect(subject.server).to be_kind_of(WEBrick::HTTPServer)
133 | end
134 |
135 | describe 'mounted endpoints' do
136 | before do
137 | @mount_table = subject.server.instance_variable_get(:@mount_tab)
138 | end
139 |
140 | it '/' do
141 | expect(@mount_table['/'][0]).to eq(Contentful::Bootstrap::IndexController)
142 | end
143 |
144 | it '/oauth_callback' do
145 | expect(@mount_table['/oauth_callback'][0]).to eq(Contentful::Bootstrap::OAuthCallbackController)
146 | end
147 |
148 | it '/save_token' do
149 | expect(@mount_table['/save_token'][0]).to eq(Contentful::Bootstrap::SaveTokenController)
150 | end
151 | end
152 | end
153 | end
154 | end
155 |
--------------------------------------------------------------------------------
/spec/contentful/bootstrap/command_runner_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Contentful::Bootstrap::CommandRunner do
4 | let(:path) { File.expand_path(File.join('spec', 'fixtures', 'ini_fixtures', 'contentfulrc.ini')) }
5 | subject { Contentful::Bootstrap::CommandRunner.new(path) }
6 |
7 | describe 'instance methods' do
8 | describe '#create_space' do
9 | before do
10 | allow_any_instance_of(Contentful::Bootstrap::Commands::CreateSpace).to receive(:run)
11 | end
12 |
13 | it 'default values' do
14 | expect(Contentful::Bootstrap::Commands::CreateSpace).to receive(:new).with(
15 | subject.token, 'foo', {}
16 | ).and_call_original
17 |
18 | subject.create_space('foo')
19 | end
20 |
21 | it 'with options' do
22 | expect(Contentful::Bootstrap::Commands::CreateSpace).to receive(:new).with(
23 | subject.token, 'foo', template: 'bar', json_template: 'baz', mark_processed: true, trigger_oauth: false
24 | ).and_call_original
25 |
26 | subject.create_space('foo', template: 'bar', json_template: 'baz', mark_processed: true, trigger_oauth: false)
27 | end
28 |
29 | it 'runs command' do
30 | expect_any_instance_of(Contentful::Bootstrap::Commands::CreateSpace).to receive(:run)
31 |
32 | subject.create_space('foo', template: 'bar', json_template: 'baz', trigger_oauth: false)
33 | end
34 | end
35 |
36 | describe '#update_space' do
37 | before do
38 | allow_any_instance_of(Contentful::Bootstrap::Commands::UpdateSpace).to receive(:run)
39 | end
40 |
41 | it 'default values' do
42 | expect(Contentful::Bootstrap::Commands::UpdateSpace).to receive(:new).with(
43 | subject.token, 'foo', {}
44 | ).and_call_original
45 |
46 | subject.update_space('foo')
47 | end
48 |
49 | it 'with options' do
50 | expect(Contentful::Bootstrap::Commands::UpdateSpace).to receive(:new).with(
51 | subject.token, 'foo', json_template: 'bar', mark_processed: true, trigger_oauth: false
52 | ).and_call_original
53 |
54 | subject.update_space('foo', json_template: 'bar', mark_processed: true, trigger_oauth: false)
55 | end
56 |
57 | it 'runs command' do
58 | expect_any_instance_of(Contentful::Bootstrap::Commands::UpdateSpace).to receive(:run)
59 |
60 | subject.update_space('foo', json_template: 'bar', trigger_oauth: false)
61 | end
62 | end
63 |
64 | describe '#generate_token' do
65 | before do
66 | allow_any_instance_of(Contentful::Bootstrap::Commands::GenerateToken).to receive(:run)
67 | allow_any_instance_of(Contentful::Bootstrap::Commands::GenerateToken).to receive(:fetch_space)
68 | end
69 |
70 | it 'default values' do
71 | expect(Contentful::Bootstrap::Commands::GenerateToken).to receive(:new).with(
72 | subject.token, 'foo', {}
73 | ).and_call_original
74 |
75 | subject.generate_token('foo')
76 | end
77 |
78 | it 'with options' do
79 | expect(Contentful::Bootstrap::Commands::GenerateToken).to receive(:new).with(
80 | subject.token, 'foo', name: 'bar', trigger_oauth: false
81 | ).and_call_original
82 |
83 | subject.generate_token('foo', name: 'bar', trigger_oauth: false)
84 | end
85 |
86 | it 'runs command' do
87 | expect_any_instance_of(Contentful::Bootstrap::Commands::GenerateToken).to receive(:run)
88 |
89 | subject.generate_token('foo', name: 'bar', trigger_oauth: false)
90 | end
91 | end
92 |
93 | describe '#generate_json' do
94 | it 'requires access token to run' do
95 | expect {
96 | subject.generate_json('foo')
97 | }.to raise_error('Access Token required')
98 | end
99 |
100 | it 'default values' do
101 | allow_any_instance_of(Contentful::Bootstrap::Commands::GenerateJson).to receive(:run)
102 |
103 | expect(Contentful::Bootstrap::Commands::GenerateJson).to receive(:new).with(
104 | 'foo', 'bar', 'master', nil, false, false, false, []
105 | ).and_call_original
106 |
107 | subject.generate_json('foo', access_token: 'bar')
108 | end
109 |
110 | it 'with options' do
111 | allow_any_instance_of(Contentful::Bootstrap::Commands::GenerateJson).to receive(:run)
112 |
113 | expect(Contentful::Bootstrap::Commands::GenerateJson).to receive(:new).with(
114 | 'foo', 'bar', 'master', 'baz', true, false, false, []
115 | ).and_call_original
116 |
117 | subject.generate_json('foo', access_token: 'bar', filename: 'baz', content_types_only: true)
118 | end
119 |
120 | it 'runs command' do
121 | expect_any_instance_of(Contentful::Bootstrap::Commands::GenerateJson).to receive(:run)
122 |
123 | subject.generate_json('foo', access_token: 'bar', filename: 'baz')
124 | end
125 | end
126 | end
127 |
128 | describe 'attributes' do
129 | it ':config_path' do
130 | expect(subject.config_path).to eq path
131 | end
132 |
133 | it ':token' do
134 | expect(subject.token == Contentful::Bootstrap::Token.new(path)).to be_truthy
135 | end
136 | end
137 | end
138 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/generator.rb:
--------------------------------------------------------------------------------
1 | require 'contentful'
2 | require 'inifile'
3 | require 'json'
4 | require 'zlib'
5 | require 'contentful/bootstrap/version'
6 |
7 | module Contentful
8 | module Bootstrap
9 | class Generator
10 | DELIVERY_API_URL = 'cdn.contentful.com'.freeze
11 | PREVIEW_API_URL = 'preview.contentful.com'.freeze
12 |
13 | attr_reader :content_types_only, :content_type_ids, :client
14 |
15 | def initialize(space_id, access_token, environment, content_types_only, use_preview, content_type_ids)
16 | @client = Contentful::Client.new(
17 | space: space_id,
18 | access_token: access_token,
19 | environment: environment,
20 | application_name: 'bootstrap',
21 | application_version: ::Contentful::Bootstrap::VERSION,
22 | api_url: use_preview ? PREVIEW_API_URL : DELIVERY_API_URL
23 | )
24 | @content_types_only = content_types_only
25 | @content_type_ids = content_type_ids
26 | end
27 |
28 | def generate_json
29 | template = {}
30 | template['version'] = Contentful::Bootstrap.major_version
31 | template['contentTypes'] = content_types
32 | template['assets'] = assets
33 | template['entries'] = entries
34 | JSON.pretty_generate(template)
35 | end
36 |
37 | private
38 |
39 | def content_types
40 | query = {}
41 | query['sys.id[in]'] = content_type_ids.join(',') unless content_type_ids.empty?
42 |
43 | proccessed_content_types = @client.content_types(query).map do |type|
44 | result = { 'id' => type.sys[:id], 'name' => type.name }
45 | result['displayField'] = type.display_field unless type.display_field.nil?
46 |
47 | result['fields'] = type.fields.map do |field|
48 | map_field_properties(field)
49 | end
50 |
51 | result
52 | end
53 | proccessed_content_types.sort_by { |item| item['id'] }
54 | end
55 |
56 | def assets
57 | return [] if content_types_only
58 |
59 | processed_assets = []
60 |
61 | query = { order: 'sys.createdAt', limit: 1000 }
62 | assets_count = @client.assets(limit: 1).total
63 | ((assets_count / 1000) + 1).times do |i|
64 | query[:skip] = i * 1000
65 |
66 | @client.assets(query).each do |asset|
67 | processed_asset = {
68 | 'id' => asset.sys[:id],
69 | 'title' => asset.title,
70 | 'file' => {
71 | 'filename' => ::File.basename(asset.file.file_name, '.*'),
72 | 'url' => "https:#{asset.file.url}"
73 | }
74 | }
75 | processed_assets << processed_asset
76 | end
77 | end
78 |
79 | processed_assets.sort_by { |item| item['id'] }
80 | end
81 |
82 | def entries
83 | return {} if content_types_only
84 |
85 | entries = {}
86 |
87 | query = { order: 'sys.createdAt', limit: 1000 }
88 | count_query = { limit: 1 }
89 |
90 | unless content_type_ids.empty?
91 | search_key = 'sys.contentType.sys.id[in]'
92 | ids = content_type_ids.join(',')
93 |
94 | query[search_key] = ids
95 | count_query[search_key] = ids
96 | end
97 |
98 | entries_count = @client.entries(count_query).total
99 | ((entries_count / 1000) + 1).times do |i|
100 | query[:skip] = i * 1000
101 |
102 | @client.entries(query).each do |entry|
103 | result = { 'sys' => { 'id' => entry.sys[:id] }, 'fields' => {} }
104 |
105 | entry.fields.each do |key, value|
106 | value = map_field(value)
107 | result['fields'][field_id(entry, key)] = value unless value.nil?
108 | end
109 |
110 | ct_id = entry.content_type.sys[:id]
111 | entries[ct_id] = [] if entries[ct_id].nil?
112 | entries[ct_id] << result
113 | end
114 | end
115 |
116 | entries
117 | end
118 |
119 | def field_id(entry, field_name)
120 | entry.raw['fields'].keys.detect { |f| f == field_name.to_s || f == Support.camel_case(field_name.to_s).to_s }
121 | end
122 |
123 | def map_field(value)
124 | return value.map { |v| map_field(v) } if value.is_a? ::Array
125 |
126 | if value.is_a?(Contentful::Asset) || value.is_a?(Contentful::Entry)
127 | return {
128 | 'linkType' => value.class.name.split('::').last,
129 | 'id' => value.sys[:id]
130 | }
131 | end
132 |
133 | return nil if value.is_a?(Contentful::Link)
134 |
135 | value
136 | end
137 |
138 | def map_field_properties(field)
139 | properties = {}
140 |
141 | %i[id name type link_type required localized].each do |property|
142 | value = field.public_send(property) if field.respond_to?(property)
143 | properties[Support.camel_case(property.to_s).to_sym] = value unless value.nil? || %i[required localized].include?(property)
144 | end
145 |
146 | items = field.items if field.respond_to?(:items)
147 | properties[:items] = map_field_properties(items) unless items.nil?
148 |
149 | properties
150 | end
151 | end
152 | end
153 | end
154 |
--------------------------------------------------------------------------------
/spec/fixtures/json_fixtures/cfexampleapi_cat_human.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "contentTypes": [
4 | {
5 | "id": "cat",
6 | "name": "Cat",
7 | "displayField": "name",
8 | "fields": [
9 | {
10 | "id": "name",
11 | "name": "Name",
12 | "type": "Text"
13 | },
14 | {
15 | "id": "likes",
16 | "name": "Likes",
17 | "type": "Array",
18 | "items": {
19 | "type": "Symbol"
20 | }
21 | },
22 | {
23 | "id": "color",
24 | "name": "Color",
25 | "type": "Symbol"
26 | },
27 | {
28 | "id": "bestFriend",
29 | "name": "Best Friend",
30 | "type": "Link",
31 | "linkType": "Entry"
32 | },
33 | {
34 | "id": "birthday",
35 | "name": "Birthday",
36 | "type": "Date"
37 | },
38 | {
39 | "id": "lifes",
40 | "name": "Lifes left",
41 | "type": "Integer"
42 | },
43 | {
44 | "id": "lives",
45 | "name": "Lives left",
46 | "type": "Integer"
47 | },
48 | {
49 | "id": "image",
50 | "name": "Image",
51 | "type": "Link",
52 | "linkType": "Asset"
53 | }
54 | ]
55 | },
56 | {
57 | "id": "human",
58 | "name": "Human",
59 | "displayField": "name",
60 | "fields": [
61 | {
62 | "id": "name",
63 | "name": "Name",
64 | "type": "Text"
65 | },
66 | {
67 | "id": "description",
68 | "name": "Description",
69 | "type": "Text"
70 | },
71 | {
72 | "id": "likes",
73 | "name": "Likes",
74 | "type": "Array",
75 | "items": {
76 | "type": "Symbol"
77 | }
78 | },
79 | {
80 | "id": "image",
81 | "name": "Image",
82 | "type": "Array",
83 | "items": {
84 | "type": "Link",
85 | "linkType": "Asset"
86 | }
87 | }
88 | ]
89 | }
90 | ],
91 | "assets": [
92 | {
93 | "id": "1x0xpXu4pSGS4OukSyWGUK",
94 | "title": "Doge",
95 | "file": {
96 | "filename": "doge",
97 | "url": "https://images.contentful.com/cfexampleapi/1x0xpXu4pSGS4OukSyWGUK/cc1239c6385428ef26f4180190532818/doge.jpg"
98 | }
99 | },
100 | {
101 | "id": "happycat",
102 | "title": "Happy Cat",
103 | "file": {
104 | "filename": "happycatw",
105 | "url": "https://images.contentful.com/cfexampleapi/3MZPnjZTIskAIIkuuosCss/382a48dfa2cb16c47aa2c72f7b23bf09/happycatw.jpg"
106 | }
107 | },
108 | {
109 | "id": "jake",
110 | "title": "Jake",
111 | "file": {
112 | "filename": "jake",
113 | "url": "https://images.contentful.com/cfexampleapi/4hlteQAXS8iS0YCMU6QMWg/2a4d826144f014109364ccf5c891d2dd/jake.png"
114 | }
115 | },
116 | {
117 | "id": "nyancat",
118 | "title": "Nyan Cat",
119 | "file": {
120 | "filename": "Nyan_cat_250px_frame",
121 | "url": "https://images.contentful.com/cfexampleapi/4gp6taAwW4CmSgumq2ekUm/9da0cd1936871b8d72343e895a00d611/Nyan_cat_250px_frame.png"
122 | }
123 | }
124 | ],
125 | "entries": {
126 | "cat": [
127 | {
128 | "sys": {
129 | "id": "nyancat"
130 | },
131 | "fields": {
132 | "name": "Nyan Cat",
133 | "likes": [
134 | "rainbows",
135 | "fish"
136 | ],
137 | "color": "rainbow",
138 | "bestFriend": {
139 | "linkType": "Entry",
140 | "id": "happycat"
141 | },
142 | "birthday": "2011-04-04T22:00:00+00:00",
143 | "lives": 1337,
144 | "image": {
145 | "linkType": "Asset",
146 | "id": "nyancat"
147 | }
148 | }
149 | },
150 | {
151 | "sys": {
152 | "id": "happycat"
153 | },
154 | "fields": {
155 | "name": "Happy Cat",
156 | "likes": [
157 | "cheezburger"
158 | ],
159 | "color": "gray",
160 | "bestFriend": {
161 | "linkType": "Entry",
162 | "id": "nyancat"
163 | },
164 | "birthday": "2003-10-28T23:00:00+00:00",
165 | "lives": 1,
166 | "image": {
167 | "linkType": "Asset",
168 | "id": "happycat"
169 | }
170 | }
171 | },
172 | {
173 | "sys": {
174 | "id": "garfield"
175 | },
176 | "fields": {
177 | "name": "Garfield",
178 | "likes": [
179 | "lasagna"
180 | ],
181 | "color": "orange",
182 | "birthday": "1979-06-18T23:00:00+00:00",
183 | "lives": 9
184 | }
185 | }
186 | ],
187 | "human": [
188 | {
189 | "sys": {
190 | "id": "finn"
191 | },
192 | "fields": {
193 | "name": "Finn",
194 | "description": "Fearless adventurer! Defender of pancakes.",
195 | "likes": [
196 | "adventure"
197 | ]
198 | }
199 | }
200 | ]
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/spec/contentful/bootstrap/token_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Contentful::Bootstrap::Token do
4 | let(:path) { File.expand_path(File.join('spec', 'fixtures', 'ini_fixtures', 'contentfulrc.ini')) }
5 | let(:no_token_path) { File.expand_path(File.join('spec', 'fixtures', 'ini_fixtures', 'no_token.ini')) }
6 | let(:sections_path) { File.expand_path(File.join('spec', 'fixtures', 'ini_fixtures', 'sections.ini')) }
7 | let(:no_global_path) { File.expand_path(File.join('spec', 'fixtures', 'ini_fixtures', 'no_global.ini')) }
8 | let(:with_org_id) { File.expand_path(File.join('spec', 'fixtures', 'ini_fixtures', 'orgid.ini')) }
9 | subject { Contentful::Bootstrap::Token.new(path) }
10 |
11 | describe 'attributes' do
12 | it ':config_path' do
13 | expect(subject.config_path).to eq path
14 | end
15 | end
16 |
17 | describe 'initialize' do
18 | it 'uses provided config_path' do
19 | expect(subject.class.new('bar').config_path).to eq 'bar'
20 | end
21 | end
22 |
23 | describe 'instance methods' do
24 | describe '#present?' do
25 | it 'non existing file returns false' do
26 | expect(subject.class.new('foobar').present?).to be_falsey
27 | end
28 |
29 | it 'existing file without management token returns false' do
30 | expect(subject.class.new(no_token_path).present?).to be_falsey
31 | end
32 |
33 | it 'existing file with management token returns true' do
34 | expect(subject.present?).to be_truthy
35 | end
36 | end
37 |
38 | describe '#filename' do
39 | it 'returns default path if config_path is empty' do
40 | expect(subject.class.new.filename).to eq ::File.join(ENV['HOME'], subject.class::DEFAULT_PATH)
41 | end
42 |
43 | it 'returns config_path' do
44 | expect(subject.filename).to eq path
45 | end
46 | end
47 |
48 | describe '#config_section' do
49 | it 'returns default section by default' do
50 | expect(subject.config_section).to eq 'global'
51 | end
52 |
53 | it 'returns default section if ENV["CONTENTFUL_ENV"] section does not exist' do
54 | ENV['CONTENTFUL_ENV'] = 'blah'
55 | expect(subject.class.new(sections_path).config_section).to eq 'global'
56 | ENV['CONTENTFUL_ENV'] = ''
57 | end
58 |
59 | it 'returns ENV["CONTENTFUL_ENV"] section if exists' do
60 | ENV['CONTENTFUL_ENV'] = 'other_section'
61 | expect(subject.class.new(sections_path).config_section).to eq 'other_section'
62 | ENV['CONTENTFUL_ENV'] = ''
63 | end
64 | end
65 |
66 | describe '#read' do
67 | it 'fails if "global" section does not exist' do
68 | expect { subject.class.new(no_global_path).read }.to raise_error 'Token not found'
69 | end
70 |
71 | it 'fails if management token is not found' do
72 | expect { subject.class.new(no_token_path).read }.to raise_error 'Token not found'
73 | end
74 |
75 | it 'returns token if its found' do
76 | expect(subject.read).to eq 'foobar'
77 | end
78 | end
79 |
80 | describe '#read_organization_id' do
81 | it 'nil if default org id is not set' do
82 | expect(subject.read_organization_id).to be_nil
83 | end
84 |
85 | it 'returns value of org id is set' do
86 | expect(subject.class.new(with_org_id).read_organization_id).to eq 'my_org'
87 | end
88 | end
89 |
90 | describe 'write methods' do
91 | before do
92 | @file = subject.config_file
93 | expect(@file).to receive(:save)
94 | end
95 |
96 | it '#write_access_token' do
97 |
98 | expect(@file.has_section?('some_space')).to be_falsey
99 |
100 | subject.write_access_token('some_space', 'asd')
101 |
102 | expect(@file['some_space']['CONTENTFUL_DELIVERY_ACCESS_TOKEN']).to eq 'asd'
103 | end
104 |
105 | it '#write_space_id' do
106 | expect(@file.has_section?('some_space')).to be_falsey
107 |
108 | subject.write_space_id('some_space', 'asd')
109 |
110 | expect(@file['some_space']['SPACE_ID']).to eq 'asd'
111 | end
112 |
113 | it '#write_organization_id' do
114 | subject.write_organization_id('foo')
115 | expect(@file['global']['CONTENTFUL_ORGANIZATION_ID']).to eq 'foo'
116 | end
117 |
118 | describe '#write' do
119 | it 'writes management tokens when only value is sent' do
120 | expect(@file['global']['CONTENTFUL_MANAGEMENT_ACCESS_TOKEN']).to eq 'foobar'
121 |
122 | subject.write('baz')
123 |
124 | expect(@file['global']['CONTENTFUL_MANAGEMENT_ACCESS_TOKEN']).to eq 'baz'
125 | end
126 |
127 | it 'can write management tokens to any section' do
128 | expect(@file.has_section?('blah')).to be_falsey
129 |
130 | subject.write('baz', 'blah')
131 |
132 | expect(@file['blah']['CONTENTFUL_MANAGEMENT_ACCESS_TOKEN']).to eq 'baz'
133 | end
134 |
135 | it 'can write any key to any section' do
136 | expect(@file.has_section?('blah')).to be_falsey
137 |
138 | subject.write('foo', 'bar', 'baz')
139 |
140 | expect(@file['bar']['baz']).to eq 'foo'
141 | end
142 | end
143 | end
144 |
145 | describe '#==' do
146 | it 'returns false when other is not a token' do
147 | expect(subject == 1).to be_falsey
148 | end
149 |
150 | it 'returns false when other token does not have same config_path' do
151 | expect(subject == subject.class.new('foo')).to be_falsey
152 | end
153 |
154 | it 'returns true when other token has same config_path' do
155 | expect(subject == subject.class.new(path)).to be_truthy
156 | end
157 | end
158 | end
159 | end
160 |
--------------------------------------------------------------------------------
/spec/fixtures/vcr_fixtures/check_created_space.yml:
--------------------------------------------------------------------------------
1 | ---
2 | http_interactions:
3 | - request:
4 | method: get
5 | uri: https://cdn.contentful.com/spaces/vsy1ouf6jdcq/environments/master/content_types?limit=1000
6 | body:
7 | encoding: US-ASCII
8 | string: ''
9 | headers:
10 | X-Contentful-User-Agent:
11 | - sdk contentful.rb/2.1.3; platform ruby/2.4.1; os macOS/16;
12 | Authorization:
13 | - Bearer 90e1b4964c3631cc9c751c42339814635623b001a53aec5aad23377299445433
14 | Content-Type:
15 | - application/vnd.contentful.delivery.v1+json
16 | Accept-Encoding:
17 | - gzip
18 | Connection:
19 | - close
20 | Host:
21 | - cdn.contentful.com
22 | User-Agent:
23 | - http.rb/2.2.2
24 | response:
25 | status:
26 | code: 200
27 | message: OK
28 | headers:
29 | Access-Control-Allow-Headers:
30 | - Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation,X-Contentful-User-Agent,X-Contentful-Enable-Experimental-Feature
31 | Access-Control-Allow-Methods:
32 | - GET,HEAD,OPTIONS
33 | Access-Control-Allow-Origin:
34 | - "*"
35 | Access-Control-Expose-Headers:
36 | - Etag
37 | Access-Control-Max-Age:
38 | - '86400'
39 | Cache-Control:
40 | - max-age=0
41 | Content-Type:
42 | - application/vnd.contentful.delivery.v1+json
43 | Etag:
44 | - '"c283f5e14b648b925eac46aa3ee07ca3"'
45 | Server:
46 | - Contentful
47 | X-Content-Type-Options:
48 | - nosniff
49 | X-Contentful-Request-Id:
50 | - d1e40cd5d4cfb17b8122b3b8256bf31a
51 | Content-Length:
52 | - '98'
53 | Accept-Ranges:
54 | - bytes
55 | Date:
56 | - Fri, 18 Aug 2017 21:03:09 GMT
57 | Via:
58 | - 1.1 varnish
59 | Age:
60 | - '0'
61 | Connection:
62 | - close
63 | X-Served-By:
64 | - cache-atl6236-ATL
65 | X-Cache:
66 | - MISS
67 | X-Cache-Hits:
68 | - '0'
69 | X-Timer:
70 | - S1503090189.214686,VS0,VE206
71 | Vary:
72 | - Accept-Encoding
73 | body:
74 | encoding: ASCII-8BIT
75 | string: |
76 | {
77 | "sys": {
78 | "type": "Array"
79 | },
80 | "total": 0,
81 | "skip": 0,
82 | "limit": 1000,
83 | "items": []
84 | }
85 | http_version:
86 | recorded_at: Fri, 18 Aug 2017 21:03:09 GMT
87 | - request:
88 | method: get
89 | uri: https://cdn.contentful.com/spaces/vsy1ouf6jdcq
90 | body:
91 | encoding: US-ASCII
92 | string: ''
93 | headers:
94 | X-Contentful-User-Agent:
95 | - sdk contentful.rb/2.1.3; platform ruby/2.4.1; os macOS/16;
96 | Authorization:
97 | - Bearer 90e1b4964c3631cc9c751c42339814635623b001a53aec5aad23377299445433
98 | Content-Type:
99 | - application/vnd.contentful.delivery.v1+json
100 | Accept-Encoding:
101 | - gzip
102 | Connection:
103 | - close
104 | Host:
105 | - cdn.contentful.com
106 | User-Agent:
107 | - http.rb/2.2.2
108 | response:
109 | status:
110 | code: 200
111 | message: OK
112 | headers:
113 | Access-Control-Allow-Headers:
114 | - Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation,X-Contentful-User-Agent,X-Contentful-Enable-Experimental-Feature
115 | Access-Control-Allow-Methods:
116 | - GET,HEAD,OPTIONS
117 | Access-Control-Allow-Origin:
118 | - "*"
119 | Access-Control-Expose-Headers:
120 | - Etag
121 | Access-Control-Max-Age:
122 | - '86400'
123 | Cache-Control:
124 | - max-age=0
125 | Content-Type:
126 | - application/vnd.contentful.delivery.v1+json
127 | Etag:
128 | - '"41e2e178aad9b651bb9c230458dacaa0"'
129 | Server:
130 | - Contentful
131 | X-Content-Type-Options:
132 | - nosniff
133 | X-Contentful-Request-Id:
134 | - 07c944a494b440cb72fbfc4a8aaee17b
135 | Content-Length:
136 | - '229'
137 | Accept-Ranges:
138 | - bytes
139 | Date:
140 | - Fri, 18 Aug 2017 21:03:10 GMT
141 | Via:
142 | - 1.1 varnish
143 | Age:
144 | - '0'
145 | Connection:
146 | - close
147 | X-Served-By:
148 | - cache-atl6239-ATL
149 | X-Cache:
150 | - MISS
151 | X-Cache-Hits:
152 | - '0'
153 | X-Timer:
154 | - S1503090190.215703,VS0,VE174
155 | Vary:
156 | - Accept-Encoding
157 | body:
158 | encoding: ASCII-8BIT
159 | string: |
160 | {
161 | "sys": {
162 | "type": "Space",
163 | "id": "vsy1ouf6jdcq"
164 | },
165 | "name": "B.rb - locale creation",
166 | "locales": [
167 | {
168 | "code": "es-AR",
169 | "default": true,
170 | "name": "es-AR",
171 | "fallbackCode": null
172 | }
173 | ]
174 | }
175 | http_version:
176 | recorded_at: Fri, 18 Aug 2017 21:03:10 GMT
177 | recorded_with: VCR 2.9.3
178 |
--------------------------------------------------------------------------------
/bin/contentful_bootstrap:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'optparse'
4 | require 'contentful/bootstrap/command_runner'
5 |
6 | options = {}
7 |
8 | subcommands = {
9 | 'create_space' => OptionParser.new do |opts|
10 | opts.banner = 'Usage: create_space [--template TEMPLATE_NAME] [--json-template JSON_PATH] [--locale LOCALE] [--mark-processed] [--config CONFIG_PATH]'
11 | opts.on('-t TEMPLATE', '--template TEMPLATE', 'Specify Template', 'Available Templates: blog, catalogue, gallery') do |t|
12 | options[:template] = t
13 | end
14 | opts.on('-j JSON_PATH', '--json-template JSON_PATH', 'Specify JSON Template Path') do |j|
15 | options[:json_template] = j
16 | end
17 | opts.on('-l LOCALE', '--locale LOCALE', 'Set the Default Locale for the Space', "Defaults to 'en-US'") do |l|
18 | options[:locale] = l
19 | end
20 | opts.on('-m', '--mark-processed', 'Mark Processed Items on JSON Templates') do |m|
21 | options[:mark_processed] = m
22 | end
23 | opts.on('-c CONFIG_PATH', '--config CONFIG_PATH', 'Specify Configuration Path') do |c|
24 | options[:config_path] = c
25 | end
26 | opts.on('-q', '--quiet', "Don't output to STDOUT") do |q|
27 | options[:quiet] = q
28 | end
29 | opts.on('-n', '--no-publish', "Don't publish") do |n|
30 | options[:no_publish] = n
31 | end
32 | opts.on_tail('-h', '--help', 'Print this message') do
33 | puts opts
34 | end
35 | end,
36 | 'update_space' => OptionParser.new do |opts|
37 | opts.banner = 'Usage: update_space --json-template JSON_PATH [--environment ENVIRONMENT_ID] [--locale LOCALE] [--mark-processed] [--config CONFIG_PATH]'
38 | opts.on('-j JSON_PATH', '--json-template JSON_PATH', 'Specify JSON Template Path') do |j|
39 | options[:json_template] = j
40 | end
41 | opts.on('-e ENVIRONMENT_ID', '--environment ENVIRONMENT_ID', 'Specify target environment ID', "Defaults to 'master'") do |e|
42 | options[:environment] = e
43 | end
44 | opts.on('-l LOCALE', '--locale LOCALE', 'Set the Default Locale for the Space', "Defaults to 'en-US'") do |l|
45 | options[:locale] = l
46 | end
47 | opts.on('-m', '--mark-processed', 'Mark Processed Items on JSON Templates') do |m|
48 | options[:mark_processed] = m
49 | end
50 | opts.on('-T', '--skip-content-types', 'Skip Content Types on Space update') do |t|
51 | options[:skip_content_types] = t
52 | end
53 | opts.on('-c CONFIG_PATH', '--config CONFIG_PATH', 'Specify Configuration Path') do |c|
54 | options[:config_path] = c
55 | end
56 | opts.on('-n', '--no-publish', "Don't publish") do |n|
57 | options[:no_publish] = n
58 | end
59 | opts.on('-q', '--quiet', "Don't output to STDOUT") do |q|
60 | options[:quiet] = q
61 | end
62 | opts.on_tail('-h', '--help', 'Print this message') do
63 | puts opts
64 | end
65 | end,
66 | 'generate_token' => OptionParser.new do |opts|
67 | opts.banner = 'Usage: generate_token [--name TOKEN_NAME] [--config CONFIG_PATH]'
68 | opts.on('-n NAME', '--name TOKEN_NAME', 'Specify Token Name') do |n|
69 | options[:name] = n
70 | end
71 | opts.on('-c CONFIG_PATH', '--config CONFIG_PATH', 'Specify Configuration Path') do |c|
72 | options[:config_path] = c
73 | end
74 | opts.on('-q', '--quiet', "Don't output to STDOUT") do |q|
75 | options[:quiet] = q
76 | end
77 | opts.on_tail('-h', '--help', 'Print this message') do
78 | puts opts
79 | end
80 | end,
81 | 'generate_json' => OptionParser.new do |opts|
82 | opts.banner = 'Usage: generate_json [--environment ENVIRONMENT_ID] [--output-file OUTPUT_PATH] [--content-types-only] [--use-preview] [--content-type-ids , ]'
83 | opts.on('-e ENVIRONMENT_ID', '--environment ENVIRONMENT_ID', 'Specify target environment ID', "Defaults to 'master'") do |e|
84 | options[:environment] = e
85 | end
86 | opts.on('-t', '--content-types-only', 'Only fetch Content Types') do |t|
87 | options[:content_types_only] = t
88 | end
89 | opts.on('-p', '--use-preview', 'Use Preview API') do |p|
90 | options[:use_preview] = p
91 | end
92 | opts.on('-i', '--content-type-ids ,', ::Array, 'Array of content type IDs to import') do |ids|
93 | options[:content_type_ids] = ids
94 | end
95 | opts.on('-o OUTPUT_PATH', '--output-file OUTPUT_PATH', 'Specify Output File') do |f|
96 | options[:filename] = f
97 | end
98 | opts.on('-q', '--quiet', "Don't output to STDOUT") do |q|
99 | options[:quiet] = q
100 | end
101 | opts.on_tail('-h', '--help', 'Print this message') do
102 | puts opts
103 | end
104 | end
105 | }
106 |
107 | global = OptionParser.new do |opts|
108 | opts.banner = 'Usage: contentful_bootstrap [options]'
109 | opts.separator ''
110 | opts.separator <<-HELP
111 | Available commands are:
112 | \t#{subcommands.keys.sort.join("\n\t")}
113 | HELP
114 | opts.on_tail('-v', '--version', 'Show Version') do
115 | puts "Contentful Bootstrap: #{Contentful::Bootstrap::VERSION}"
116 | exit(0)
117 | end
118 | end
119 |
120 | global.order!
121 | command = ARGV.shift
122 |
123 | is_help = ARGV.include?('-h') || ARGV.include?('--help')
124 | space = ARGV.shift unless is_help
125 |
126 | options[:access_token] = ARGV.shift if command == 'generate_json' && !is_help
127 |
128 | if subcommands.key? command
129 | subcommands[command].order!
130 |
131 | unless STDIN.tty? && STDOUT.tty?
132 | $stderr.write "This tool requires user interaction\n"
133 | $stderr.write "Exiting!\n"
134 | exit(1)
135 | end
136 |
137 | exit if is_help
138 |
139 | if space.nil? || space.empty?
140 | ARGV << '-h'
141 | subcommands[command].order!
142 | puts
143 | puts 'Required Arguments not specified. Exiting!'
144 | exit
145 | end
146 |
147 | options[:trigger_oauth] = true
148 |
149 | Contentful::Bootstrap::CommandRunner.new(options.fetch(:config_path, '')).send(command, space, options)
150 | else
151 | puts 'Usage: contentful_bootstrap [options]'
152 | puts
153 | puts <<-HELP
154 | Subcommand not available or missing
155 | Available commands are:
156 | HELP
157 | puts "\t#{subcommands.keys.sort.join("\n\t")}"
158 | end
159 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/templates/json_template.rb:
--------------------------------------------------------------------------------
1 | require 'json'
2 | require 'contentful/bootstrap/templates/base'
3 | require 'contentful/bootstrap/templates/links'
4 |
5 | module Contentful
6 | module Bootstrap
7 | module Templates
8 | class JsonTemplate < Base
9 | CONTENT_TYPES_KEY = 'contentTypes'.freeze
10 | ENTRIES_KEY = 'entries'.freeze
11 | ASSETS_KEY = 'assets'.freeze
12 | BOOTSTRAP_PROCCESSED_KEY = 'bootstrapProcessed'.freeze
13 | DISPLAY_FIELD_KEY = 'displayField'.freeze
14 | ALTERNATE_DISPLAY_FIELD_KEY = 'display_field'.freeze
15 | SYS_KEY = 'sys'.freeze
16 |
17 | attr_reader :assets, :entries, :content_types
18 |
19 | def initialize(space, file, environment = 'master', mark_processed = false, all = true, quiet = false, skip_content_types = false, no_publish = false)
20 | @file = file
21 | json
22 | check_version
23 |
24 | super(space, environment, quiet, skip_content_types, no_publish)
25 |
26 | @all = all
27 | @mark_processed = mark_processed
28 |
29 | @assets = process_assets
30 | @entries = process_entries
31 | @content_types = process_content_types
32 | end
33 |
34 | def after_run
35 | return unless mark_processed?
36 |
37 | # Re-parse JSON to avoid side effects from `Templates::Base`
38 | @json = parse_json
39 |
40 | json.fetch(CONTENT_TYPES_KEY, []).each do |content_type|
41 | content_type[BOOTSTRAP_PROCCESSED_KEY] = true
42 | end
43 |
44 | json.fetch(ASSETS_KEY, []).each do |asset|
45 | asset[BOOTSTRAP_PROCCESSED_KEY] = true
46 | end
47 |
48 | json.fetch(ENTRIES_KEY, {}).each do |_content_type_name, entry_list|
49 | entry_list.each do |entry|
50 | if entry.key?(SYS_KEY)
51 | entry[SYS_KEY][BOOTSTRAP_PROCCESSED_KEY] = true
52 | else
53 | entry[SYS_KEY] = { BOOTSTRAP_PROCCESSED_KEY => true }
54 | end
55 | end
56 | end
57 |
58 | ::File.write(@file, JSON.pretty_generate(@json))
59 | end
60 |
61 | def json
62 | @json ||= parse_json
63 | end
64 |
65 | private
66 |
67 | def parse_json
68 | ::JSON.parse(::File.read(@file))
69 | rescue StandardError
70 | output 'File is not JSON. Exiting!'
71 | exit(1)
72 | end
73 |
74 | def check_version
75 | json_version = json.fetch('version', 0)
76 | gem_major = Contentful::Bootstrap.major_version
77 | raise "JSON Templates Version Mismatch. Current Version: #{gem_major}" unless gem_major == json_version
78 | end
79 |
80 | def process_content_types
81 | processed_content_types = json.fetch(CONTENT_TYPES_KEY, [])
82 |
83 | unless all?
84 | processed_content_types = processed_content_types.reject do |content_type|
85 | content_type.fetch(BOOTSTRAP_PROCCESSED_KEY, false)
86 | end
87 | end
88 |
89 | processed_content_types.each do |content_type|
90 | content_type[DISPLAY_FIELD_KEY] = if content_type.key?(ALTERNATE_DISPLAY_FIELD_KEY)
91 | content_type.delete(ALTERNATE_DISPLAY_FIELD_KEY)
92 | else
93 | content_type[DISPLAY_FIELD_KEY]
94 | end
95 | end
96 |
97 | processed_content_types
98 | end
99 |
100 | def process_assets
101 | unprocessed_assets = json.fetch(ASSETS_KEY, [])
102 |
103 | unless all?
104 | unprocessed_assets = unprocessed_assets.reject do |asset|
105 | asset.fetch(BOOTSTRAP_PROCCESSED_KEY, false)
106 | end
107 | end
108 |
109 | unprocessed_assets.map do |asset|
110 | asset['file'] = create_file(
111 | asset['file']['filename'],
112 | asset['file']['url'],
113 | contentType: asset['file'].fetch('contentType', 'image/jpeg')
114 | )
115 | asset
116 | end
117 | end
118 |
119 | def process_entry(entry)
120 | processed_entry = {}
121 | processed_entry['id'] = entry[SYS_KEY]['id'] if entry.key?(SYS_KEY) && entry[SYS_KEY].key?('id')
122 |
123 | entry.fetch('fields', {}).each do |field, value|
124 | if link?(value)
125 | processed_entry[field] = create_link(value)
126 | next
127 | elsif array?(value)
128 | processed_entry[field] = value.map { |i| link?(i) ? create_link(i) : i }
129 | next
130 | end
131 |
132 | processed_entry[field] = value
133 | end
134 |
135 | processed_entry
136 | end
137 |
138 | def process_entries
139 | unprocessed_entries = json.fetch(ENTRIES_KEY, {})
140 | unprocessed_entries.each_with_object({}) do |(content_type_id, entry_list), processed_entries|
141 | entries_for_content_type = []
142 |
143 | unless all?
144 | entry_list = entry_list.reject do |entry|
145 | entry[SYS_KEY].fetch(BOOTSTRAP_PROCCESSED_KEY, false)
146 | end
147 | end
148 |
149 | entry_list.each do |entry|
150 | entries_for_content_type << process_entry(entry)
151 | end
152 |
153 | processed_entries[content_type_id] = entries_for_content_type
154 | end
155 | end
156 |
157 | def create_link(link_properties)
158 | link_type = link_properties['linkType'].capitalize
159 | id = link_properties['id']
160 | case link_type
161 | when 'Entry'
162 | Contentful::Bootstrap::Templates::Links::Entry.new(id)
163 | when 'Asset'
164 | Contentful::Bootstrap::Templates::Links::Asset.new(id)
165 | end
166 | end
167 |
168 | def all?
169 | @all
170 | end
171 |
172 | def mark_processed?
173 | @mark_processed
174 | end
175 |
176 | def link?(value)
177 | value.is_a?(::Hash) && value.key?('id') && value.key?('linkType')
178 | end
179 |
180 | def array?(value)
181 | value.is_a?(::Array)
182 | end
183 | end
184 | end
185 | end
186 | end
187 |
--------------------------------------------------------------------------------
/spec/fixtures/vcr_fixtures/create_space.yml:
--------------------------------------------------------------------------------
1 | ---
2 | http_interactions:
3 | - request:
4 | method: post
5 | uri: https://api.contentful.com/spaces
6 | body:
7 | encoding: UTF-8
8 | string: '{"name":"blog","defaultLocale":"en-US"}'
9 | headers:
10 | User-Agent:
11 | - RubyContentfulManagementGem/0.7.2
12 | Authorization:
13 | - Bearer foobar
14 | Content-Type:
15 | - application/vnd.contentful.management.v1+json
16 | Connection:
17 | - close
18 | Host:
19 | - api.contentful.com
20 | response:
21 | status:
22 | code: 201
23 | message: Created
24 | headers:
25 | Accept-Ranges:
26 | - bytes
27 | Access-Control-Allow-Headers:
28 | - Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation
29 | Access-Control-Allow-Methods:
30 | - DELETE,GET,HEAD,POST,PUT,OPTIONS
31 | Access-Control-Allow-Origin:
32 | - "*"
33 | Access-Control-Expose-Headers:
34 | - Etag
35 | Access-Control-Max-Age:
36 | - '1728000'
37 | Cache-Control:
38 | - max-age=0
39 | Content-Type:
40 | - application/vnd.contentful.management.v1+json
41 | Date:
42 | - Wed, 18 Nov 2015 15:18:44 GMT
43 | Etag:
44 | - '"5ff2a2eea2a7ca0d643293849f3a7295"'
45 | Location:
46 | - https://api.contentful.com/spaces/imf7gr7jg4ed
47 | Server:
48 | - nginx
49 | Status:
50 | - 201 Created
51 | Strict-Transport-Security:
52 | - max-age=15768000
53 | X-Content-Type-Options:
54 | - nosniff
55 | X-Contentful-Request-Id:
56 | - 0b5-124655605
57 | Content-Length:
58 | - '447'
59 | Connection:
60 | - Close
61 | body:
62 | encoding: UTF-8
63 | string: |+
64 | {
65 | "sys":{
66 | "type":"Space",
67 | "id":"imf7gr7jg4ed",
68 | "version":1,
69 | "createdBy":{
70 | "sys":{
71 | "type":"Link",
72 | "linkType":"User",
73 | "id":"4SejVrWT96dvL9IV4Nb7sQ"
74 | }
75 | },
76 | "createdAt":"2015-11-18T15:18:43Z",
77 | "updatedBy":{
78 | "sys":{
79 | "type":"Link",
80 | "linkType":"User",
81 | "id":"4SejVrWT96dvL9IV4Nb7sQ"
82 | }
83 | },
84 | "updatedAt":"2015-11-18T15:18:43Z"
85 | },
86 | "name":"blog"
87 | }
88 |
89 | http_version:
90 | recorded_at: Wed, 18 Nov 2015 15:18:44 GMT
91 | - request:
92 | method: post
93 | uri: https://api.contentful.com/spaces/imf7gr7jg4ed/api_keys
94 | body:
95 | encoding: UTF-8
96 | string: '{"name":"Bootstrap Token","description":"Created with ''contentful_bootstrap.rb
97 | v2.0.2''"}'
98 | headers:
99 | User-Agent:
100 | - RubyContentfulManagementGem/0.7.2
101 | Authorization:
102 | - Bearer foobar
103 | Content-Type:
104 | - application/vnd.contentful.management.v1+json
105 | Connection:
106 | - close
107 | Host:
108 | - api.contentful.com
109 | response:
110 | status:
111 | code: 201
112 | message: Created
113 | headers:
114 | Accept-Ranges:
115 | - bytes
116 | Access-Control-Allow-Headers:
117 | - Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation
118 | Access-Control-Allow-Methods:
119 | - DELETE,GET,HEAD,POST,PUT,OPTIONS
120 | Access-Control-Allow-Origin:
121 | - "*"
122 | Access-Control-Expose-Headers:
123 | - Etag
124 | Access-Control-Max-Age:
125 | - '1728000'
126 | Cache-Control:
127 | - max-age=0
128 | Content-Type:
129 | - application/vnd.contentful.management.v1+json
130 | Date:
131 | - Wed, 18 Nov 2015 15:18:45 GMT
132 | Etag:
133 | - '"f8404c82e215f0cc39614ed00adc75b5"'
134 | Location:
135 | - https://api.contentful.com/spaces/imf7gr7jg4ed/api_keys/4y83USq9HGzofldbLvDrW0
136 | Server:
137 | - nginx
138 | Status:
139 | - 201 Created
140 | Strict-Transport-Security:
141 | - max-age=15768000
142 | X-Content-Type-Options:
143 | - nosniff
144 | X-Contentful-Request-Id:
145 | - 0b5-124655625
146 | Content-Length:
147 | - '954'
148 | Connection:
149 | - Close
150 | body:
151 | encoding: UTF-8
152 | string: |+
153 | {
154 | "sys":{
155 | "type":"ApiKey",
156 | "id":"4y83USq9HGzofldbLvDrW0",
157 | "version":0,
158 | "space":{
159 | "sys":{
160 | "type":"Link",
161 | "linkType":"Space",
162 | "id":"imf7gr7jg4ed"
163 | }
164 | },
165 | "createdBy":{
166 | "sys":{
167 | "type":"Link",
168 | "linkType":"User",
169 | "id":"4SejVrWT96dvL9IV4Nb7sQ"
170 | }
171 | },
172 | "createdAt":"2015-11-18T15:18:45Z",
173 | "updatedBy":{
174 | "sys":{
175 | "type":"Link",
176 | "linkType":"User",
177 | "id":"4SejVrWT96dvL9IV4Nb7sQ"
178 | }
179 | },
180 | "updatedAt":"2015-11-18T15:18:45Z"
181 | },
182 | "name":"Bootstrap Token",
183 | "description":"Created with 'contentful_bootstrap.rb v2.0.2'",
184 | "accessToken":"362afdb99e627ed0e70d71747cc905e1be3a2a85c798e57513ed6a2c0c2721f4",
185 | "policies":[
186 | {
187 | "effect":"allow",
188 | "actions":"all"
189 | }
190 | ],
191 | "preview_api_key":{
192 | "sys":{
193 | "type":"Link",
194 | "linkType":"PreviewApiKey",
195 | "id":"4y9pGFQBeSYI2AQQqJ84Ok"
196 | }
197 | }
198 | }
199 |
200 | http_version:
201 | recorded_at: Wed, 18 Nov 2015 15:18:45 GMT
202 | recorded_with: VCR 2.9.3
203 |
--------------------------------------------------------------------------------
/spec/fixtures/vcr_fixtures/check_update_space_localized.yml:
--------------------------------------------------------------------------------
1 | ---
2 | http_interactions:
3 | - request:
4 | method: get
5 | uri: https://cdn.contentful.com/spaces/vsy1ouf6jdcq/environments/master/content_types?limit=1000
6 | body:
7 | encoding: US-ASCII
8 | string: ''
9 | headers:
10 | X-Contentful-User-Agent:
11 | - sdk contentful.rb/2.1.3; platform ruby/2.4.1; os macOS/16;
12 | Authorization:
13 | - Bearer 90e1b4964c3631cc9c751c42339814635623b001a53aec5aad23377299445433
14 | Content-Type:
15 | - application/vnd.contentful.delivery.v1+json
16 | Accept-Encoding:
17 | - gzip
18 | Connection:
19 | - close
20 | Host:
21 | - cdn.contentful.com
22 | User-Agent:
23 | - http.rb/2.2.2
24 | response:
25 | status:
26 | code: 200
27 | message: OK
28 | headers:
29 | Access-Control-Allow-Headers:
30 | - Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation,X-Contentful-User-Agent,X-Contentful-Enable-Experimental-Feature
31 | Access-Control-Allow-Methods:
32 | - GET,HEAD,OPTIONS
33 | Access-Control-Allow-Origin:
34 | - "*"
35 | Access-Control-Expose-Headers:
36 | - Etag
37 | Access-Control-Max-Age:
38 | - '86400'
39 | Cache-Control:
40 | - max-age=0
41 | Content-Type:
42 | - application/vnd.contentful.delivery.v1+json
43 | Etag:
44 | - '"594786b7b50ce50203bbce682cdb5782"'
45 | Server:
46 | - Contentful
47 | X-Content-Type-Options:
48 | - nosniff
49 | X-Contentful-Request-Id:
50 | - 32854106ee78a518ebdd3a86aa4aa8a8
51 | Content-Length:
52 | - '780'
53 | Accept-Ranges:
54 | - bytes
55 | Date:
56 | - Wed, 23 Aug 2017 14:39:47 GMT
57 | Via:
58 | - 1.1 varnish
59 | Age:
60 | - '0'
61 | Connection:
62 | - close
63 | X-Served-By:
64 | - cache-gru17131-GRU
65 | X-Cache:
66 | - MISS
67 | X-Cache-Hits:
68 | - '0'
69 | X-Timer:
70 | - S1503499187.395554,VS0,VE263
71 | Vary:
72 | - Accept-Encoding
73 | body:
74 | encoding: ASCII-8BIT
75 | string: |
76 | {
77 | "sys": {
78 | "type": "Array"
79 | },
80 | "total": 1,
81 | "skip": 0,
82 | "limit": 1000,
83 | "items": [
84 | {
85 | "sys": {
86 | "space": {
87 | "sys": {
88 | "type": "Link",
89 | "linkType": "Space",
90 | "id": "vsy1ouf6jdcq"
91 | }
92 | },
93 | "id": "test",
94 | "type": "ContentType",
95 | "createdAt": "2017-08-23T14:02:48.328Z",
96 | "updatedAt": "2017-08-23T14:02:48.328Z",
97 | "revision": 1
98 | },
99 | "displayField": "text",
100 | "name": "Test",
101 | "description": "",
102 | "fields": [
103 | {
104 | "id": "text",
105 | "name": "Text",
106 | "type": "Symbol",
107 | "localized": true,
108 | "required": false,
109 | "disabled": false,
110 | "omitted": false
111 | }
112 | ]
113 | }
114 | ]
115 | }
116 | http_version:
117 | recorded_at: Wed, 23 Aug 2017 14:39:47 GMT
118 | - request:
119 | method: get
120 | uri: https://cdn.contentful.com/spaces/vsy1ouf6jdcq/environments/master/entries?locale=es-AR
121 | body:
122 | encoding: US-ASCII
123 | string: ''
124 | headers:
125 | X-Contentful-User-Agent:
126 | - sdk contentful.rb/2.1.3; platform ruby/2.4.1; os macOS/16;
127 | Authorization:
128 | - Bearer 90e1b4964c3631cc9c751c42339814635623b001a53aec5aad23377299445433
129 | Content-Type:
130 | - application/vnd.contentful.delivery.v1+json
131 | Accept-Encoding:
132 | - gzip
133 | Connection:
134 | - close
135 | Host:
136 | - cdn.contentful.com
137 | User-Agent:
138 | - http.rb/2.2.2
139 | response:
140 | status:
141 | code: 200
142 | message: OK
143 | headers:
144 | Access-Control-Allow-Headers:
145 | - Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation,X-Contentful-User-Agent,X-Contentful-Enable-Experimental-Feature
146 | Access-Control-Allow-Methods:
147 | - GET,HEAD,OPTIONS
148 | Access-Control-Allow-Origin:
149 | - "*"
150 | Access-Control-Expose-Headers:
151 | - Etag
152 | Access-Control-Max-Age:
153 | - '86400'
154 | Cache-Control:
155 | - max-age=0
156 | Content-Encoding:
157 | - gzip
158 | Content-Type:
159 | - application/vnd.contentful.delivery.v1+json
160 | Etag:
161 | - W/"4b2e84b99e4024a461dd9b1a98e6743c"
162 | Server:
163 | - Contentful
164 | X-Content-Type-Options:
165 | - nosniff
166 | X-Contentful-Request-Id:
167 | - 8f60a93bc7915a078a7b094062c8d915
168 | Content-Length:
169 | - '322'
170 | Accept-Ranges:
171 | - bytes
172 | Date:
173 | - Wed, 23 Aug 2017 14:39:48 GMT
174 | Via:
175 | - 1.1 varnish
176 | Age:
177 | - '0'
178 | Connection:
179 | - close
180 | X-Served-By:
181 | - cache-gru17131-GRU
182 | X-Cache:
183 | - MISS
184 | X-Cache-Hits:
185 | - '0'
186 | X-Timer:
187 | - S1503499188.241671,VS0,VE140
188 | Vary:
189 | - Accept-Encoding
190 | body:
191 | encoding: ASCII-8BIT
192 | string: !binary |-
193 | H4sIAAAAAAAAA+VSy07DMBC85ysin0mVVxvILSA4cYKeQAhZiSuZpHawtxVWlX/Hdh61UiRUqRfEnjybnX1M5uD5PpJKotw/6KcGoFqiESqEwArpXHdlaoADbnQ+tkjWtNUgtKChWwoaRWGPKZCtafhqG/ZtZ1PsJNni0owaK/qks4tJ2OS40yNlNTIzj6Gns3o97PxsO84KaGXO2UsV8d1m9VGVn+aqMbrpbe/sA/UcIBLeI6fftMg9A6HcD6UgGEhVGB1QHEZZEF4HcbKO0jy5ydN0kYXZi0vYtdV5BEH2VFLOjNDOpiVnQBgMClxOyjun74+CGnF+F7LhJW6sn4gMiqeRMGmNNpQ01dF+Rn8E5Mvq+MD5RLD/ZqD9dUvFl7DUarFMludY6pTwDy11i8XMUtpYb17nfQPclY1+CQUAAA==
194 | http_version:
195 | recorded_at: Wed, 23 Aug 2017 14:39:48 GMT
196 | recorded_with: VCR 2.9.3
197 |
--------------------------------------------------------------------------------
/spec/fixtures/vcr_fixtures/check_original_staging_environment_status.yml:
--------------------------------------------------------------------------------
1 | ---
2 | http_interactions:
3 | - request:
4 | method: get
5 | uri: https://cdn.contentful.com/spaces/9utsm1g0t7f5/environments/staging/content_types?limit=1000
6 | body:
7 | encoding: US-ASCII
8 | string: ''
9 | headers:
10 | X-Contentful-User-Agent:
11 | - sdk contentful.rb/2.6.0; platform ruby/2.5.1; os macOS/16;
12 | Authorization:
13 | - Bearer a67d4d9011f6d6c1dfe4169d838114d3d3849ab6df6fb1d322cf3ee91690fae4
14 | Content-Type:
15 | - application/vnd.contentful.delivery.v1+json
16 | Accept-Encoding:
17 | - gzip
18 | Connection:
19 | - close
20 | Host:
21 | - cdn.contentful.com
22 | User-Agent:
23 | - http.rb/2.2.2
24 | response:
25 | status:
26 | code: 200
27 | message: OK
28 | headers:
29 | Access-Control-Allow-Headers:
30 | - Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation,X-Contentful-User-Agent,X-Contentful-Enable-Alpha-Feature
31 | Access-Control-Allow-Methods:
32 | - GET,HEAD,OPTIONS
33 | Access-Control-Allow-Origin:
34 | - "*"
35 | Access-Control-Expose-Headers:
36 | - Etag
37 | Access-Control-Max-Age:
38 | - '86400'
39 | Cache-Control:
40 | - max-age=0
41 | Content-Encoding:
42 | - gzip
43 | Content-Type:
44 | - application/vnd.contentful.delivery.v1+json
45 | Etag:
46 | - W/"16b325fc276a429445c1bb5f86871c51"
47 | Server:
48 | - Contentful
49 | X-Content-Type-Options:
50 | - nosniff
51 | X-Contentful-Request-Id:
52 | - 42ce7683a009a3589203be2d29e2713c
53 | Content-Length:
54 | - '397'
55 | Accept-Ranges:
56 | - bytes
57 | Date:
58 | - Wed, 18 Apr 2018 11:39:53 GMT
59 | Via:
60 | - 1.1 varnish
61 | Age:
62 | - '0'
63 | Connection:
64 | - close
65 | X-Served-By:
66 | - cache-hhn1549-HHN
67 | X-Cache:
68 | - MISS
69 | X-Cache-Hits:
70 | - '0'
71 | X-Timer:
72 | - S1524051594.639483,VS0,VE247
73 | Vary:
74 | - Accept-Encoding
75 | body:
76 | encoding: ASCII-8BIT
77 | string: !binary |-
78 | H4sIAAAAAAAAA8VTPW/CMBDd+RXIc6kSoC2woapMVReYWjGYxEEnHDu1DWqK+O/12UlwIlDpVC/Jfb57z+djr98nutRk1j/aX2uYsmDWInOlaEms73SHOUYayq0/dpbeQWGNyBkccjAYiiLvAMNy7PjhOvq+HRgHpQuaIFad4Z3BMOhwznqoVxA7gqDnY+HFblUNvXQdOwmQIp/p3ug83kbmKXtAWvU5Nf+OqD/E12RSBs2aKZ6lMEwYBxrUJIpRw9I5akGGUTwZRONBPFnF8Ww4nY0e76PR+D0s2Bfp3wqYOICSIrfgN8nmWWhDtyC2XVlu1fQlAP1VN8UOoEEKXIcquZGVpKALTssFMO6uRND8LKC3rHcRiE5SphMFhfEtScOBZNjkvGR4ba018tRbCJjToLyF2C5S67Es843kLbUIlwnl8M1w7IxyzcIVI4p97kFdCVrSdMOvBKV9OXZl6q6XVvECq8TvX3tER9XKVy1nO1hzW7Ev88/MKo5r98XHt+6dej+I71T9hwQAAA==
79 | http_version:
80 | recorded_at: Wed, 18 Apr 2018 11:39:54 GMT
81 | - request:
82 | method: get
83 | uri: https://cdn.contentful.com/spaces/9utsm1g0t7f5/environments/staging/entries
84 | body:
85 | encoding: US-ASCII
86 | string: ''
87 | headers:
88 | X-Contentful-User-Agent:
89 | - sdk contentful.rb/2.6.0; platform ruby/2.5.1; os macOS/16;
90 | Authorization:
91 | - Bearer a67d4d9011f6d6c1dfe4169d838114d3d3849ab6df6fb1d322cf3ee91690fae4
92 | Content-Type:
93 | - application/vnd.contentful.delivery.v1+json
94 | Accept-Encoding:
95 | - gzip
96 | Connection:
97 | - close
98 | Host:
99 | - cdn.contentful.com
100 | User-Agent:
101 | - http.rb/2.2.2
102 | response:
103 | status:
104 | code: 200
105 | message: OK
106 | headers:
107 | Access-Control-Allow-Headers:
108 | - Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation,X-Contentful-User-Agent,X-Contentful-Enable-Alpha-Feature
109 | Access-Control-Allow-Methods:
110 | - GET,HEAD,OPTIONS
111 | Access-Control-Allow-Origin:
112 | - "*"
113 | Access-Control-Expose-Headers:
114 | - Etag
115 | Access-Control-Max-Age:
116 | - '86400'
117 | Cache-Control:
118 | - max-age=0
119 | Content-Type:
120 | - application/vnd.contentful.delivery.v1+json
121 | Etag:
122 | - '"a59f5ecde9dc32b551b923b6121939a2"'
123 | Server:
124 | - Contentful
125 | X-Content-Type-Options:
126 | - nosniff
127 | X-Contentful-Request-Id:
128 | - d5f7331306705cc373bdfeb7d8593878
129 | Content-Length:
130 | - '907'
131 | Accept-Ranges:
132 | - bytes
133 | Date:
134 | - Wed, 18 Apr 2018 11:39:54 GMT
135 | Via:
136 | - 1.1 varnish
137 | Age:
138 | - '0'
139 | Connection:
140 | - close
141 | X-Served-By:
142 | - cache-hhn1536-HHN
143 | X-Cache:
144 | - MISS
145 | X-Cache-Hits:
146 | - '0'
147 | X-Timer:
148 | - S1524051594.976805,VS0,VE305
149 | Vary:
150 | - Accept-Encoding
151 | body:
152 | encoding: ASCII-8BIT
153 | string: |
154 | {
155 | "sys": {
156 | "type": "Array"
157 | },
158 | "total": 1,
159 | "skip": 0,
160 | "limit": 100,
161 | "items": [
162 | {
163 | "sys": {
164 | "space": {
165 | "sys": {
166 | "type": "Link",
167 | "linkType": "Space",
168 | "id": "9utsm1g0t7f5"
169 | }
170 | },
171 | "id": "6yVdruR4GsKO2iKOqQS2CS",
172 | "type": "Entry",
173 | "createdAt": "2018-04-18T11:29:53.072Z",
174 | "updatedAt": "2018-04-18T11:29:53.072Z",
175 | "environment": {
176 | "sys": {
177 | "id": "staging",
178 | "type": "Link",
179 | "linkType": "Environment"
180 | }
181 | },
182 | "revision": 1,
183 | "contentType": {
184 | "sys": {
185 | "type": "Link",
186 | "linkType": "ContentType",
187 | "id": "foo"
188 | }
189 | },
190 | "locale": "en-US"
191 | },
192 | "fields": {
193 | "name": "Test",
194 | "content": "Some content"
195 | }
196 | }
197 | ]
198 | }
199 | http_version:
200 | recorded_at: Wed, 18 Apr 2018 11:39:54 GMT
201 | recorded_with: VCR 2.9.3
202 |
--------------------------------------------------------------------------------
/examples/templates/catalogue.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "contentTypes": [
4 | {
5 | "id": "brand",
6 | "name": "Brand",
7 | "displayField": "name",
8 | "fields": [
9 | {
10 | "id": "name",
11 | "name": "Company Name",
12 | "type": "Symbol"
13 | },
14 | {
15 | "id": "website",
16 | "name": "Website",
17 | "type": "Symbol"
18 | },
19 | {
20 | "id": "logo",
21 | "name": "Logo",
22 | "type": "Link",
23 | "linkType": "Asset"
24 | }
25 | ]
26 | },
27 | {
28 | "id": "category",
29 | "name": "Category",
30 | "displayField": "title",
31 | "fields": [
32 | {
33 | "id": "title",
34 | "name": "Title",
35 | "type": "Symbol"
36 | },
37 | {
38 | "id": "description",
39 | "name": "Description",
40 | "type": "Text"
41 | },
42 | {
43 | "id": "icon",
44 | "name": "Icon",
45 | "type": "Link",
46 | "linkType": "Asset"
47 | }
48 | ]
49 | },
50 | {
51 | "id": "product",
52 | "name": "Product",
53 | "displayField": "name",
54 | "fields": [
55 | {
56 | "id": "name",
57 | "name": "name",
58 | "type": "Symbol"
59 | },
60 | {
61 | "id": "description",
62 | "name": "Description",
63 | "type": "Text"
64 | },
65 | {
66 | "id": "image",
67 | "name": "Image",
68 | "type": "Link",
69 | "linkType": "Asset"
70 | },
71 | {
72 | "id": "brand",
73 | "name": "Brand",
74 | "type": "Link",
75 | "linkType": "Entry"
76 | },
77 | {
78 | "id": "category",
79 | "name": "Category",
80 | "type": "Link",
81 | "linkType": "Entry"
82 | },
83 | {
84 | "id": "url",
85 | "name": "Available at",
86 | "type": "Symbol"
87 | }
88 | ]
89 | }
90 | ],
91 | "assets": [
92 | {
93 | "id": "playsam_image",
94 | "title": "Playsam",
95 | "file": {
96 | "filename": "playsam_image",
97 | "url": "https://images.contentful.com/liicpxzmg1q0/4zj1ZOfHgQ8oqgaSKm4Qo2/3be82d54d45b5297e951aee9baf920da/playsam.jpg?h=250&"
98 | }
99 | },
100 | {
101 | "id": "normann_image",
102 | "title": "Normann",
103 | "file": {
104 | "filename": "normann_image",
105 | "url": "https://images.contentful.com/liicpxzmg1q0/3wtvPBbBjiMKqKKga8I2Cu/75c7c92f38f7953a741591d215ad61d4/zJYzDlGk.jpeg?h=250&"
106 | }
107 | },
108 | {
109 | "id": "toy_image",
110 | "title": "Toys",
111 | "file": {
112 | "filename": "toy_image",
113 | "url": "https://images.contentful.com/liicpxzmg1q0/6t4HKjytPi0mYgs240wkG/866ef53a11af9c6bf5f3808a8ce1aab2/toys_512pxGREY.png?h=250&"
114 | }
115 | },
116 | {
117 | "id": "kitchen_image",
118 | "title": "Kitchen and Home",
119 | "file": {
120 | "filename": "kitchen_image",
121 | "url": "https://images.contentful.com/liicpxzmg1q0/6m5AJ9vMPKc8OUoQeoCS4o/ffc20f5a8f2a71cca4801bc9c51b966a/1418244847_Streamline-18-256.png?h=250&"
122 | }
123 | },
124 | {
125 | "id": "toy_car",
126 | "title": "Playsam Toy Car",
127 | "file": {
128 | "filename": "toy_car",
129 | "url": "https://images.contentful.com/liicpxzmg1q0/wtrHxeu3zEoEce2MokCSi/acef70d12fe019228c4238aa791bdd48/quwowooybuqbl6ntboz3.jpg?h=250&"
130 | }
131 | },
132 | {
133 | "id": "whiskers",
134 | "title": "Normann Whisk Beaters",
135 | "file": {
136 | "filename": "whiskers",
137 | "url": "https://images.contentful.com/liicpxzmg1q0/10TkaLheGeQG6qQGqWYqUI/d510dde5e575d40288cf75b42383aa53/ryugj83mqwa1asojwtwb.jpg?h=250&"
138 | }
139 | }
140 | ],
141 | "entries": {
142 | "brand": [
143 | {
144 | "sys": {
145 | "id": "playsam"
146 | },
147 | "fields": {
148 | "name": "Playsam, Inc",
149 | "website": "http://www.playsam.com",
150 | "logo": {
151 | "linkType": "Asset",
152 | "id": "playsam_image"
153 | }
154 | }
155 | },
156 | {
157 | "sys": {
158 | "id": "normann"
159 | },
160 | "fields": {
161 | "name": "Normann Copenhagen, Inc",
162 | "website": "http://www.normann-copenhagen.com/",
163 | "logo": {
164 | "linkType": "Asset",
165 | "id": "normann_image"
166 | }
167 | }
168 | }
169 | ],
170 | "category": [
171 | {
172 | "sys": {
173 | "id": "toys"
174 | },
175 | "fields": {
176 | "title": "Toys",
177 | "description": "Toys for children",
178 | "icon": {
179 | "linkType": "Asset",
180 | "id": "toy_image"
181 | }
182 | }
183 | },
184 | {
185 | "sys": {
186 | "id": "kitchen"
187 | },
188 | "fields": {
189 | "title": "House and Kitchen",
190 | "description": "House and Kitchen accessories",
191 | "icon": {
192 | "linkType": "Asset",
193 | "id": "kitchen_image"
194 | }
195 | }
196 | }
197 | ],
198 | "product": [
199 | {
200 | "sys": {
201 | "id": "playsam_car"
202 | },
203 | "fields": {
204 | "name": "Playsam Streamliner Classic Car, Espresso",
205 | "description": "A classic Playsam design, the Streamliner Classic Car has been selected as Swedish Design Classic by the Swedish National Museum for its inventive style and sleek surface. It's no wonder that this wooden car has also been a long-standing favorite for children both big and small!",
206 | "image": {
207 | "linkType": "Asset",
208 | "id": "toy_car"
209 | },
210 | "brand": {
211 | "linkType": "Entry",
212 | "id": "playsam"
213 | },
214 | "category": {
215 | "linkType": "Entry",
216 | "id": "toys"
217 | },
218 | "url": "http://www.amazon.com/dp/B001R6JUZ2/"
219 | }
220 | },
221 | {
222 | "sys": {
223 | "id": "whisk_beater"
224 | },
225 | "fields": {
226 | "name": "Whisk Beater",
227 | "description": "A creative little whisk that comes in 8 different colors. Handy and easy to clean after use. A great gift idea.",
228 | "image": {
229 | "linkType": "Asset",
230 | "id": "whiskers"
231 | },
232 | "brand": {
233 | "linkType": "Entry",
234 | "id": "normann"
235 | },
236 | "category": {
237 | "linkType": "Entry",
238 | "id": "kitchen"
239 | },
240 | "url": "http://www.amazon.com/dp/B0081F2CCK/"
241 | }
242 | }
243 | ]
244 | }
245 | }
246 |
247 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/templates/base.rb:
--------------------------------------------------------------------------------
1 | require 'json'
2 | require 'contentful/management'
3 | require 'contentful/bootstrap/templates/links/base'
4 |
5 | module Contentful
6 | module Bootstrap
7 | module Templates
8 | class Base
9 | attr_reader :environment, :skip_content_types
10 |
11 | def initialize(space, environment_id = 'master', quiet = false, skip_content_types = false, no_publish = false)
12 | @environment = space.environments.find(environment_id)
13 | @quiet = quiet
14 | @skip_content_types = skip_content_types
15 | @no_publish = no_publish
16 | end
17 |
18 | def run
19 | create_content_types unless skip_content_types
20 | create_assets
21 | create_entries
22 |
23 | after_run
24 | rescue Contentful::Management::Error => e
25 | error = e.error
26 | output "Error at: #{error[:url]}"
27 | output "Message: #{error[:message]}"
28 | output "Details: #{error[:details]}"
29 |
30 | raise e
31 | end
32 |
33 | def content_types
34 | []
35 | end
36 |
37 | def entries
38 | {}
39 | end
40 |
41 | def assets
42 | []
43 | end
44 |
45 | def after_run; end
46 |
47 | protected
48 |
49 | def output(text = nil)
50 | Support.output(text, @quiet)
51 | end
52 |
53 | def create_file(name, url, properties = {})
54 | image = Contentful::Management::File.new
55 | image.properties[:contentType] = properties.fetch(:contentType, 'image/jpeg')
56 | image.properties[:fileName] = name.to_s
57 | image.properties[:upload] = url
58 | image
59 | end
60 |
61 | private
62 |
63 | def create_content_types
64 | content_types.each do |ct|
65 | begin
66 | output "Creating Content Type '#{ct['name']}'"
67 |
68 | fields = []
69 | ct['fields'].each do |f|
70 | field = Contentful::Management::Field.new
71 | field.id = f['id']
72 | field.name = f['name']
73 | field.type = f['type']
74 | field.link_type = f['linkType'] if link?(f)
75 |
76 | if array_field?(f)
77 | array_field = Contentful::Management::Field.new
78 | array_field.type = f['items']['type']
79 | array_field.link_type = f['items']['linkType']
80 | field.items = array_field
81 | end
82 |
83 | fields << field
84 | end
85 |
86 | content_type = environment.content_types.create(
87 | id: ct['id'],
88 | name: ct['name'],
89 | displayField: ct['displayField'],
90 | description: ct['description'],
91 | fields: fields
92 | )
93 |
94 | content_type.activate
95 | rescue Contentful::Management::Conflict
96 | output "ContentType '#{ct['id']}' already created! Skipping"
97 | next
98 | end
99 | end
100 | end
101 |
102 | def link?(field)
103 | field.key?('linkType')
104 | end
105 |
106 | def array_field?(field)
107 | field.key?('items')
108 | end
109 |
110 | def create_assets
111 | assets.each do |asset_json|
112 | begin
113 | output "Creating Asset '#{asset_json['title']}'"
114 | asset = environment.assets.create(
115 | id: asset_json['id'],
116 | title: asset_json['title'],
117 | file: asset_json['file']
118 | )
119 | asset.process_file
120 | rescue Contentful::Management::Conflict
121 | output "Asset '#{asset_json['id']}' already created! Updating instead."
122 |
123 | asset = environment.assets.find(asset_json['id']).tap do |a|
124 | a.title = asset_json['title']
125 | a.file = asset_json['file']
126 | end
127 |
128 | asset.save
129 | asset.process_file
130 | end
131 | end
132 |
133 | assets.each do |asset_json|
134 | attempts = 0
135 | while attempts < 10
136 | asset = environment.assets.find(asset_json['id'])
137 | unless asset.file.url.nil?
138 | asset.publish unless @no_publish
139 | break
140 | end
141 |
142 | sleep(1) # Wait for Process
143 | attempts += 1
144 | end
145 | end
146 | end
147 |
148 | def create_entries
149 | content_types = []
150 | processed_entries = entries.map do |content_type_id, entry_list|
151 | content_type = environment.content_types.find(content_type_id)
152 | content_types << content_type
153 |
154 | entry_list.each.map do |e|
155 | array_fields = []
156 | regular_fields = []
157 | e.each do |field_name, value|
158 | if value.is_a? ::Array
159 | array_fields << field_name
160 | next
161 | end
162 |
163 | regular_fields << field_name
164 | end
165 |
166 | array_fields.each do |af|
167 | e[af].map! do |item|
168 | if item.is_a? ::Contentful::Bootstrap::Templates::Links::Base
169 | item.to_management_object
170 | else
171 | item
172 | end
173 | end
174 | e[af.to_sym] = e.delete(af)
175 | end
176 |
177 | regular_fields.each do |rf|
178 | value = e.delete(rf)
179 | value = value.to_management_object if value.is_a? ::Contentful::Bootstrap::Templates::Links::Base
180 | e[rf.to_sym] = value
181 | end
182 |
183 | begin
184 | output "Creating Entry #{e[:id]}"
185 | entry = content_type.entries.create(id: e[:id])
186 | entry.save
187 |
188 | e = e.clone
189 | e[:id] = entry.id # in case no ID was specified in template
190 | rescue Contentful::Management::Conflict
191 | output "Entry '#{e[:id]}' already exists! Skipping"
192 | ensure
193 | next e
194 | end
195 | end
196 | end.flatten
197 |
198 | processed_entries = processed_entries.map do |e|
199 | output "Populating Entry #{e[:id]}"
200 |
201 | entry = environment.entries.find(e[:id])
202 | e.delete(:id)
203 | entry.update(e)
204 | entry.save
205 |
206 | 10.times do
207 | break if environment.entries.find(entry.id).sys[:version] >= 4
208 | sleep(0.5)
209 | end
210 |
211 | entry.id
212 | end
213 |
214 | processed_entries.each do |e|
215 | output "Publishing Entry #{e}"
216 | environment.entries.find(e).publish
217 | end unless @no_publish
218 | end
219 | end
220 | end
221 | end
222 | end
223 |
--------------------------------------------------------------------------------
/lib/contentful/bootstrap/templates/catalogue.rb:
--------------------------------------------------------------------------------
1 | require 'contentful/bootstrap/templates/base'
2 | require 'contentful/bootstrap/templates/links'
3 |
4 | module Contentful
5 | module Bootstrap
6 | module Templates
7 | class Catalogue < Base
8 | def content_types
9 | [
10 | {
11 | 'id' => 'brand',
12 | 'name' => 'Brand',
13 | 'displayField' => 'name',
14 | 'fields' => [
15 | {
16 | 'id' => 'name',
17 | 'name' => 'Company Name',
18 | 'type' => 'Symbol'
19 | },
20 | {
21 | 'id' => 'website',
22 | 'name' => 'Website',
23 | 'type' => 'Symbol'
24 | },
25 | {
26 | 'id' => 'logo',
27 | 'name' => 'Logo',
28 | 'type' => 'Link',
29 | 'linkType' => 'Asset'
30 | }
31 | ]
32 | },
33 | {
34 | 'id' => 'category',
35 | 'name' => 'Category',
36 | 'displayField' => 'title',
37 | 'fields' => [
38 | {
39 | 'id' => 'title',
40 | 'name' => 'Title',
41 | 'type' => 'Symbol'
42 | },
43 | {
44 | 'id' => 'description',
45 | 'name' => 'Description',
46 | 'type' => 'Text'
47 | },
48 | {
49 | 'id' => 'icon',
50 | 'name' => 'Icon',
51 | 'type' => 'Link',
52 | 'linkType' => 'Asset'
53 | }
54 | ]
55 | },
56 | {
57 | 'id' => 'product',
58 | 'name' => 'Product',
59 | 'displayField' => 'name',
60 | 'fields' => [
61 | {
62 | 'id' => 'name',
63 | 'name' => 'name',
64 | 'type' => 'Symbol'
65 | },
66 | {
67 | 'id' => 'description',
68 | 'name' => 'Description',
69 | 'type' => 'Text'
70 | },
71 | {
72 | 'id' => 'image',
73 | 'name' => 'Image',
74 | 'type' => 'Link',
75 | 'linkType' => 'Asset'
76 | },
77 | {
78 | 'id' => 'brand',
79 | 'name' => 'Brand',
80 | 'type' => 'Link',
81 | 'linkType' => 'Entry'
82 | },
83 | {
84 | 'id' => 'category',
85 | 'name' => 'Category',
86 | 'type' => 'Link',
87 | 'linkType' => 'Entry'
88 | },
89 | {
90 | 'id' => 'url',
91 | 'name' => 'Available at',
92 | 'type' => 'Symbol'
93 | }
94 | ]
95 | }
96 | ]
97 | end
98 |
99 | def assets
100 | [
101 | {
102 | 'id' => 'playsam_image',
103 | 'title' => 'Playsam',
104 | 'file' => create_file('playsam_image.jpg', 'https://images.contentful.com/liicpxzmg1q0/4zj1ZOfHgQ8oqgaSKm4Qo2/3be82d54d45b5297e951aee9baf920da/playsam.jpg?h=250&')
105 | },
106 | {
107 | 'id' => 'normann_image',
108 | 'title' => 'Normann',
109 | 'file' => create_file('normann_image.jpg', 'https://images.contentful.com/liicpxzmg1q0/3wtvPBbBjiMKqKKga8I2Cu/75c7c92f38f7953a741591d215ad61d4/zJYzDlGk.jpeg?h=250&')
110 | },
111 | {
112 | 'id' => 'toy_image',
113 | 'title' => 'Toys',
114 | 'file' => create_file('toy_image.jpg', 'https://images.contentful.com/liicpxzmg1q0/6t4HKjytPi0mYgs240wkG/866ef53a11af9c6bf5f3808a8ce1aab2/toys_512pxGREY.png?h=250&')
115 | },
116 | {
117 | 'id' => 'kitchen_image',
118 | 'title' => 'Kitchen and Home',
119 | 'file' => create_file('kitchen_image.jpg', 'https://images.contentful.com/liicpxzmg1q0/6m5AJ9vMPKc8OUoQeoCS4o/ffc20f5a8f2a71cca4801bc9c51b966a/1418244847_Streamline-18-256.png?h=250&')
120 | },
121 | {
122 | 'id' => 'toy_car',
123 | 'title' => 'Playsam Toy Car',
124 | 'file' => create_file('toy_car.jpg', 'https://images.contentful.com/liicpxzmg1q0/wtrHxeu3zEoEce2MokCSi/acef70d12fe019228c4238aa791bdd48/quwowooybuqbl6ntboz3.jpg?h=250&')
125 | },
126 | {
127 | 'id' => 'whiskers',
128 | 'title' => 'Normann Whisk Beaters',
129 | 'file' => create_file('whiskers.jpg', 'https://images.contentful.com/liicpxzmg1q0/10TkaLheGeQG6qQGqWYqUI/d510dde5e575d40288cf75b42383aa53/ryugj83mqwa1asojwtwb.jpg?h=250&')
130 | }
131 | ]
132 | end
133 |
134 | def entries
135 | {
136 | 'brand' => [
137 | {
138 | 'id' => 'playsam',
139 | 'name' => 'Playsam, Inc',
140 | 'website' => 'http://www.playsam.com',
141 | 'logo' => Links::Asset.new('playsam_image')
142 | },
143 | {
144 | 'id' => 'normann',
145 | 'name' => 'Normann Copenhagen, Inc',
146 | 'website' => 'http://www.normann-copenhagen.com/',
147 | 'logo' => Links::Asset.new('normann_image')
148 | }
149 | ],
150 | 'category' => [
151 | {
152 | 'id' => 'toys',
153 | 'title' => 'Toys',
154 | 'description' => 'Toys for children',
155 | 'icon' => Links::Asset.new('toy_image')
156 | },
157 | {
158 | 'id' => 'kitchen',
159 | 'title' => 'House and Kitchen',
160 | 'description' => 'House and Kitchen accessories',
161 | 'icon' => Links::Asset.new('kitchen_image')
162 | }
163 | ],
164 | 'product' => [
165 | {
166 | 'id' => 'playsam_car',
167 | 'name' => 'Playsam Streamliner Classic Car, Espresso',
168 | 'description' => 'A classic Playsam design, the Streamliner Classic Car has been selected as Swedish Design Classic by the Swedish National Museum for its inventive style and sleek surface. It\'s no wonder that this wooden car has also been a long-standing favorite for children both big and small!',
169 | 'image' => Links::Asset.new('toy_car'),
170 | 'brand' => Links::Entry.new('playsam'),
171 | 'category' => Links::Entry.new('toys'),
172 | 'url' => 'http://www.amazon.com/dp/B001R6JUZ2/'
173 | },
174 | {
175 | 'id' => 'whisk_beater',
176 | 'name' => 'Whisk Beater',
177 | 'description' => 'A creative little whisk that comes in 8 different colors. Handy and easy to clean after use. A great gift idea.',
178 | 'image' => Links::Asset.new('whiskers'),
179 | 'brand' => Links::Entry.new('normann'),
180 | 'category' => Links::Entry.new('kitchen'),
181 | 'url' => 'http://www.amazon.com/dp/B0081F2CCK/'
182 | }
183 | ]
184 | }
185 | end
186 | end
187 | end
188 | end
189 | end
190 |
--------------------------------------------------------------------------------
/spec/fixtures/vcr_fixtures/multiple_organizations.yml:
--------------------------------------------------------------------------------
1 | ---
2 | http_interactions:
3 | - request:
4 | method: post
5 | uri: https://api.contentful.com/spaces
6 | body:
7 | encoding: UTF-8
8 | string: '{"name":"foo","defaultLocale":"en-US"}'
9 | headers:
10 | X-Contentful-User-Agent:
11 | - sdk contentful-management.rb/1.10.0; integration bootstrap/3.7.0; platform
12 | ruby/2.4.1; os macOS/16;
13 | Authorization:
14 | - Bearer foo
15 | Content-Type:
16 | - application/vnd.contentful.management.v1+json
17 | Connection:
18 | - close
19 | Host:
20 | - api.contentful.com
21 | User-Agent:
22 | - http.rb/2.2.2
23 | response:
24 | status:
25 | code: 404
26 | message: Not Found
27 | headers:
28 | Accept-Ranges:
29 | - bytes
30 | Access-Control-Allow-Headers:
31 | - Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation,X-Contentful-User-Agent,X-Contentful-Enable-Experimental-Feature
32 | Access-Control-Allow-Methods:
33 | - DELETE,GET,HEAD,POST,PUT,OPTIONS
34 | Access-Control-Allow-Origin:
35 | - "*"
36 | Access-Control-Expose-Headers:
37 | - Etag
38 | Access-Control-Max-Age:
39 | - '1728000'
40 | Cache-Control:
41 | - max-age=0
42 | Content-Type:
43 | - application/vnd.contentful.management.v1+json
44 | Date:
45 | - Wed, 08 Nov 2017 09:43:18 GMT
46 | Server:
47 | - Contentful
48 | Set-Cookie:
49 | - _auth_new_session=ac54e1df2700ab38d711c6bed86b8598; path=/; expires=Wed, 22
50 | Nov 2017 09:43:18 -0000; secure; HttpOnly
51 | - incap_ses_408_673446=diigVgCKkncIdqEZxIKpBbbRAloAAAAAjLj7VcFHL5qxg9P593j6sQ==;
52 | path=/; Domain=.contentful.com
53 | - nlbi_673446=yMlRcftUokXK5lq96lKYhQAAAAC4qCDoH1n9W8NuANYcytI+; path=/; Domain=.contentful.com
54 | - visid_incap_673446=SkWRQ4PkT9abJpCX7ieh/rbRAloAAAAAQUIPAAAAAAA8KRGJif+0o9Hi2xP/5W8C;
55 | expires=Wed, 07 Nov 2018 21:13:21 GMT; path=/; Domain=.contentful.com
56 | Strict-Transport-Security:
57 | - max-age=15768000
58 | X-Content-Type-Options:
59 | - nosniff
60 | X-Contentful-Request-Id:
61 | - 5d3cb1a9281d9d3bb95293024d459af3
62 | X-Frame-Options:
63 | - ALLOWALL
64 | X-Xss-Protection:
65 | - 1; mode=block
66 | Content-Length:
67 | - '285'
68 | Connection:
69 | - Close
70 | X-Iinfo:
71 | - 3-3465080-3465084 NNNN CT(100 92 0) RT(1510134197914 31) q(0 0 2 -1) r(3 3)
72 | U5
73 | X-Cdn:
74 | - Incapsula
75 | body:
76 | encoding: ASCII-8BIT
77 | string: '{"requestId":"5d3cb1a9281d9d3bb95293024d459af3","message":"This User
78 | has multiple Organizations with permissions to create a Space. Please pass
79 | the X-Contentful-Organization header in which Organization to create the Space.","sys":{"type":"Error","id":"MissingOrganizationParameter"}}
80 |
81 | '
82 | http_version:
83 | recorded_at: Wed, 08 Nov 2017 09:43:18 GMT
84 | - request:
85 | method: get
86 | uri: https://api.contentful.com/organizations
87 | body:
88 | encoding: US-ASCII
89 | string: ''
90 | headers:
91 | X-Contentful-User-Agent:
92 | - sdk contentful-management.rb/1.10.0; integration bootstrap/3.7.0; platform
93 | ruby/2.4.1; os macOS/16;
94 | Authorization:
95 | - Bearer foo
96 | Content-Type:
97 | - application/vnd.contentful.management.v1+json
98 | Connection:
99 | - close
100 | Host:
101 | - api.contentful.com
102 | User-Agent:
103 | - http.rb/2.2.2
104 | response:
105 | status:
106 | code: 200
107 | message: OK
108 | headers:
109 | Accept-Ranges:
110 | - bytes
111 | Access-Control-Allow-Headers:
112 | - Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation,X-Contentful-User-Agent,X-Contentful-Enable-Experimental-Feature
113 | Access-Control-Allow-Methods:
114 | - DELETE,GET,HEAD,POST,PUT,OPTIONS
115 | Access-Control-Allow-Origin:
116 | - "*"
117 | Access-Control-Expose-Headers:
118 | - Etag
119 | Access-Control-Max-Age:
120 | - '1728000'
121 | Cache-Control:
122 | - max-age=0
123 | Content-Type:
124 | - application/vnd.contentful.management.v1+json
125 | Date:
126 | - Wed, 08 Nov 2017 09:43:19 GMT
127 | Etag:
128 | - W/"44c5cb3db8991a0968f988a1ba73470a"
129 | Server:
130 | - Contentful
131 | Strict-Transport-Security:
132 | - max-age=15768000
133 | X-Content-Type-Options:
134 | - nosniff
135 | X-Contentful-Request-Id:
136 | - fe4ada72e4e21d4ce196ea177cc538fb
137 | X-Frame-Options:
138 | - ALLOWALL
139 | X-Xss-Protection:
140 | - 1; mode=block
141 | Content-Length:
142 | - '1595'
143 | Connection:
144 | - Close
145 | Set-Cookie:
146 | - incap_ses_408_673446=+wAibagqxUBgdqEZxIKpBbbRAloAAAAANWD79znTuGxbfDBOJImupw==;
147 | path=/; Domain=.contentful.com
148 | - nlbi_673446=igoOWQaRGS189dtW6lKYhQAAAADk7QU5KcgD4rxu8Gv7kRku; path=/; Domain=.contentful.com
149 | - visid_incap_673446=4JhySUJERPCjGygLVEdeNLbRAloAAAAAQUIPAAAAAAA6ypi8lhIjQOEQge8NzJhr;
150 | expires=Wed, 07 Nov 2018 21:13:21 GMT; path=/; Domain=.contentful.com
151 | X-Iinfo:
152 | - 3-3465136-3465142 NNNN CT(95 91 0) RT(1510134198306 34) q(0 0 2 -1) r(3 3)
153 | U5
154 | X-Cdn:
155 | - Incapsula
156 | body:
157 | encoding: ASCII-8BIT
158 | string: '{"total":9,"limit":25,"skip":0,"sys":{"type":"Array"},"items":[
159 | {"name":"foo1","sys":{"type":"Organization","id":"foo1","version":55,"createdAt":"2013-05-06T07:53:34Z","updatedAt":"2017-06-30T11:57:20Z"}},
160 | {"name":"foo2","sys":{"type":"Organization","id":"foo2","version":22,"createdAt":"2015-01-07T16:26:56Z","updatedAt":"2016-06-28T08:58:37Z"}},
161 | {"name":"foo3","sys":{"type":"Organization","id":"foo3","version":14,"createdAt":"2015-09-17T23:13:27Z","updatedAt":"2016-06-28T08:58:39Z"}},
162 | {"name":"foo4","sys":{"type":"Organization","id":"foo4","version":5,"createdAt":"2015-12-15T13:09:13Z","updatedAt":"2017-11-01T12:12:05Z"}},
163 | {"name":"foo5","sys":{"type":"Organization","id":"foo5","version":0,"createdAt":"2016-03-11T18:00:48Z","updatedAt":"2016-03-11T18:00:48Z"}},
164 | {"name":"foo6","sys":{"type":"Organization","id":"foo6","version":1,"createdAt":"2017-01-09T09:20:00Z","updatedAt":"2017-01-09T10:21:02Z"}},
165 | {"name":"foo7","sys":{"type":"Organization","id":"foo7","version":0,"createdAt":"2017-04-19T08:59:40Z","updatedAt":"2017-04-19T08:59:40Z"}},
166 | {"name":"foo8","sys":{"type":"Organization","id":"foo8","version":1,"createdAt":"2017-08-08T12:53:09Z","updatedAt":"2017-08-28T11:33:05Z"}},
167 | {"name":"foo9","sys":{"type":"Organization","id":"foo9","version":0,"createdAt":"2017-11-01T12:11:30Z","updatedAt":"2017-11-01T12:11:30Z"}}]}'
168 | http_version:
169 | recorded_at: Wed, 08 Nov 2017 09:43:19 GMT
170 | recorded_with: VCR 2.9.3
171 |
--------------------------------------------------------------------------------
/spec/contentful/bootstrap/commands/update_space_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Contentful::Bootstrap::Commands::UpdateSpace do
4 | let(:path) { File.expand_path(File.join('spec', 'fixtures', 'ini_fixtures', 'contentfulrc.ini')) }
5 | let(:token) { Contentful::Bootstrap::Token.new path }
6 | subject { described_class.new token, 'foo', environment: 'master', json_template: 'bar', mark_processed: false, trigger_oauth: false, quiet: true }
7 | let(:space_double) { SpaceDouble.new }
8 |
9 | before do
10 | allow(::File).to receive(:write)
11 | end
12 |
13 | describe 'instance methods' do
14 | describe '#run' do
15 | it 'with all non nil attributes' do
16 | expect(subject).to receive(:fetch_space) { space_double }
17 | expect(subject).to receive(:update_json_template).with(space_double)
18 |
19 | expect(subject.run).to eq(space_double)
20 | end
21 |
22 | it 'exits if JSON not sent' do
23 | update_space_command = described_class.new(token, 'foo', mark_processed: false, quiet: true)
24 |
25 | expect { update_space_command.run }.to raise_error SystemExit
26 | end
27 |
28 | it 'exits if space is not found' do
29 | update_space_command = described_class.new(token, 'foo', json_template: 'bar', quiet: true)
30 |
31 | expect_any_instance_of(::Contentful::Management::ClientSpaceMethodsFactory).to receive(:find).with('foo').and_raise(::Contentful::Management::NotFound.new(ErrorRequestDouble.new))
32 |
33 | expect { update_space_command.run }.to raise_error SystemExit
34 | end
35 |
36 | it 'exits if JSON template is not an existing file' do
37 | expect(subject).to receive(:fetch_space) { space_double }
38 |
39 | expect { subject.run }.to raise_error SystemExit
40 | end
41 |
42 | describe 'runs JSON Template without already processed elements' do
43 | [true, false].each do |mark_processed|
44 | context "mark_processed is #{mark_processed}" do
45 | subject { described_class.new token, 'foo', environment: 'master', json_template: 'bar', mark_processed: mark_processed, trigger_oauth: false, quiet: true}
46 |
47 | it "calls JsonTemplate with mark_processed as #{mark_processed}" do
48 | allow(::File).to receive(:exist?) { true }
49 |
50 | mock_template = Object.new
51 |
52 | expect(subject).to receive(:fetch_space) { space_double }
53 | expect(mock_template).to receive(:run)
54 |
55 | expect(::Contentful::Bootstrap::Templates::JsonTemplate).to receive(:new).with(space_double, 'bar', 'master', mark_processed, true, true, false, false) { mock_template }
56 |
57 | subject.run
58 | end
59 | end
60 | end
61 | end
62 |
63 | context 'with skip_content_types set to true' do
64 | subject { described_class.new token, 'foo', json_template: 'bar', trigger_oauth: false, skip_content_types: true, quiet: true }
65 |
66 | it 'calls JsonTemplate with skip_content_types' do
67 | allow(::File).to receive(:exist?) { true }
68 |
69 | mock_template = Object.new
70 |
71 | expect(subject).to receive(:fetch_space) { space_double }
72 | expect(mock_template).to receive(:run)
73 |
74 | expect(::Contentful::Bootstrap::Templates::JsonTemplate).to receive(:new).with(space_double, 'bar', 'master', false, true, true, true, false) { mock_template }
75 |
76 | subject.run
77 | end
78 | end
79 |
80 | context 'with no_publish set to true' do
81 | subject { described_class.new token, 'foo', environment: 'master', json_template: 'bar', trigger_oauth: false, skip_content_types: true, quiet: true, no_publish: true }
82 |
83 | it 'calls JsonTemplate with no_publish' do
84 | allow(::File).to receive(:exist?) { true }
85 |
86 | mock_template = Object.new
87 |
88 | expect(subject).to receive(:fetch_space) { space_double }
89 | expect(mock_template).to receive(:run)
90 |
91 | expect(::Contentful::Bootstrap::Templates::JsonTemplate).to receive(:new).with(space_double, 'bar', 'master', false, true, true, true, true) { mock_template }
92 |
93 | subject.run
94 | end
95 | end
96 | end
97 | end
98 |
99 | describe 'attributes' do
100 | it ':json_template' do
101 | expect(subject.json_template).to eq 'bar'
102 | end
103 | end
104 |
105 | describe 'integration' do
106 | it 'can update localized spaces' do
107 | vcr('update_space_localized') {
108 | json_path = File.expand_path(File.join('spec', 'fixtures', 'json_fixtures', 'update_space_localized.json'))
109 | subject = described_class.new(token, 'vsy1ouf6jdcq', environment: 'master', locale: 'es-AR', json_template: json_path, mark_processed: false, trigger_oauth: false, quiet: true)
110 |
111 | subject.run
112 | }
113 |
114 | vcr('check_update_space_localized') {
115 | client = Contentful::Client.new(
116 | space: 'vsy1ouf6jdcq',
117 | access_token: '90e1b4964c3631cc9c751c42339814635623b001a53aec5aad23377299445433',
118 | dynamic_entries: :auto,
119 | raise_errors: true
120 | )
121 |
122 | entries = client.entries(locale: 'es-AR')
123 |
124 | expect(entries.map(&:text)).to eq ['Foo', 'Bar']
125 | }
126 | end
127 |
128 | it 'can update an existing asset and keep it as draft' do
129 | vcr('update_existing_asset') {
130 | json_path = File.expand_path(File.join('spec', 'fixtures', 'json_fixtures', 'assets_draft.json'))
131 | subject = described_class.new(token, 'f3abi4dqvrhg', environment: 'master', json_template: json_path, no_publish: true, trigger_oauth: false, quiet: true)
132 |
133 | subject.run
134 | }
135 |
136 | vcr('check_update_space_with_draft_content') {
137 | delivery_client = Contentful::Client.new(
138 | space: 'f3abi4dqvrhg',
139 | access_token: 'efab52abe735b200abb0f053ad8a3d0da633487c0c98cf03dc806c2b3bd049a1',
140 | dynamic_entries: :auto,
141 | raise_errors: true
142 | )
143 |
144 | preview_client = Contentful::Client.new(
145 | space: 'f3abi4dqvrhg',
146 | access_token: '06c28ef41823bb636714dfd812066fa026a49e95041a0e94903d6cf016bba50e',
147 | dynamic_entries: :auto,
148 | api_url: 'preview.contentful.com',
149 | raise_errors: true
150 | )
151 |
152 | delivery_cat = delivery_client.assets.first
153 | preview_cat = preview_client.assets.first
154 |
155 | expect(preview_cat.title).not_to eq delivery_cat
156 | expect(preview_cat.title).to eq 'Cat'
157 | expect(delivery_cat.title).to eq 'Foo'
158 | }
159 | end
160 |
161 | it 'can update a specific environment different than master' do
162 | delivery_client = nil
163 | vcr('check_original_staging_environment_status') {
164 | delivery_client = Contentful::Client.new(
165 | space: '9utsm1g0t7f5',
166 | access_token: 'a67d4d9011f6d6c1dfe4169d838114d3d3849ab6df6fb1d322cf3ee91690fae4',
167 | environment: 'staging',
168 | dynamic_entries: :auto,
169 | raise_errors: true
170 | )
171 |
172 | expect(delivery_client.entries.size).to eq 1
173 | }
174 |
175 | vcr('update_with_environment') {
176 | json_path = File.expand_path(File.join('spec', 'fixtures', 'json_fixtures', 'environment_template.json'))
177 | subject = described_class.new(token, '9utsm1g0t7f5', environment: 'staging', json_template: json_path, trigger_oauth: false, quiet: true)
178 |
179 | subject.run
180 | }
181 |
182 | vcr('check_staging_environment_status') {
183 | entries = delivery_client.entries
184 | expect(entries.size).to eq 2
185 | expect(entries.items.detect { |i| i.id == 'foo_update' }.name).to eq 'Test updated'
186 | }
187 | end
188 | end
189 | end
190 |
--------------------------------------------------------------------------------
/spec/contentful/bootstrap/commands/create_space_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Contentful::Bootstrap::Commands::CreateSpace do
4 | let(:path) { File.expand_path(File.join('spec', 'fixtures', 'ini_fixtures', 'contentfulrc.ini')) }
5 | let(:token) { Contentful::Bootstrap::Token.new path }
6 | subject { described_class.new token, 'foo', template: 'bar', json_template: 'baz', trigger_oauth: false, quiet: true }
7 | let(:space_double) { SpaceDouble.new }
8 |
9 | before do
10 | allow(::File).to receive(:write)
11 | end
12 |
13 | describe 'instance methods' do
14 | describe '#run' do
15 | it 'with all non nil attributes' do
16 | expect(subject).to receive(:fetch_space) { space_double }
17 | expect(subject).to receive(:create_template).with(space_double)
18 | expect(subject).to receive(:create_json_template).with(space_double)
19 | expect(subject).to receive(:generate_token).with(space_double)
20 |
21 | subject.run
22 | end
23 |
24 | it 'does not create template when template_name is nil' do
25 | create_space_command = described_class.new(token, 'foo', json_template: 'baz', trigger_oauth: false, quiet: true)
26 |
27 | expect(create_space_command.template_name).to eq nil
28 |
29 | expect(create_space_command).to receive(:fetch_space) { space_double }
30 | expect(create_space_command).not_to receive(:create_template).with(space_double)
31 | expect(create_space_command).to receive(:create_json_template).with(space_double)
32 | expect(create_space_command).to receive(:generate_token).with(space_double)
33 |
34 | create_space_command.run
35 | end
36 |
37 | it 'does not create json template when json_template is nil' do
38 | create_space_command = described_class.new(token, 'foo', template: 'bar', trigger_oauth: false, quiet: true)
39 |
40 | expect(create_space_command.json_template).to eq nil
41 |
42 | expect(create_space_command).to receive(:fetch_space) { space_double }
43 | expect(create_space_command).to receive(:create_template).with(space_double)
44 | expect(create_space_command).not_to receive(:create_json_template).with(space_double)
45 | expect(create_space_command).to receive(:generate_token).with(space_double)
46 |
47 | create_space_command.run
48 | end
49 | end
50 | end
51 |
52 | describe 'attributes' do
53 | it ':template_name' do
54 | expect(subject.template_name).to eq 'bar'
55 | end
56 |
57 | it ':json_template' do
58 | expect(subject.json_template).to eq 'baz'
59 | end
60 | end
61 |
62 | describe 'localization' do
63 | it 'can create a space with a different locale' do
64 | allow(Contentful::Bootstrap::Support).to receive(:gets).and_return('y')
65 | allow(Contentful::Bootstrap::Support).to receive(:gets).and_return('n')
66 |
67 | command = described_class.new(token, 'B.rb - locale creation', locale: 'es-AR')
68 |
69 | vcr('space_with_different_locale') {
70 | command.run
71 | }
72 |
73 | vcr('check_created_space') {
74 | client = ::Contentful::Client.new(
75 | space: 'vsy1ouf6jdcq',
76 | access_token: '90e1b4964c3631cc9c751c42339814635623b001a53aec5aad23377299445433',
77 | dynamic_entries: :auto,
78 | raise_errors: true
79 | )
80 |
81 | space = client.space
82 |
83 | expect(space.name).to eq("B.rb - locale creation")
84 | expect(space.locales.first.code).to eq "es-AR"
85 | expect(space.locales.first.default).to be_truthy
86 | }
87 | end
88 | end
89 |
90 | describe 'issues' do
91 | it 'Importing asset array values does not work #22' do
92 | json_path = File.expand_path(File.join('spec', 'fixtures', 'json_fixtures', 'issue_22.json'))
93 |
94 | allow(Contentful::Bootstrap::Support).to receive(:gets).and_return('y')
95 | allow(Contentful::Bootstrap::Support).to receive(:gets).and_return('n')
96 |
97 | command = described_class.new(token, 'issue_22', json_template: json_path, quiet: true)
98 |
99 | vcr('issue_22') {
100 | command.run
101 | }
102 | end
103 |
104 | it 'assets can be created with any content type #39' do
105 | json_path = File.expand_path(File.join('spec', 'fixtures', 'json_fixtures', 'asset_no_transform.json'))
106 |
107 | allow(Contentful::Bootstrap::Support).to receive(:gets).and_return('y')
108 | allow(Contentful::Bootstrap::Support).to receive(:gets).and_return('n')
109 |
110 | command = described_class.new(token, 'asset_no_transform', json_template: json_path, mark_processed: false, quiet: true)
111 |
112 | vcr('asset_no_transform') {
113 | command.run
114 | }
115 | end
116 |
117 | it 'doesnt fail on multiple organizations #54' do
118 | vcr('multiple_organizations') {
119 | path = File.expand_path(File.join('spec', 'fixtures', 'ini_fixtures', 'no_org.ini'))
120 | token = Contentful::Bootstrap::Token.new path
121 |
122 | allow(Contentful::Bootstrap::Support).to receive(:gets).and_return('y')
123 | allow(Contentful::Bootstrap::Support).to receive(:gets).and_return('n')
124 | subject = described_class.new token, 'foo', quiet: true
125 |
126 | expect(subject).to receive(:generate_token).with(space_double)
127 | expect(Contentful::Bootstrap::Support).to receive(:gets) { 'foobar' }
128 | expect(token).to receive(:write_organization_id).with('foobar')
129 | expect(subject.client).to receive(:spaces).and_call_original
130 | space_proxy_double = Object.new
131 | expect(subject.client).to receive(:spaces) { space_proxy_double }
132 | expect(space_proxy_double).to receive(:create).with(name: 'foo', defaultLocale: 'en-US', organization_id: 'foobar') { space_double }
133 |
134 | subject.run
135 | }
136 | end
137 |
138 | context 'with no_publish set to true' do
139 | subject { described_class.new token, 'foo', environment: 'master', json_template: 'bar', trigger_oauth: false, quiet: true, no_publish: true }
140 |
141 | it 'calls JsonTemplate with no_publish' do
142 | allow(Contentful::Bootstrap::Support).to receive(:gets).and_return('y')
143 | allow(Contentful::Bootstrap::Support).to receive(:gets).and_return('n')
144 | allow(::File).to receive(:exist?) { true }
145 |
146 | mock_template = Object.new
147 |
148 | expect(subject).to receive(:fetch_space) { space_double }
149 | expect(mock_template).to receive(:run)
150 |
151 | expect(::Contentful::Bootstrap::Templates::JsonTemplate).to receive(:new).with(space_double, 'bar', 'master', false, true, true, false, true) { mock_template }
152 |
153 | subject.run
154 | end
155 | end
156 | end
157 |
158 | describe 'integration' do
159 | before do
160 | allow(Contentful::Bootstrap::Support).to receive(:gets).and_return('y')
161 | allow(Contentful::Bootstrap::Support).to receive(:gets).and_return('n')
162 | end
163 |
164 | it 'create space' do
165 | command = described_class.new token, 'some_space', quiet: true
166 |
167 | vcr('create_space') {
168 | command.run
169 | }
170 | end
171 |
172 | it 'create space with blog template' do
173 | command = described_class.new token, 'blog_space', template: 'blog', quiet: true
174 |
175 | vcr('create_space_with_blog_template') {
176 | command.run
177 | }
178 | end
179 |
180 | it 'create space with gallery template' do
181 | command = described_class.new token, 'gallery_space', template: 'gallery', quiet: true
182 |
183 | vcr('create_space_with_gallery_template') {
184 | command.run
185 | }
186 | end
187 |
188 | it 'create space with catalogue template' do
189 | command = described_class.new token, 'catalogue_space', template: 'catalogue', quiet: true
190 |
191 | vcr('create_space_with_catalogue_template') {
192 | command.run
193 | }
194 | end
195 |
196 | it 'create space with json template' do
197 | skip 'covered by create_space_spec:issues/#22'
198 | end
199 |
200 | it 'create space with json template with no ids' do
201 | json_path = File.expand_path(File.join('spec', 'fixtures', 'json_fixtures', 'no_ids.json'))
202 | command = described_class.new token, 'no_ids_space', json_template: json_path, quiet: true
203 |
204 | vcr('no_ids') {
205 | command.run
206 | }
207 | end
208 | end
209 | end
210 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ## Unreleased
4 | ## v3.12.0
5 | ### Added
6 | * Added logging for publishing actions. [#71](https://github.com/contentful/contentful-bootstrap.rb/pull/71)
7 |
8 | ## v3.11.1
9 | ### Fixed
10 | * Fixed array importing. [#70](https://github.com/contentful/contentful-bootstrap.rb/pull/70)
11 |
12 | ## v3.11.0
13 | ### Added
14 | * Added `--environment` support to `update_space` and `generate_json`.
15 |
16 | ### Changed
17 | * Updated to the new CMA and CDA SDK versions.
18 |
19 | ## v3.10.0
20 | ### Added
21 | * Added `--content-type-ids` filter to `generate_json` command to allow selecting which content types to import.
22 |
23 | ## v3.9.1
24 | ### Fixed
25 | * Fixed an issue in which assets save as `Contentful::Management::File` objects instead of as JSON when using `--mark-processed`.
26 |
27 | ## v3.9.0
28 | ### Fixed
29 | * Fixed `quiet` not being forwarded properly to `generate_json` and `update_space`
30 | * Fixed `X-Contentful-User-Agent` headers now use `application` instead of `integration` header.
31 |
32 | ### Added
33 | * Added `--use-preview` flag to `generate_json` command [#62](https://github.com/contentful/contentful-bootstrap.rb/issues/62)
34 | * Added `--no-publish` flag to `create_space` and `update_space` commands [#62](https://github.com/contentful/contentful-bootstrap.rb/issues/62)
35 |
36 | ### Changed
37 | * `generate_json` now imports all available content in your space [#62](https://github.com/contentful/contentful-bootstrap.rb/issues/62)
38 | * Assets can now be updated when using `update_space`.
39 |
40 | ## v3.8.0
41 | ### Changed
42 | * Changed `Contentful::Bootstrap::CreateCommand#organizations` to use the new public `/organizations` endpoint.
43 |
44 | ## v3.7.0
45 | ### Fixed
46 | * Fixed `skip_content_types` option on `update_space` [#59](https://github.com/contentful/contentful-bootstrap.rb/pull/59)
47 |
48 | ### Added
49 | * Added `--locale` option to `create_space` and `update_space` in order to create Spaces on a locale different to `en-US`
50 |
51 | ## v3.6.1
52 |
53 | ### Changed
54 | * Changed User Agent Headers to use the new format
55 | * Updated versions of CMA and CDA SDK to latest available
56 |
57 | ## v3.5.2
58 |
59 | ### Fixed
60 | * Fixed compatibility with CDA 2.0.0 SDK
61 |
62 | ## v3.5.1
63 | ### Fixed
64 | * Fixed organization fetching when no organization ID is provided on the configuration file [#54](https://github.com/contentful/contentful-bootstrap.rb/issues/54)
65 |
66 | ## v3.5.0
67 |
68 | ### Added
69 | * Add `-q` and `--quiet` flags to the CLI Tool and their respective command classes [#48](https://github.com/contentful/contentful-bootstrap.rb/issues/48)
70 | * Add `:no_input` option to library commands [#48](https://github.com/contentful/contentful-bootstrap.rb/issues/48)
71 |
72 | ### Changed
73 | * Refactored internals to allow more option flexibility and simplified the `CommandRunner`.
74 | * Updated dependencies to the newest available SDKs
75 |
76 | ### Fixed
77 | * Fixed Object Field parsing for JSON Templates [#51](https://github.com/contentful/contentful-bootstrap.rb/issues/51)
78 |
79 |
80 | ## v3.4.0
81 | ### Added
82 | * Add `-v` and `--version` flags to output current version
83 | * Add possibility to define `CONTENTFUL_ORGANIZATION_ID` in your `.contentfulrc` file [#44](https://github.com/contentful/contentful-bootstrap.rb/issues/44)
84 |
85 | ## v3.3.0
86 | ### Added
87 | * Adds possibility to change `contentType` for Assets [#39](https://github.com/contentful/contentful-bootstrap.rb/issues/39)
88 | * Adds `--content-types-only` option to `generate_json`
89 | * Adds `--skip-content-types` option to `update_space`
90 |
91 | ### Fixed
92 | * Fixes a bug in built-in templates using obsolete `link_type` properties
93 |
94 | ## v3.2.0
95 | ### Added
96 | * Adds `update_space` command to update already existing spaces using JSON Templates
97 | * Adds `--mark-processed` option to `create_space` and `update_space` to enforce marking which resources have already been processed
98 |
99 | ## v3.1.1
100 | ### Fixed
101 | * Fixes a bug where `display_field` was not properly populated when being generated from JSON templates [#35](https://github.com/contentful/contentful-bootstrap.rb/issues/35)
102 |
103 | ## v3.1.0
104 | ### Fixed
105 | * Version Locked Webmock causing all our VCRs to fail
106 | * Version Locked Contentful Management SDK
107 |
108 | ### Added
109 | * Custom User-Agent header
110 |
111 | ## v3.0.0
112 | ### Changed
113 | * Change JSON Template format to resemble the API more closely
114 | * Create Entries on 2 Steps [#27](https://github.com/contentful-labs/contentful-bootstrap.rb/pull/27)
115 | * Add JSON Template Version Check [#31](https://github.com/contentful-labs/contentful-bootstrap.rb/issues/31)
116 |
117 | ## v2.0.2
118 | ### Fixed
119 | * Array and Link handling
120 |
121 | ### Changed
122 | * Command now provide better help on incomplete command
123 |
124 | ## v2.0.1 [YANKED]
125 | ### Changed
126 | * Scoped File Usage on GenerateJson Command
127 |
128 | ## v2.0.0 [YANKED]
129 | ### Changed
130 | * Refactored `Commands` into new classes
131 | * Renamed `Commands` to `CommandRunner`, kept external interface, moved internal logic to new `Commands` classes
132 | * Refactored `Token` to be an Object instead of a collection of static behavior
133 | * General code refactoring and cleanup
134 |
135 | ### Added
136 | * More robust mechanism for waiting on processed assets
137 | * JSON Template generator `generate_json` command
138 | * Added Specs for almost all the code
139 | * Applied Rubocop Style guide
140 |
141 | ## v1.6.0
142 | ### Added
143 | * Support for Symbols in Array fields
144 | * Support for Links of other Entries that are not yet saved
145 |
146 | ## v1.5.1
147 | ### Fixed
148 | * Array fields were getting overwritten with `nil`
149 |
150 | ## v1.5.0
151 | ### Added
152 | * Multiple Organization ID fetch
153 |
154 | ## v1.4.0
155 | ### Added
156 | * Contentful Space URL after creation
157 | * Support for Array fields in JSON Templates
158 |
159 | ## v1.3.2
160 | ### Added
161 | * Error out when no STDIN is detected
162 |
163 | ## v1.3.1
164 | ### Fixed
165 | * Fix help messages. [#5](https://github.com/contentful-labs/contentful-bootstrap.rb/issues/5)
166 |
167 | ## v1.3.0
168 | ### Added
169 | * Spaces now get their own section in `~/.contentfulrc`
170 |
171 | ### Changed
172 | * Delivery API Tokens now saved per Space
173 |
174 | ## v1.2.0
175 | ### Added
176 | * JSON Template Parser
177 | * `catalogue.json` Template Example
178 |
179 | ### Changed
180 | * Changed existing templates from using `Symbol` entries as keys to `String`
181 | * Changed command optional parameters to `Hash` to allow better flexibility in commands
182 |
183 | ## v1.1.0
184 | ### Removed
185 | * Removed `init` command as `v1.0.0` refactor removed it's necessity
186 |
187 | ## v1.0.1 [YANKED]
188 | ### Fixed
189 | * Release is now on `master`
190 |
191 | ## v1.0.0 [YANKED]
192 | ### Changed
193 | * Changed namespace from `ContentfulBootstrap` to `Contentful::Bootstrap` to mimic other libraries
194 | * Changed repository name from `contentful_bootstrap.rb` to `contentful-bootstrap.rb` to mimic other libraries
195 | * Delivery API Token will always get created when using `contentful_bootstrap` commands to create a space
196 | * Configuration now read from `~/.contentfulrc`
197 | * Tool now requests user to allow to write configuration files
198 |
199 | ### Added
200 | * `contentful-bootstrap.rb` version number to API Token description
201 | * `inifile` as runtime dependency
202 | * Add optional `--config CONFIG_PATH` parameter to commands
203 |
204 | ## v0.0.7
205 | ### Fixed
206 | * Redirected Favicon fetch to Contentful's favicon, as some browsers would ping the server indefinitely
207 |
208 | ## v0.0.6
209 | ### Fixed
210 | * Token was not being returned on `generate_token` call
211 |
212 | ### Added
213 | * `catalogue` template now available
214 | * Added small sleep window between asset processing and publishing
215 |
216 |
217 | ## v0.0.5
218 | ### Added
219 | * API Token Generation
220 | * `generate_token` command added to binary
221 | * Better Formatting of current steps
222 | * Space and Access Token Values displayed at end of command
223 |
224 | ## v0.0.4
225 | ### Added
226 | * Added support for users with multiple Organizations
227 | * Added `gallery` template
228 |
229 | ### Changed
230 | * Removed `deklarativna` dependency
231 | * Removed `sinatra` dependency
232 | * Removed unnecessary `Gemfile.lock` file
233 |
234 | ## v0.0.3
235 | ### Added
236 | * Added `contentful_bootstrap` command
237 | * Added `blog` template
238 |
239 | ### Fixed
240 | * Fixed Dependencies
241 |
242 | ## v0.0.2 [YANKED]
243 |
244 | ## v0.0.1 [YANKED]
245 |
--------------------------------------------------------------------------------