Coder Social home page Coder Social logo

contentful / contentful-management.rb Goto Github PK

View Code? Open in Web Editor NEW
33.0 32.0 33.0 1.3 MB

Ruby client for the Contentful Content Management API

Home Page: https://www.contentful.com/developers/documentation/content-management-api/

License: MIT License

Ruby 100.00%

contentful-management.rb's Introduction

Contentful::Management

Gem Version CircleCI

Ruby client for the Contentful Content Management API.

Contentful provides a content infrastructure for digital teams to power content in websites, apps, and devices. Unlike a CMS, Contentful was built to integrate with the modern software stack. It offers a central hub for structured content, powerful management and delivery APIs, and a customizable web app that enable developers and content creators to ship digital products faster.

Setup

Add this line to your application's Gemfile:

gem 'contentful-management'

Usage

Examples

Some examples can be found in the examples/ directory or you take a look at this extended example script.

Client

At the beginning the API client instance should be created for each thread that is going to be used in your application:

require 'contentful/management'

client = Contentful::Management::Client.new('access_token')

The access token can easily be created through the management api documentation.

Spaces

Retrieving all spaces:

spaces = client.spaces.all

Retrieving one space by ID:

blog_space = client.spaces.find('blog_space_id')

Destroying a space:

blog_space.destroy

Creating a space:

blog_space = client.spaces.new
blog_space.name = 'Blog Space'
blog_space.save

or

blog_space = client.spaces.create(name: 'Blog Space')

or in the context of the organization (if you have multiple organizations within your account):

blog_space = client.spaces.create(name: 'Blog Space', organization_id: 'organization_id')

If you want to create a default locale different from en-US:

blog_space = client.spaces.create(name: 'Blog Space', default_locale: 'de-DE')

Updating a space:

blog_space.update(name: 'New Blog Space')

or

blog_space.name = 'New Blog Space'
blog_space.save

Environments

Retrieving all environments:

environments = client.environments('space_id').all

Or if you already have a fetched space:

environments = space.environments.all

Retrieving one environment by ID:

environment = client.environments('space_id').find('master')

Destroying a environment:

environment.destroy

Creating an environment:

environment = client.environments('space_id').new
environment.id = 'dev'
environment.name = 'Development'
environment.save

or

environment = client.environments(space_id).create(id: 'dev', name: 'Development')

Creating an evironment with a different source:

environment = client.environments(space_id).create(id: 'dev', name: 'Development', source_environment_id: 'other_environment')

Updating a environment:

environment.update(name: 'New Blog environment')

or

environment.name = 'Dev'
environment.save

Assets

Retrieving all assets from the environment:

blog_post_assets = environment.assets.all

Retrieving an asset by id:

blog_post_asset = environment.assets.find('asset_id')

Creating a file:

image_file = Contentful::Management::File.new
image_file.properties[:contentType] = 'image/jpeg'
image_file.properties[:fileName] = 'example.jpg'
image_file.properties[:upload] = 'http://www.example.com/example.jpg'

Creating an asset:

my_image_asset = environment.assets.create(title: 'My Image', description: 'My Image Description', file: image_file)

or an asset with multiple locales

my_image_localized_asset = environment.assets.new
my_image_localized_asset.title_with_locales= {'en-US' => 'title', 'pl' => 'pl title'}
my_image_localized_asset.description_with_locales= {'en-US' => 'description', 'pl' => 'pl description'}
en_file = Contentful::Management::File.new
en_file.properties[:contentType] = 'image/jpeg'
en_file.properties[:fileName] = 'pic1.jpg'
en_file.properties[:upload] = 'http://www.example.com/pic1.jpg'
pl_file = Contentful::Management::File.new
pl_file.properties[:contentType] = 'image/jpeg'
pl_file.properties[:fileName] = 'pic2.jpg'
pl_file.properties[:upload] = 'http://www.example.com/pic2.jpg'
asset.file_with_locales= {'en-US' => en_file, 'pl' => pl_file}
asset.save

Process an asset file after create:

asset.process_file

Updating an asset:

  • default locale
my_image_asset.update(title: 'My Image', description: 'My Image Description', file: image_file)
  • another locale (we can switch locales for the object, so then all fields are in context of selected locale)
my_image_asset.locale = 'nl'
my_image_asset.update(title: 'NL Title', description: 'NL Description', file: nl_image)
  • field with multiple locales
my_image_asset.title_with_locales = {'en-US' => 'US Title', 'nl' => 'NL Title'}
my_image_asset.save

Destroying an asset:

my_image_asset.destroy

Archiving or unarchiving an asset:

my_image_asset.archive
my_image_asset.unarchive

Checking if an asset is archived:

my_image_asset.archived?

Publishing or unpublishing an asset:

my_image_asset.publish
my_image_asset.unpublish

Checking if an asset is published:

my_image_asset.published?

Checking if has been updated from last published version:

my_image_asset.updated?

File Uploads

Creating an upload from a file path:

upload = client.uploads('space_id').create('/path/to/file.md')

Alternatively, create it from an ::IO object:

File.open('/path/to/file.md', 'rb') do |file|
  upload = client.uploads('space_id').create(file)
end

Finding an upload:

upload = client.uploads('space_id').find('upload_id')

Deleting an upload:

upload.destroy

Associating an upload with an asset:

# We find or create an upload:
upload = client.uploads('space_id').find('upload_id')

# We create a File object with the associated upload:
file = Contentful::Management::File.new
file.properties[:contentType] = 'text/plain'
file.properties[:fileName] = 'file.md'
file.properties[:uploadFrom] = upload.to_link_json  # We create the Link from the upload.

# We create an asset with the associated file:
asset = client.assets('space_id', 'environment_id').create(title: 'My Upload', file: file)
asset.process_file  # We process the file, to generate an URL for our upload.

Entries

Retrieving all entries from the environment:

entries = environment.entries.all

Retrieving all entries from the environment with given content type:

entries = environment.entries.all(content_type: content_type.id)

or

entries = content_type.entries.all

Retrieving an entry by ID:

entry = environment.entries.find('entry_id')

Retrieving entries by any other field value:

entries = environment.entries.all(content_type: content_type.id, 'fields.fieldName' => 'value')

Note: all search parameters are supported.

Creating a location:

location = Location.new
location.lat = 22.44
location.lon = 33.33

Creating an entry:

  • with a default locale
my_entry = blog_post_content_type.entries.create(post_title: 'Title', assets_array_field: [image_asset_1, ...], entries_array_field: [entry_1, ...], location_field: location)
  • with multiple locales
my_entry = blog_post_content_type.entries.new
my_entry.post_title_with_locales = {'en-US' => 'EN Title', 'pl' => 'PL Title'}
my_entry.save

Updating an entry:

  • with a default locale
my_entry.update(params)
  • with another locale
entry.locale = 'nl'
entry.update(params)
  • with multiple locales
my_entry.post_title_with_locales = {'en-US' => 'EN Title', 'pl' => 'PL Title'}
my_entry.save

Destroying an entry:

my_entry.destroy

Archiving or unarchiving the entry:

my_entry.archive
my_entry.unarchive

Checking if the entry is archived:

my_entry.archived?

Publishing or unpublishing the entry:

my_entry.publish
my_entry.unpublish

Checking if the entry is published:

my_entry.published?

Checking if the entry has been updated from last publish:

my_entry.updated?

Entries created with empty fields, will not return those fields in the response. Therefore, entries that don't have cache enabled, will need to make an extra request to fetch the content type and fill the missing fields. To allow for content type caching:

  • Enable content type cache at client instantiation time
  • Query entries through environment.entries.find instead of Entry.find(environment_id, entry_id)

Content Types

Retrieving all content types from a environment:

blog_post_content_types = environment.content_types.all

Retrieving all published content types from a environment:

blog_post_content_types = environment.content_types.all_published

Retrieving one content type by ID from a environment:

blog_post_content_type = environment.content_types.find(id)

Creating a field for a content type:

title_field = Contentful::Management::Field.new
title_field.id = 'blog_post_title'
title_field.name = 'Post Title'
title_field.type = 'Text'
blog_post_content_type.fields.add(field)

or

blog_post_content_type.fields.create(id: 'title_field_id', name: 'Post Title', type: 'Text')
  • if the field_id exists, the related field will be updated.

or the field of link type:

blog_post_content_type.fields.create(id: 'my_entry_link_field', name: 'My Entry Link Field', type: 'Link', link_type: 'Entry')

or the field of ResourceLink type:

blog_post_content_type.fields.create(
  id: 'my_resource_link_id',
  name: 'My Resource Link',
  type: 'ResourceLink',
  localized: true,
  disabled: false,
  omitted: false,
  allowed_resources: [
    {
      type: 'Contentful:Entry',
      source: 'crn:contentful:::content:spaces/space_id',
      contentTypes: ["foo", "bar"]
    }
  ]
)

or the field of an array type:

items = Contentful::Management::Field.new
items.type = 'Link'
items.link_type = 'Entry'
blog_post_content_type.fields.create(id: 'my_array_field', name: 'My Array Field', type: 'Array', items: items)

Deleting a field from the content type:

blog_post_content_type.fields.destroy(title_field_id)

Creating a content type:

environment.content_types.create(name: 'Post', fields: [title_field, body_field])

or

blog_post_content_type = environment.content_types.new
blog_post_content_type.name = 'Post'
blog_post_content_type.fields = [title_field, body_field]
blog_post_content_type.save

Destroying a content type:

blog_post_content_type.destroy

Activating or deactivating a content type:

blog_post_content_type.activate
blog_post_content_type.deactivate

Checking if a content type is active:

blog_post_content_type.active?

Updating a content type:

blog_post_content_type.update(name: 'Post', description: 'Post Description', fields: [title_field])

Validations

in

Takes an array of values and validates that the field value is in this array.

validation_in = Contentful::Management::Validation.new
validation_in.in = ['foo', 'bar', 'baz']
blog_post_content_type.fields.create(id: 'valid', name: 'Testing IN', type: 'Text', validations: [validation_in])

size

Takes optional min and max parameters and validates the size of the array (number of objects in it).

validation_size = Contentful::Management::Validation.new
validation_size.size = { min: 10, max: 15 }
blog_post_content_type.fields.create(id: 'valid', name: 'Test SIZE', type: 'Text', validations: [validation_size])

range

Takes optional min and max parameters and validates the range of a value.

validation_range = Contentful::Management::Validation.new
validation_range.range = { min: 100, max: 150 }
blog_post_content_type.fields.create(id: 'valid', name: 'Range', type: 'Text', validations: [validation_range])

regex

Takes a string that reflects a JS regex and flags, validates against a string. See JS Reference for the parameters.

validation_regexp = Contentful::Management::Validation.new
validation_regexp.regexp = {pattern: '^such', flags: 'im'}
blog_post_content_type.fields.create(id: 'valid', name: 'Regex', type: 'Text', validations: [validation_regexp])

linkContentType

Takes an array of content type ids and validates that the link points to an entry of that content type.

validation_link_content_type = Contentful::Management::Validation.new
validation_link_content_type.link_content_type =  ['post_content_type_id']
blog_post_content_type.fields.create(id: 'entry', name: 'Test linkContentType', type: 'Entry', validations: [validation_link_content_type])

linkMimetypeGroup

Takes a MimeType group name and validates that the link points to an asset of this group.

validation_link_mimetype_group = Contentful::Management::Validation.new
validation_link_mimetype_group.link_mimetype_group = 'image'
content_type.fields.create(id: 'asset', validations: [validation_link_mimetype_group])

present

Validates that a value is present.

validation_present = Contentful::Management::Validation.new
validation_present.present = true
content_type.fields.create(id: 'number', validations: [validation_present])

linkField

Validates that the property is a link (must not be a valid link, just that it looks like one).

validation_link_field = Contentful::Management::Validation.new
validation_link_field.link_field  = true
content_type.fields.create(id: 'entry', validations: [validation_link_field])

Locales

Retrieving all locales from the environment:

blog_post_locales = environment.locales.all

Retrieving one locale by ID from the environment:

blog_post_locale = environment.locales.find(locale_id)

Creating a locale:

environment.locales.create(name: 'German', code: 'de-DE')

Creating a locale with fallback:

environment.locales.create(name: 'German', code: 'de-DE', fallback_code: 'en-US')

Updating a locale:

blog_post_locale.update(name: 'German', code: 'de-DE')

Updating a locale with fallback:

blog_post_locale.update(name: 'German', code: 'de-DE', fallback_code: 'en-US')

Destroying a locale:

blog_post_locale.destroy

Tags

Retrieving all tags from the environment:

tags = environment.tags.all

Retrieving one tag by ID from the environment:

tag = environment.tags.find(tag_id)

Creating a tag:

environment.tags.create(name: 'tag name', id: 'tagID')

Updating a tag:

tag.update(name: 'new name')

Destroying a tag:

tag.destroy

Tagging an entry:

entry.update(_metadata: {"tags": [{ "sys": { "type": "Link", "linkType": "Tag", "id": "fooTag" } }]})

Tagging an asset:

asset.update(_metadata: {"tags": [{ "sys": { "type": "Link", "linkType": "Tag", "id": "fooTag" } }]})

Roles

Retrieving all roles from the space:

blog_post_roles = blog_space.roles.all

Retrieving one role by ID from the space:

blog_post_role = blog_space.role.find(role_id)

Creating a role:

role_attributes = {
  name: 'My Role',
  description: 'foobar role',
  permissions: {
    'ContentDelivery': 'all',
    'ContentModel': ['read'],
    'Settings': []
  },
  policies: [
    {
      effect: 'allow',
      actions: 'all',
      constraint: {
        and: [
          {
            equals: [
              { doc: 'sys.type' },
              'Entry'
            ]
          },
          {
            equals: [
              { doc: 'sys.type' },
              'Asset'
            ]
          }
        ]
      }
    }
  ]
}
blog_space.roles.create(role_attributes)

Updating a role:

blog_post_role.update(name: 'Some Other Role') # Can change any attribute here

Destroying a role:

blog_post_role.destroy

Webhooks

Retrieving all webhooks from the space:

webhooks = blog_space.webhooks.all

Retrieving one webhook by ID from the space:

blog_post_webhook = blog_space.webhooks.find(webhook_id)

Creating a webhook:

blog_space.webhooks.create(
  name: 'My Webhook',
  url: 'https://www.example.com',
  httpBasicUsername: 'username',
  httpBasicPassword: 'password'
)

Updating a webhook:

blog_post_webhook.update(url: 'https://www.newlink.com')

Destroying a webhook:

blog_post_webhook.destroy

Creating a webhook with custom headers and custom topics:

blog_space.webhooks.create(
  name: 'Entry Save Only',
  url: 'https://www.example.com',
  topics: [ 'Entry.save' ],
  headers: [
    {
      key: 'X-My-Custom-Header',
      value: 'Some Value'
    }
  ]
)

Webhook Calls

Retrieving all webhook call details from a webhook:

all_call_details = my_webhook.webhook_calls.all

Retrieving one webhook call detail by ID from a webhook:

call_details = my_webhook.webhook_calls.find(call_id)

Webhook Health

Retrieving webhook health details from a webhook:

health_details = my_webhook.webhook_health.find

Space Memberships

Retrieving all space memberships from the space:

memberships = blog_space.space_memberships.all

Retrieving one space membership by ID from the space:

blog_post_membership = blog_space.space_memberships.find(membership_id)

Creating a space membership:

blog_space.space_memberships.create(
  admin: false,
  roles: [
    {
      'sys' => {
        'type' => 'Link',
        'linkType' => 'Role',
        'id' => 'my_role_id'
      }
    }
  ],
  email: '[email protected]'
)

Updating a space membership:

blog_post_membership.update(admin: true)

Destroying a space membership:

blog_post_membership.destroy

Organizations

Retrieving all organization details:

organizations = client.organizations.all

Usage

Note: This feature is available only to Commited v2 customers.

Organization Periodic Usage

Retrieving all API Usage statistics for an Organization during a given usage period, broken down by organization for all APIs:

# Optionally, you can pass the metric, start and end date filters
usage = client.organization_periodic_usages('organization_id').all

# For example only CDA and CMA metrics from yesterday onwards
usage = client.organization_periodic_usages('organization_id').all('metric[in]': ['cda', 'cma'], startDate: (Date.today - 1).iso8601)

Alternatively, if you have an already fetched organization:

# Breaking down CMA usage by space, for a given period.
usage = organization.periodic_usages.all

Space Periodic Usage

Retrieving all API Usage statistics for an Organization during a given usage period, broken down by space for all APIs:

# Optionally, you can pass the metric, start and end date filters
usage = client.space_periodic_usages('organization_id').all

# For example only CDA and CMA metrics from yesterday onwards
usage = client.space_periodic_usages('organization_id').all('metric[in]': ['cda', 'cma'], startDate: (Date.today - 1).iso8601)

Alternatively, if you have an already fetched organization:

# Breaking down CMA usage by space, for a given period.
usage = organization.space_periodic_usages.all

Users

Retrieving current user details:

user = client.users.me

Retrieving all users in organization:

user = organization.users.all

Retrieving one user by ID from an organization:

user = organization.users.find('user_id')

Retrieving all users in a space:

user = blog_space.users.all

Retrieving one user by ID from the space:

user = blog_space.users.find('user_id')

UI Extensions

Retrieving all UI extensions from the environment:

extensions = environment.ui_extensions.all

Retrieving one UI extension by ID from the environment:

blog_post_extension = environment.ui_extensions.find(extension_id)

Creating a UI extension:

environment.ui_extensions.create(
  extension: {
    'name' => 'My extension',
    'src' => 'https://www.example.com',
    'fieldTypes' => [{"type": "Symbol"}],
    'sidebar' => false
  }
)

Destroying a UI extension:

blog_post_extension.destroy

API Keys

Retrieving all API keys from the space:

blog_post_api_keys = blog_space.api_keys.all

Retrieving one API key by ID from the space:

blog_post_api_key = blog_space.api_keys.find(api_key_id)

Creating an API key:

blog_space.api_keys.create(name: 'foobar key', description: 'key for foobar mobile app')

Creating an API key with multiple environments:

blog_space.api_keys.create(
  name: 'foobar key - multiple environments',
  description: 'key for foobar app',
  environments: [
    {
      sys: {
        type: 'Link',
        linkType: 'Environment',
        id: 'master'
      }
    },
    {
      sys: {
        type: 'Link',
        linkType: 'Environment',
        id: 'staging'
      }
    }
  ]
)

Preview API Keys

Retrieving all Preview API keys from the space:

blog_post_preview_api_keys = blog_space.preview_api_keys.all

Retrieving one Preview API key by ID from the space:

blog_post_preview_api_key = blog_space.preview_api_keys.find(api_key_id)

If you already have an API key fetched, you can retrieve the Preview API key from it:

blog_post_preview_api_key = blog_post_api_key.preview_api_key

Personal Access Tokens

Retrieving all personal access tokens:

tokens = client.personal_access_tokens.all

Retrieving one personal access token by ID:

token = client.personal_access_tokens.find(token_id)

Creating a personal access token:

client.personal_access_tokens.create(name: 'foobar key', scopes: ['content_management_manage'])

Revoking a personal access token:

token.revoke

Editor Interface

Retrieving editor interface for a content type:

blog_post_editor_interface = blog_post_content_type.editor_interface.default

You can call the EditorInterface API from any level within the content model hierarchy, take into account that you'll need to pass the IDs of the levels below it.

Hierarchy is as follows: No Object -> Environment -> ContentType -> EditorInterface

Entry Snapshots

Retrieving all snapshots for a given entry:

snapshots = entry.snapshots.all

Retrieving a snapshot for a given entry:

snapshot = entry.snapshots.find('some_snapshot_id')

Entry references

Retrieving entry references:

references = entry.references(include: 1)

Content Type Snapshots

Retrieving all snapshots for a given content type:

snapshots = content_type.snapshots.all

Retrieving a snapshot for a given content type:

snapshot = content_type.snapshots.find('some_snapshot_id')

Pagination

environment.entries.all(limit: 5).next_page
environment.assets.all(limit: 5).next_page
environment.entries.all(limit: 5).next_page

Logging

Logging is disabled by default, it can be enabled by setting a logger instance and a logging severity.

client = Contentful::Management::Client.new('access_token', logger: logger_instance, log_level: Logger::DEBUG)

Example loggers:

Rails.logger
Logger.new('logfile.log')

The default severity is set to INFO and logs only the request attributes (headers, parameters and url). Setting it to DEBUG will also log the raw JSON response.

Raise Errors

If :raise_errors is set to true, an Exception will be raised in case of an error. The default is false, in this case a Contentful::Management::Error object will be returned.

client = Contentful::Management::Client.new('access_token', raise_errors: true)

Content Type Cache

This allows for fetching content types for your environment at client instantiation time, which prevents extra requests per entry. To enable this, in your client instantiation do:

client = Contentful::Management::Client.new(token, dynamic_entries: {'my_space_id' => 'my_environment_id'})

You can enable the cache for as many environments as you want. If no environment is added, content types will be fetched upon environment find.

To completely disable this feature, upon client instantiation do:

client = Contentful::Management::Client.new(token, disable_content_type_caching: true)

Proxy Support

This allows for using the CMA SDK through a proxy, for this, your proxy must support HTTPS and your server must have a valid signed certificate.

To enable this, in your client instantiation do:

PROXY_HOST = 'localhost'
PROXY_PORT = 8888

# Just host/port
client = Contributing::Management::Client.new(
  token,
  proxy_host: PROXY_HOST,
  proxy_port: PROXY_PORT
)

# With username/password
client = Contributing::Management::Client.new(
  token,
  proxy_host: PROXY_HOST,
  proxy_port: PROXY_PORT,
  proxy_username: 'YOUR_USERNAME',
  proxy_password: 'YOUR_PASSWORD'
)

Rate limit management

With the following configuration options you can handle how rate limits are handled within your applications.

:max_rate_limit_retries

To increase or decrease the retry attempts after a 429 Rate Limit error. Default value is 1. Using 0 will disable retry behaviour. Each retry will be attempted after the value (in seconds) of the X-Contentful-RateLimit-Reset header, which contains the amount of seconds until the next non rate limited request is available, has passed. This is blocking per execution thread.

:max_rate_limit_wait

Maximum time to wait for next available request (in seconds). Default value is 60 seconds. Keep in mind that if you hit the houly rate limit maximum, you can have up to 60 minutes of blocked requests. It is set to a default of 60 seconds in order to avoid blocking processes for too long, as rate limit retry behaviour is blocking per execution thread.

Contributing

  1. Fork it ( https://github.com/[my-github-username]/contentful-management/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

contentful-management.rb's People

Contributors

angusfretwell avatar armandoamador avatar averethel avatar cakejelly avatar cf-allstar[bot] avatar dlitvakb avatar ghepting avatar grncdr avatar hq063 avatar jjolton-contentful avatar kamui avatar loudmouth avatar mariobodemann avatar neonichu avatar pxlpnk avatar roblinde avatar rubydog avatar ruderngespra avatar sbonami avatar sdepold avatar seand7565 avatar severin avatar timfjord avatar tnir avatar warkocz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

contentful-management.rb's Issues

Posting a Resource with Circular References breaks when Application also includes ActiveSupport

When your app requires ActiveSupport and you have a resource with circular references.

As ActiveSupport patches HTTP::Client to call ActiveSupport#to_json on #post and #put.

This causes a never-ending loop that will halt any other operations. This affects specifically Entry#save in the case of circular references.

Fix required is very simple:

module Contentful
  module Management
    class Entry
      alias_method :to_json, :fields_for_query
    end
  end
end

Pagination on assets doesn't work without first calling limit

If I do

client = Contentful::Management::Client.new(ENV['CONTENTFUL_MANAGEMENT_ACCESS_TOKEN'], logger: Rails.logger, log_level: Logger::DEBUG, max_rate_limit_wait: 10)
space = client.spaces.find(ENV['CONTENTFUL_SPACE_ID'])
assets = space.assets.all
assets = assets.next_page

The query hash on the request isn't instantiated:

NoMethodError: undefined method `[]=' for nil:NilClass
from /Users/cchow/.rbenv/versions/2.3.4/lib/ruby/gems/2.3.0/gems/contentful-management-1.10.0/lib/contentful/management/array.rb:27:in `next_page'

Calling space.assets.all(limit: 100) works with next_page though.

This doesn't seem to affect entries (don't need to call limit first).

409 Conflict while creating a new asset

Hello, for some reason I am receiving a 409 Conflict when I go to publish an asset that the script itself is creating. There is no human modifying the record, and this code was working perfectly until yesterday.

I am using the Contentful Management gem for Ruby (raw, without Contentful Model) to do this. Code:

  def create_image_asset(url, title: nil, description: nil)
    return nil unless url 

    # Create an asset for the image 
    cf_image = Contentful::Management::File.new 
    content_type = if url.end_with?('.png')
      'image/png' 
    elsif url.end_with?('.jpg')
      'image/jpeg'
    else 
      nil 
    end 

    raise "Could not determine image format for URL '#{url}'" unless content_type

    filename = url.gsub(/^.*\//, '')
    cf_image.properties[:contentType] = content_type 
    cf_image.properties[:fileName] = filename
    cf_image.properties[:upload] = url

    title = filename unless title 
    description = "Imported by Platform from '#{url}'" unless description 

    asset = @space.assets.create(title: title, description: description, file: cf_image)

    if asset.kind_of?(Contentful::Management::ServerError) 
      raise "Received error while publishing image to Contentful: #{e.inspect}"
    end 

    asset.process_file 
    begin 
      asset.publish
    rescue StandardError => e
      Rails.logger.error "Failed to publish image asset. Asset had description '#{description}', title '#{title}', filename '#{filename}'"
      asset = nil
    end

    return asset
  end 

Why would this result in a 409 Conflict? Does @space.assets.create() return an existing object in some case? I used to re-raise after logging the failure, but this is running an ongoing content importer into Contentful and we're reliant on that content being imported into Contentful on a timely basis. For now our editors are going to have to manually find the thumbnail for the content, but we'd like to not have to do that. I have also filed a support request with the Contentful support team.

fields_for_query has buggy order-dependent logic

This comes from ZenDesk, and appears to be a bug on this line.

If the first (locale, fields) tuple in raw_fields does not contain every field name, then the result of fields_for_query can entirely skip fields that do have defined values in other locales.

Update Documentation

There are outdated links on class comments and some comments are oudated as well

Items are nil when creating an array field for a content type

require 'contentful/management'
require 'logger'

def link_field 
  field = Contentful::Management::Field.new 
  field.type = 'Link' 
  field.link_type = 'Entry' 
  field 
end

logger = Logger.new(STDOUT)
level = Logger::DEBUG
client = Contentful::Management::Client.new('token', logger: logger, log_level: level)
space = Contentful::Management::Space.find 'space'
ct = space.content_types.find('content_type')

From the request

{:query=>{:name=>"content_Type", :description=>"", :fields=>[{:id=>"test_array_id", :name=>"test_array", :type=>"Array", :items=>nil}]}}

It seems somewhere in here the items array gets lost:
https://github.com/contentful/contentful-management.rb/blob/master/lib/contentful/management/field.rb#L37

Possible to resolve users from snapshots?

For a given Entry I'd like to enumerate all snapshots and display the name of the person who made the change as well as the timestamp. I can get the timestamp easily enough, but users show up as #<Contentful::Management::Link: @sys={:type=>"Link", :linkType=>"User", :id=>"23hkj6hkjs809sd"}>

It's not clear from the docs if there's a way to fetch or inflate that user. Ideas?

Implement :disabled property on Field class

I ran into an issue trying to do content_types.fields.destroy() ... from what I can tell, Contentful is very unhappy when a field goes missing entirely. Although it will allow the update to proceed, future attempts to activate the content type will fail (through the admin and through the API) with the error "Error activating "Product": Previously published fields are missing or invalid". Note I emailed [email protected] with details about this.

As I looked to resolve for my own use of the API, it occurred to me that the admin itself does not support "deleting" fields, it only supports disabling them. The API also supports disabling fields with the boolean :disabled which is returned from the API as true when the field is disabled. If the flag is passed to the API it is respected.

So a feature request would be to add the property :disabled to the Field class.

Presumably an additional beneficial feature would be to either reimplement destroy to only disable instead of removing, or to add a separate implementation of disable to content_type.fields that disabled the field.

I've resorted to adding the property to Field via class_eval for my purposes and it suffices.

Thanks!

(Side note: Sorry to bug you guys with all these issues, but the implementation I'm working on is putting this API through its paces, so I'm just logging any issues or shortcomings I'm uncovering. I have one or two more issues to log in addition to this).

Erroneously reporting 'Service Unavailable, contenful.com API seems to be down'

Using the following code:

require 'contentful/management'

TOKEN_FILE_PATH = "./data/.oauth.token"
SPACE_ID = "pzben22qc4sf"

def get_access_token
  # check for the existence of the /data/.oauth.token file
  if File.exists? TOKEN_FILE_PATH
    open(TOKEN_FILE_PATH).read
  else
    raise "Please authenticate the application before using this tool"
  end
end

puts "ACCESS TOKEN: #{get_access_token}"
puts "SPACE_ID: #{SPACE_ID}"

client = Contentful::Management::Client.new(get_access_token)
puts client
space = client.spaces.find(SPACE_ID)
puts space
space.assets.all

We get the following output:

#<Contentful::Management::Client:0x007fc192162500>
Service Unavailable, contenful.com API seems to be down
test.rb:22:in `<main>': undefined method `assets' for #<Contentful::Management::ServiceUnavailable:0x007fc1921a8960> (NoMethodError)

Using the same space, same accessToken in Node, everything runs fine and nothing reports as a 503 (because everything is operational and online).

This recently just caused us a couple of days of issues because we assumed that the bridge was down, and it clearly isn't.

Looking at the code, it looks like maybe there's a condition that's not getting handled or getting mapped to an error hash that isn't entirely right.

OpenSSL::SSL::SSLError: SSL_connect SYSCALL returned=5 errno=0 state=SSLv3/TLS write client hello

Occasionally, the management gem crashes on request to Contentful with this error. It's not readily reproducible, and happens during our content import process, so this is blocking us from fully moving to Contentful for our content solution.

Information online is hard to find, but some indicate this may be due to a misconfiguration on Contentful's servers, which would make sense if only a few of the CMA servers have the misconfiguration (since the problem is not consistent).

Any guidance?

OpenSSL::SSL::SSLError: SSL_connect SYSCALL returned=5 errno=0 state=SSLv3/TLS write client hello
project-gemset/gems/http-2.2.2/lib/http/timeout/null.rb:26:in `connect'
project-gemset/gems/http-2.2.2/lib/http/timeout/null.rb:26:in `connect_ssl'
project-gemset/gems/http-2.2.2/lib/http/timeout/null.rb:35:in `start_tls'
project-gemset/gems/http-2.2.2/lib/http/connection.rb:158:in `start_tls'
project-gemset/gems/http-2.2.2/lib/http/connection.rb:44:in `initialize'
project-gemset/gems/http-2.2.2/lib/http/client.rb:60:in `new'
project-gemset/gems/http-2.2.2/lib/http/client.rb:60:in `perform'
project-gemset/gems/http-2.2.2/lib/http/client.rb:41:in `request'
project-gemset/gems/http-2.2.2/lib/http/chainable.rb:19:in `get'
project-gemset/gems/contentful-management-1.10.1/lib/contentful/management/client.rb:453:in `public_send'
project-gemset/gems/contentful-management-1.10.1/lib/contentful/management/client.rb:453:in `http_send'
project-gemset/gems/contentful-management-1.10.1/lib/contentful/management/client.rb:399:in `block in get'
project-gemset/gems/contentful-management-1.10.1/lib/contentful/management/client.rb:343:in `execute_request'
project-gemset/gems/contentful-management-1.10.1/lib/contentful/management/client.rb:398:in `get'
project-gemset/gems/contentful-management-1.10.1/lib/contentful/management/request.rb:41:in `get'
project-gemset/gems/contentful-management-1.10.1/lib/contentful/management/resource_requester.rb:69:in `get'
project-gemset/gems/contentful-management-1.10.1/lib/contentful/management/resource_requester.rb:15:in `all'
project-gemset/gems/contentful-management-1.10.1/lib/contentful/management/resource.rb:186:in `all'
project-gemset/gems/contentful-management-1.10.1/lib/contentful/management/content_type_entry_methods_factory.rb:26:in `all'
(our code has control below here)

Example editor interface update code does not work

I have been working on an integration with contentful and I have been having some issues getting the example code to work. The example below was taken from here

require 'contentful/management'

client = Contentful::Management::Client.new('<content_management_api_key>')

editor_interface = client.editor_interfaces.default('<space_id>', '<content_type_id>')
editor_interface.controls[0] = {
  fieldId: 'title',
  widgetId: 'singleLine'
}
editor_interface.save

The error that I am getting is

NoMethodError:
       undefined method `save' for #<Contentful::Management::EditorInterface:0x00007f8e0140fe98>

I assume that the method I should be using is update? Something like the following perhaps?

require 'contentful/management'

client = Contentful::Management::Client.new('<content_management_api_key>')

editor_interface = client.editor_interfaces.default('<space_id>', '<content_type_id>')
editor_interface.controls[0] = {
  fieldId: 'title',
  widgetId: 'singleLine'
}
editor_interface.update(controls: editor_interface.controls)

Gem is not thread safe

Doesn't look like this gem is thread safe. Using https://github.com/grosser/parallel with in_threads I get the following errors when iterating over entries:

/Users/cchow/.rbenv/versions/2.3.4/lib/ruby/gems/2.3.0/gems/contentful-management-1.10.0/lib/contentful/management/request.rb:13:in `initialize': undefined method `version=' for nil:NilClass (NoMethodError)
        from /Users/cchow/.rbenv/versions/2.3.4/lib/ruby/gems/2.3.0/gems/contentful-management-1.10.0/lib/contentful/management/resource_requester.rb:84:in `new'
        from /Users/cchow/.rbenv/versions/2.3.4/lib/ruby/gems/2.3.0/gems/contentful-management-1.10.0/lib/contentful/management/resource_requester.rb:84:in `delete'
        from /Users/cchow/.rbenv/versions/2.3.4/lib/ruby/gems/2.3.0/gems/contentful-management-1.10.0/lib/contentful/management/resource_requester.rb:51:in `unarchive'
        from /Users/cchow/.rbenv/versions/2.3.4/lib/ruby/gems/2.3.0/gems/contentful-management-1.10.0/lib/contentful/management/resource/publisher.rb:21:in `unpublish'
        from entry.rb:38:in `block (3 levels) in <main>'
        from /Users/cchow/.rbenv/versions/2.3.4/lib/ruby/2.3.0/benchmark.rb:308:in `realtime'
        from entry.rb:37:in `block (2 levels) in <main>'
        from /Users/cchow/.rbenv/versions/2.3.4/lib/ruby/gems/2.3.0/gems/parallel-1.12.1/lib/parallel.rb:486:in `call_with_index'
        from /Users/cchow/.rbenv/versions/2.3.4/lib/ruby/gems/2.3.0/gems/parallel-1.12.1/lib/parallel.rb:342:in `block (2 levels) in work_in_threads'
        from /Users/cchow/.rbenv/versions/2.3.4/lib/ruby/gems/2.3.0/gems/parallel-1.12.1/lib/parallel.rb:495:in `with_instrumentation'
        from /Users/cchow/.rbenv/versions/2.3.4/lib/ruby/gems/2.3.0/gems/parallel-1.12.1/lib/parallel.rb:341:in `block in work_in_threads'
        from /Users/cchow/.rbenv/versions/2.3.4/lib/ruby/gems/2.3.0/gems/parallel-1.12.1/lib/parallel.rb:206:in `block (2 levels) in in_threads'

The script I was running:

require 'benchmark'
require 'optparse'
require 'parallel'
require 'contentful/management'
require 'pry'

STDOUT.sync = true
logger = Logger.new(STDOUT)

options = {}

OptionParser.new do |parser|
  parser.on("-t", "--token KEY", "Contentful management access token") do |v|
    options[:token] = v
  end
  parser.on("-s", "--space-id SPACEID", "Contentful space ID") do |v|
    options[:space_id] = v
  end
  parser.on("-p", "--type CONTENT_MODEL", "Contentful type to delete ID") do |v|
    options[:type] = v
  end
  parser.on("-c", "--command COMMAND", "Contentful command like publish, unpublish or destroy") do |v|
    options[:command] = v
  end
end.parse!

client = Contentful::Management::Client.new(options[:token], log_level: Logger::DEBUG)
space = client.spaces.find(options[:space_id])
content_type = space.content_types.find(options[:type])
entries = content_type.entries.all(limit: 100, order: 'sys.id')

loop do
  break if entries.count == 0

  # entries.each do |entry|
  Parallel.each(entries, in_threads: 5) do |entry|
    t = Benchmark.realtime do
      entry.send(options[:command])
    end

    logger.info({ duration: "#{t * 1000}ms", action: "entry.#{options[:command]}" }.to_json)
  end

  entries = entries.next_page
end

Seems to be some shared state across threads in the gem but haven't a chance to look. Using in_processes works though.

Error when updating content type with validations

I'm getting an IOError when attempting to update an existing content type that has fields with validation on it. The error is emanating from pretty deep within HTTP::Client, when the parameters are being coerced as json. It appears that during the process of converting properties to a hash, the :validations property of the Validation object has an extra Validation on it which is getting missed.

So let me see if I can explain. This is what fields looks like on an example content type I'm building at the create step:

[#<Contentful::Management::Field: @properties={
:id=>"featuredImage", 
:name=>"Featured Image", 
:type=>"Link", 
:linkType=>"Asset", 
:validations=>
[#<Contentful::Management::Validation: @properties={:linkMimetypeGroup=>"image"}>]}>]

After the object is created, if I re-retrieve the content type and attempt to update it, this is what fields looks like:

[#<Contentful::Management::Field: @properties={
 :id=>"featuredImage", 
 :name=>"Featured Image", 
 :type=>"Link", 
 :linkType=>"Asset", 
 :items=>#<Contentful::Management::Field:>, 
 :required=>false, 
 :localized=>false, 
 :validations=>[
                <Contentful::Management::Validation: @properties={:in=>nil, :size=>nil, :present=>false, 
                :validations=>#<Contentful::Management::Validation:>, :regexp=>nil, :linkContentType=>nil, :range=>nil, 
                :linkMimetypeGroup=>"image", :linkField=>false}>
                ]}>
]

The biggest difference is the existence of the :validations property on the Validation object itself, which gets left in place and cannot be coerced into json.

It looks like some special handling needs to be added to account for this property, akin to the way those other properties are handled in the parse_value of the Field class.

Let me know if the above makes sense or you have any questions. I'm going to attempt to work around this issue in the meantime.

Implement :displayField property on ContentType

This one appears to be a bug, because if content_type.update() gets called without explicitly setting :displayField as an attribute, then the displayField that was set previously on the content type is lost subsequently.

The fix for this would be to add a :displayProperty field to ContentType, since that field is already included in the response from the API and the API respects it when it's included. The update() method would also need to be updated to merge that property into the parameters hash.

No errors for locale validation

I'm using Contentful Model gem but thought I'd raise the issue here as it seems more to do with Management.

Tried doing this in seeds.rb:

Root::Model::Index::Jumbotron::CallbackForm.create({
  :title_text => "Get Your Free Quote",
  :footer_text => "and we'll call to tell you\n\ncall us on 123 123 123 to get a quote now",
  :button_text => "Get a callback"
})

And got the following error:

Contentful::Management::UnprocessableEntity: HTTP status code: 422 Unprocessable Entity
Message: Validation error
Details:
	* Name:  - Path: '' - Value: ''
	* Name:  - Path: '' - Value: ''
	* Name:  - Path: '' - Value: ''

Not sure how to interpret the error.

Getting Contentful::Link rather than Contentful::Entry for nested objects

My migrations:

# root_callback_form_content_type.rb
class RootCallbackFormContentType < ActiveRecord::Migration[5.1]
  include ContentfulModel::Migrations::Migration

  def up
    create_content_type("Root::CallbackForm") do |content_type|
      content_type.id("root_callbackForm")
      content_type.field("Title Text", :symbol)
      content_type.field("Footer Text", :text)
      content_type.field("Button Text", :symbol)
    end.publish
  end
end

# jumbotron_content_type.rb
class JumbotronContentType < ActiveRecord::Migration[5.1]
  include ContentfulModel::Migrations::Migration

  def up
    create_content_type("Root::Jumbotron") do |content_type|
      content_type.id("root_jumbotron")
      content_type.field("Text", :text)
      content_type.field("Callback Form", :entry_link).validations = [
        Contentful::Management::Validation.new.tap do |validation|
          validation.link_content_type = ["root_callbackForm"]
        end
      ]
    end.publish
  end
end

# root_index_content_type.rb
class RootIndexContentType < ActiveRecord::Migration[5.1]
  include ContentfulModel::Migrations::Migration

  def up
    create_content_type("Root::Index") do |content_type|
      content_type.id("root_index")
      content_type.field("Jumbotron", :entry_link).validations = [
        Contentful::Management::Validation.new.tap do |validation|
          validation.link_content_type = ["root_jumbotron"]
        end
      ]
    end.publish
  end
end

In a console:

irb> Root::Index.first.jumbotron.callback_form
=> <Contentful::Link id='1qugwa3L7CUIiEA2si2wmE'>

If I try and access a property, I get:

NoMethodError: undefined method `title_text' for <Contentful::Link id='1qugwa3L7CUIiEA2si2wmE'>:Contentful::Link

When I try and access Jumbotron it works as expected, not sure what the difference is. Any ideas?

"NoMethodError: undefined method `version=' for nil:NilClass" after `Contentful::Management::Space.all`

> Contentful::Management::Space.all
NoMethodError: undefined method `version=' for nil:NilClass
    from /Library/Ruby/Gems/2.0.0/gems/contentful-management-0.6.1/lib/contentful/management/request.rb:13:in `initialize'
    from /Library/Ruby/Gems/2.0.0/gems/contentful-management-0.6.1/lib/contentful/management/space.rb:29:in `new'
    from /Library/Ruby/Gems/2.0.0/gems/contentful-management-0.6.1/lib/contentful/management/space.rb:29:in `all'

show more verbose errors

Right now, it is really hard to find out, why an api call is failing. For example when I try to create an entry with

content_type.entries.create(title: 'Test Title')
=> #<Contentful::Management::UnprocessableEntity: Validation error>

to really see what is actually not validating I just found this way:

e = content_type.entries.create(title: 'Test Title')
=> #<Contentful::Management::UnprocessableEntity: Validation error>
puts e.response.raw.body.to_s
=> <showing the full response body with the error>

Would be nice to have the error message(s) in an instance var or something like that in the exception.

multiple `Field#create` calls on a content type lose validations

When calling fields.create with validations multiple times, the validations are not being sent to the api and therefore result in an invalid content type.

Called like this:

content_type.fields.create id: 'tag', name: 'Tag', type: 'Symbol', validations: [validations_in] 
content_type.fields.create id: 'slug', name: 'Slug', type: 'Symbol', validations: [validation_regex] 

Localization: default locale returned for empty fields without fallback

I have the following code:

entry = environment.entries.find('ENTRY_ID')

puts entry.title # prints English title
entry.locale = 'de'
puts entry.title # prints German title

So far so good.

But when I change locale= to another locale code that exists in my space, for which no fallback locale has been specified and for which the entry in question has no value saved (the field is empty), it returns content in the default locale (English in this case). The same is true for literally any other string I set as locale= on the entry object.

entry.locale = 'ru' # doesnโ€™t exist in my space
puts entry.title # prints English title

entry.locale = 'literally anything'
puts entry.title # prints English title

What am I missing here? I would expect an empty string to be returned. Is there a different way I should be querying for different locales? I am using the management API and not contentful.rb because the goal is to later on update the entries, but with no way of checking if a field is empty in a given locale that becomes very error prone.

Thank you for any pointers.

Gem is modifying nil

We've been using the gem lately in our project and we noticed that certain calls are modifying the nil object. Based on the history of the repo I know this library is very new but I wanted to open up an issue to discuss this and see if it would be possible to avoid modifying the nil object and adding methods to it.

For instance:
https://github.com/contentful/contentful-management.rb/blob/master/lib/contentful/management/space.rb#L92

Is there any particular reason nil is being used as opposed to any other random Ruby object? Why not just use Object.new or something similarly simple/generic? Extending nil with methods, especially methods common in Ruby/Rails apps like find/all/create, seems like a dangerous thing to do for consumers of the library. I'd be happy to submit a pull request as well if this change seems reasonable to you but I wasn't sure if that would be helpful with the library being new and under active development.

Getting snapshots?

The developers api supports getting all the snapshots of an entry or getting a snapshot by ID, wondering if this is something that can be included in the management client as well?

API errors are returned instead of thrown

    @client = Contentful::Management::Client.new(...)
    @space = @client.spaces.find(@space_id)
    puts @space.class
    # --> ::Contentful::Management::RateLimitExceeded

Expected behavior:
Errors should always be thrown, never returned.

Actual behavior:
Upon rate limiting, spaces.find() is returning the error object.

This behavior stems from the ResourceBuilder (resource_builder.rb, line 75):

      # Starts the parsing process.
      # @return [Contentful::Management::Resource, Contentful::Management::Error]
      def run
        if response.status == :ok
          create_all_resources!
        else
          response.object
        end
      end

Upload causes crash

Rvm: 2.4.1
contentful-management: 1.8.1

My script:

#!/usr/bin/ruby

require 'contentful/management'

access_token = ENV['PERSONAL_CMA_TOKEN']
client = Contentful::Management::Client.new(access_token)

my_space = client.spaces.find(ENV['MY_SPACE_ID'])
space_id = my_space.id

## Upload
upload = client.uploads.create(space_id, './test-image.png')

causes the following output:

/Users/jpwright/.rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/openssl/buffering.rb:325:in `syswrite': Broken pipe (Errno::EPIPE)
	from /Users/jpwright/.rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/openssl/buffering.rb:325:in `do_write'
	from /Users/jpwright/.rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/openssl/buffering.rb:343:in `write'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/http-2.2.2/lib/http/timeout/null.rb:51:in `write'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/http-2.2.2/lib/http/request/writer.rb:99:in `write'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/http-2.2.2/lib/http/request/writer.rb:82:in `send_request'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/http-2.2.2/lib/http/request/writer.rb:42:in `stream'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/http-2.2.2/lib/http/request.rb:113:in `stream'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/http-2.2.2/lib/http/connection.rb:74:in `send_request'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/http-2.2.2/lib/http/client.rb:63:in `perform'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/http-2.2.2/lib/http/client.rb:41:in `request'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/http-2.2.2/lib/http/chainable.rb:26:in `post'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/contentful-management-1.8.1/lib/contentful/management/http_client.rb:85:in `public_send'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/contentful-management-1.8.1/lib/contentful/management/http_client.rb:85:in `http_send'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/contentful-management-1.8.1/lib/contentful/management/http_client.rb:30:in `post_http'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/contentful-management-1.8.1/lib/contentful/management/client.rb:315:in `block in post'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/contentful-management-1.8.1/lib/contentful/management/client.rb:252:in `execute_request'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/contentful-management-1.8.1/lib/contentful/management/client.rb:314:in `post'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/contentful-management-1.8.1/lib/contentful/management/request.rb:47:in `post'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/contentful-management-1.8.1/lib/contentful/management/resource_requester.rb:31:in `create'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/contentful-management-1.8.1/lib/contentful/management/resource.rb:209:in `create'
	from /Users/jpwright/.rvm/gems/ruby-2.4.1/gems/contentful-management-1.8.1/lib/contentful/management/client_association_methods_factory.rb:37:in `create'
	from test-upload.rb:17:in `<main>'

What might the issue be?

Additionally, I've noticed that acceptance tests in spec/lib/contentful/management/ only test that the returned object is of type Contentful::Management::Upload but does not test that any of the relevant properties have correct value

Unable to add a link to an entry

I have a mapping between diseases and genes
My disease id is 4ygut0Dm2c8SGw8oEwOKoY
I am trying to add a link to an existing gene (with id 5zIJxwfys80uIMcmAcaMkm) to this disease's entry.

Code is as follows:

new_link = "{ 'sys' => { 'id' => '5zIJxwfys80uIMcmAcaMkm', 'linkType' => 'Entry', 'type' => 'Link' } }"

entry = client.entries.find(space_id, '4ygut0Dm2c8SGw8oEwOKoY')
puts entry.fields[:associatedGenes]
entry.fields[:associatedGenes].push(new_link)
entry.save
entry.publish

I was able to see the following error but I am not quite sure how to solve this.

{
  "sys": {
    "type": "Error",
    "id": "InvalidEntry"
  },
  "message": "Validation error",
  "details": {
    "errors": [
      {
        "name": "type",
        "value": null,
        "type": "Object",
        "details": "The type of \"value\" is incorrect, expected type: Object",
        "path": [
          "fields",
          "associatedGenes",
          "en-US",
          1
        ]
      }
    ]
  },
  "requestId": "f62423c0ca4b3ea4907d86b8d34709a8"
}

Could you please tell me what I am doing wrong?

Localized fields brake entry creation

I'm trying to create a page type entry with localized fields and some not, using ruby sdk.
In my space, "en-US" is the default_language, but i want to create entry localized in "it-IT"

FIELDS NOT LOCALIZED: code, url, metaTitle
LOCALIZED FIElDS: title, body

content_type_page = client.content_types.find(space_id, 'page')
 
page = content_type_page.entries.create(
     code:            "abc",
     url:             "abc/def",
     title:           "Hello World!",
     metaTitle:       "fffggghhh,
     body:            "Bacon is Healty!",
     locale:          "it-IT"
)

The page created with this code has only localized fields and not the others, but if i use locale: "en-US" the fields are all.

How can i fix the problem?

JSON dependency on Ruby >2.4

Is there a way to eliminate forced dependency with json 1.8 or at least one way to get compatibility with Ruby 2.4?

Error in FieldAware

I have been successfully using contentful-management for a while now, and it has worked like a charm. Recently, I tried to use an import script that I wrote and my script bombs in field_aware.rb with the following error:
/home/devon/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/contentful-management-1.6.0/lib/contentful/management/resource/field_aware.rb:25:in 'block (2 levels) in create_getter': wrong number of arguments (given 1, expected 0) (ArgumentError) from /home/devon/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/contentful-management-1.6.0/lib/contentful/management/entry.rb:94:in 'save' from import_language_to_contentful.rb:78:in 'block in <main>' from /home/devon/.rbenv/versions/2.3.0/lib/ruby/2.3.0/csv.rb:1748:in 'each' from /home/devon/.rbenv/versions/2.3.0/lib/ruby/2.3.0/csv.rb:1131:in 'block in foreach' from /home/devon/.rbenv/versions/2.3.0/lib/ruby/2.3.0/csv.rb:1282:in 'open' from /home/devon/.rbenv/versions/2.3.0/lib/ruby/2.3.0/csv.rb:1130:in 'foreach' from import_language_to_contentful.rb:42:in '<main>'

I've narrowed it down to only one new contentful content type (all the others work as they used to), but I can't figure out why the new one shouldn't work. In fact, if I just do:
require 'contentful/management' require "csv" client = Contentful::Management::Client.new('my_num') space = client.spaces.find('another_num') e_that_exists = space.entries.find('an_existing_id') e_that_exists.save
I get the error.

Any ideas?

gracefully accept stringified keys

content_type.fields.create(name: "Layout", id: "layout", type: "Symbol")
=> #works
content_type.fields.create("name"=>"Layout", "id"=>"layout", "type"=>"Symbol")
=> #<Contentful::Management::UnprocessableEntity: Validation error>

Updating editor interface for single field resets editor interfaces and help text for other fields

Hi,

I used the following code to change the editor interface of a single field in a content type. It appears that in the process some of the editor interfaces settings of other fields in the same content type have been changed/reset and the help texts have vanished.

client = Contentful::Management::Client.new(<TOKEN>)
space = client.spaces.find(<SPACE-ID>)
editor_interface = client.editor_interfaces.default(space_id, "content_type_id")

editor_interface.controls = [
  {
    fieldId: "fieldName",
    widgetId: "dropdown",
    settings: {
      helpText: "Here is some help text."
    }
  }
]

editor_interface.update(controls: editor_interface.controls)

Bool fields set to `false` are not saved correctly

Current behaviour:

q = content_type.entries.new
q.name = "Hello World"
q.yolo = false
puts q.fields # {:name=>"Hello World", :yolo=>false}
q.save

p = space.entries.find(q.id)
puts p.fields # {:name=>"Hello World"}

but q.fields should be == p.fields

get error types via Contentful::Management::Error response

Hey there,

We are currently using Contentful::Management::Error to rescue any exceptions, however there are really few exceptions that the Contentful Error class is handling and we wanted to create our own class that catch more error types from responses returned. In the Management JS API we are able to get the full response with the name of the error returned from Contentful. Can we get the same thing via the Contentful::Management::Error response (or really if there are other ways, feel free to recommend)? The response we got only had headers, the request and few other details, however I couldn't get from it the same details I got from the management js api json response.

Thanks, and your help is highly appreciated.

Rana

Use default locale for existing spaces

Currently, the default locale for all operations is the one set in the Client or en-US if nothing was set.

This is a sensible default when creating new Spaces, but when dealing with an existing Space, we should query the user's default locale and use that.

Client methods undefined

Some of theClient methods aren't available. Weirdly, it looks like all the attrs and the methods marked # @private are defined, but the public ones are not. My testing code below:

require 'contentful/management'

client = Contentful::Management::Client.new('<ACCESS_TOKEN>')
puts client            # exists, everything is cool
client.access_token    # this exists
client.api_version     # works
spaces = client.spaces # ๐Ÿ’ฅ
puts client.methods

delete
configuration
version
version=
post
user_agent
get
put
protocol
default_locale
access_token
request_headers
logger
base_url
organization_id
zero_length
content_type_id
dynamic_entry_cache
api_version
gzip_encoded
default_configuration
setup_logger
update_dynamic_entry_cache_for_spaces!
update_dynamic_entry_cache_for_space!
update_dynamic_entry_cache!
register_dynamic_entry
execute_request
clear_headers
content_type_id=
organization_id=
authentication_header
api_version_header
organization_header
version_header
content_type_header
zero_length_header
accept_encoding_header
zero_length=
dynamic_entry_cache=
# regular Object methods
# ...

File uploads are processing forever

I'm uploading files via the Management API, but they're never done processing. I'm trying to add secondary locales to existing files. My code works for the entries, so I think I have all the locale stuff working correctly. I can update with the new file, which is a Contentful:Management::File with its contentType, fileName, and upload set to the MIME type, file name with extension, and publicly available URL of the file, respectively. Looks like what the API wants.

I have verified that I can paste the URL into the file picker UI and have it work. But when I use the API (via this library), the file is stuck in processing forever (or at least longer than 10 minutes, when it takes less than a second via the file picker). The asset title, file name, and file type appear correctly in the web UI. Any thoughts?

Code boils down to this:

Contentful::Management::File.new.tap do |file|
    file.properties[:contentType] = 'image/png'
    file.properties[:fileName] = 'cool-picture.png'
    file.properties[:upload] = 'https://example.com/cool-picture.png'
end

I can confirm that everything is set before using update on the parent asset.

I'm on v0.7.3.

`#{field}_with_locales=` Not working properly

Since v1.0 - Fields with Locales is not working properly

Test should be added for testing assignment properly

entry.fields_for_query
# => {:test=>{"en-US"=>"asdasd", "es"=>"gfsdsad"}, :b=>{"en-US"=>"asdasd", "es"=>nil}}
entry.b = 'blah'
# => "blah"
entry.fields_for_query
# => {:test=>{"en-US"=>"asdasd", "es"=>"gfsdsad"}, :b=>{"en-US"=>"blah", "es"=>nil}}
entry.b_with_locales = {'en-US' => 'foo', 'es' => 'bar'}
# => {"en-US"=>"foo", "es"=>"bar"}
entry.fields_for_query
# => {:test=>{"en-US"=>"asdasd", "es"=>"gfsdsad"}, :b=>{"en-US"=>"foo", "es"=>nil}}

Problems with fetching entries by content type (bug?)

Hi,

I've noticed some weird behaviour when attempting to fetch entries by content type like this:

require "contentful/management"

client = Contentful::Management::Client.new("<TOKEN>")
environment = client.environments("<SPACE-ID>").find("master")

environment.entries.all(content_type: "theme", limit: 10).each do |theme|
  puts theme.id
end

I can determine from the entry IDs printed out, that the entries in question are of ALL content types, not of "theme" as requested. Furthermore, the limit is fixed to 100, no matter what I set as the limit: parameter value.

Before environments where introduced, the comparable functionality would work just fine like this:

space.entries.all(content_type: "theme", limit: 10)

I can get it to work like this, but I didn't figure out how to set a limit.

environment.content_types.find("theme").entries.all.each do |theme|
  puts theme.id
end

Am I doing something wrong or is this a genuine bug?

Using a simple field assignment does not work

q = content_type.entries.new
q.name = "Hello World"
puts q.fields
# => {}

This should result in fields being set to {:name=>"Hello World"}.

Using q.name_with_locales = { "en-US" => "Hello World" } works as intended, though.

Media type validations not serialized/deserialized correctly?

Problem

Unable to add new fields to existing content types with media fields having size validations.

Trying to do so throws a validation error.

From looking at the request/response patterns, it seems assetFileSize (and maybe others) might not be properly deserialized or serialized.

Reproducing

I was able to reproduce this after creating a new content type:

{
  "name": "BugTest",
  "description": "",
  "fields": [
    {
      "name": "test",
      "id": "test",
      "type": "Link",
      "linkType": "Asset",
      "validations": [
        {
          "assetFileSize": {
            "max": 307200
          }
        }
      ],
      "localized": false,
      "required": false,
      "disabled": false,
      "omitted": false
    }
  ],
  "sys": {
    "id": "bugTest",
    "type": "ContentType",
    "createdAt": "2017-05-18T11:49:06.669Z",
    "createdBy": {
      "sys": {
        "type": "Link",
        "linkType": "User",
        "id": "17UVH9Mjx0q9zlEC0v0EuY"
      }
    },
    "space": {
      "sys": {
        "type": "Link",
        "linkType": "Space",
        "id": "c12nbik873vl"
      }
    },
    "firstPublishedAt": "2017-05-18T11:49:07.297Z",
    "publishedCounter": 2,
    "publishedAt": "2017-05-18T11:51:43.831Z",
    "publishedBy": {
      "sys": {
        "type": "Link",
        "linkType": "User",
        "id": "17UVH9Mjx0q9zlEC0v0EuY"
      }
    },
    "publishedVersion": 3,
    "version": 4,
    "updatedAt": "2017-05-18T11:51:43.844Z",
    "updatedBy": {
      "sys": {
        "type": "Link",
        "linkType": "User",
        "id": "17UVH9Mjx0q9zlEC0v0EuY"
      }
    }
  }
}

On this content type I was able to run the following code:

require 'contentful/management'

client = Contentful::Management::Client.new(ENV['CONTENTFUL_MANAGEMENT_TOKEN'], raise_errors: true)
space = client.spaces.find(SPACE_ID)
content_type = space.content_types.find('bugTest')

field = Contentful::Management::Field.new
field.type = 'Symbol'
field.id = 'newfield'
field.name = "TestNewField"

# This throws an validation error
content_type.fields.add(field)

The request was the following:

[2] pry(#<Contentful::Management::Client>)> request.query
=> {:name=>"BugTest",
 :description=>"",
 :fields=>
  [{:id=>"test",
    :name=>"test",
    :type=>"Link",
    :linkType=>"Asset",
    :items=>nil,
    :required=>nil,
    :localized=>nil,
    :validations=>[{}],
    :disabled=>nil,
    :omitted=>nil},
   {:type=>"Symbol", :id=>"newfield", :name=>"TestNewField"}]}

Response:

[4] pry(#<Contentful::Management::Client>)> puts raw_response.body.to_s
{
  "sys": {
    "type": "Error",
    "id": "ValidationFailed"
  },
  "message": "Validation error",
  "details": {
    "errors": [
      {
        "name": "type",
        "type": "Validation",
        "value": {},
        "details": "The property undefined isn't  a recognized validation type",
        "path": [
          "fields",
          0,
          "validations",
          0
        ]
      }
    ]
  },
  "requestId": "e59262f3b98f35740d374c5f04684672"
}

Cannot update entries/assets when en-US does not exist in the space

I have tried every combination that I can but it seems that the api is unable to create entries/assets properly (with localization) when en-US doesn't exist in the Space as a locale. We have en-GB and es-ES

I have a content type in the variable jo and that type has a Name name (a string) and a Value value (a json obejct) field. The Value field has localization enabled, the Name field does not.

new_entry = jo.entries.create(:id => 'hello-world')

I now have a new_entry with id hello-world

Now I try to set attributes (I have tried this on the create call above to, but it doesn't work)

new_entry.name = "Hello World"
new_entry.locale = 'en-GB'
new_entry.value_with_locales = {'en-GB'=>{"1"=>"2"}, 'es-ES'=>{"3":"4"}}
res = new_entry.save
JSON.parse(new_entry.error[:details])['details'] #{"errors"=>[{"message"=>"Invalid field locale code \"en-US\", has to be one of \"en-GB\", \"es-ES\"", "details"=>"The property \"en-US\" is not allowed here."}]}

It appears that the locale is defaulting to en-US even though it isn't even active in the Space.

De-Singleton Client from Resources (Multi-Locale Handling)

After discussion with @neonichu,
#67 Solved the problem of spaces being aware of their default locale

But with #73, the problem of :default_locale being handled globally for requests other than Space came up.

Therefore, it's required that the Client is removed from being a Singleton, though still the Shared Instance could be used, but only as a "Token Accessor" and clones of said Client should then be used inside the resources, which will allow each resource to adjust Locale and other configuration per Resource

cc @grncdr

displayField causing validation errors when not set

This related to pull request #52.

So i just upgraded to 0.6.1 and in doing so I hit a validation error in the case where a display field is not defined on a content type. Digging here's what I think might be happening:

:displayField is a string property. When I load up a content type without a displayField being set, the nil or missing value is being coerced into a blank string i.e. "". On a subsequent update to that content type, the displayField is now being explicitly passed in the request as displayField: "" (that is instead of dispalyField: null). This works, but when the content type is activated a validation error is thrown:

{
  "sys": {
    "type": "Error",
    "id": "ValidationFailed"
  },
  "message": "Validation error",
  "details": {
    "errors": [
      {
        "name": "referencesFieldId",
        "details": "Attribute displayField must reference a field's id",
        "displayFieldValue": "",
        "allowedValues": [
          "productName",
          "headling",
          "description",
          "bullets",
          "pdpSketch",
          "relatedContent",
          "lifestyleImages"
        ]
      }
    ]
  }
}

Not sure the best way to solve, but you might need to check if display_field.blank? and then explicitly set :displayField to nil if so, as opposed to letting the "" value slip through.

We're holding off on 0.6.1 in the meantime.

Many thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.