Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting save failures for lack of an X-Contentful-Content-Type header #177

Open
joelip opened this issue Sep 28, 2018 · 7 comments
Open

Comments

@joelip
Copy link

joelip commented Sep 28, 2018

Hi there,

I've been trying to duplicate Contentful entries in an application I'm writing and trying to save an object instantiated with the new operator is yielding this error:

#<Contentful::Management::BadRequest: HTTP status code: 400 Bad Request
Message: You should provide a content type in X-Contentful-Content-Type request header.
Request ID: 2cb81ec8f3daee7d4587a8bb61688a4b>

Here is how I'm initializing the client and instantiating the new entry object:

client = Contentful::Management::Client.new(
   '<access-token-here>',
   http_proxy, # This comes from a private method. Can elaborate further if necessary.
)
source_entry = management_client.entries.find(entry_id)
source_content_type = management_client.content_types.find(entry_content_type_id)
dup_entry = source_content_type.entries.new
# Here is where I have some application-specific code for calling the setters with the values
# from `source_entry.fields_for_query`
dup_entry.save # returns the bad request object

I was modeling my code after the examples provided in the README. Any insight into what might be happening here?

@dlitvakb
Copy link
Contributor

dlitvakb commented Oct 1, 2018

Hey @joelip,

You should probably try doing:

dup_entry = source_content_type.entries.create(
  # ... your stuff here ...
)

I'd often advise against using new as it may lead to inconsistent states some times.

Cheers

@joelip
Copy link
Author

joelip commented Oct 1, 2018

@dlitvakb Can you elaborate on the "inconsistent states" more? I'm trying to reduce the number of API calls I need to make via the management API because it's so heavily rate-limited, so adding an extra create call is not something that's ideal for our use-case. Is there any way to use the new method properly that isn't in your documentation already?

@dlitvakb
Copy link
Contributor

dlitvakb commented Oct 2, 2018

Hey @joelip,

It highly depends on what you do in the part of the snippet you omitted.

The main difference between using ::create and ::new, is that with ::create you're directly making the API call with all the values for the fields set from the beginning and returns the fully hydrated entry.
When using ::new instead, you get an unhydrated entry, in which you start appending values to, this can lead to parts of the entry being incomplete at the time of finally making the request when you call #save.

The amount of API calls (if you're not creating entries with multiple locales), should be in either case 1 for each entry, no matter which method you choose to use. If you're using multiple locales, using the ::new method may reduce it to 1 API call, while when using ::create you need a minimum of 2 calls, 1 for the default locale, 1 for all the other locales.

Hope this explains it a little bit better,

Cheers

@joelip
Copy link
Author

joelip commented Oct 3, 2018

It seems like based on the examples in your documentation, that the minimum number of API calls to duplicate an entry would be 4:

  • 1 to retrieve the source entry
  • 1 to retrieve the content type of the entry that you'll be creating
  • 1 to create the entry
  • 1 to update the entry with the fields from the original (since I can't add them until the entry has been created, or do it before the API call is made with ::new)

Am I misunderstanding what's required to create a new entry based on the existing attributes of a source entry?

@dlitvakb
Copy link
Contributor

Hey @joelip,

I don't understand the 4th point there, from what I understand from your use-case you can use 3 (or 2, if you wrap the Content Type ID in an object that responds to #id).

  • Retrieve the source entry
  • Retrieve the destination content type (or use a wrapper for the content type id if you already have it)
class ContentTypeIdWrapper
  attr_reader :id

  def initialize(content_type_id)
    @id = content_type_id
  end
end
  • Create the destination entry
new_entry = client.entries(space_id, environment_id).create(
  content_type: ContentTypeIdWrapper.new(content_type_id),
  # ... all the fields from the old entry you want to duplicate here ...
)

If you're using multiple locales, after the initial creation, you should populate the entry with the additional locales and re-save.

Hope this explains it better,

Cheers

@joelip
Copy link
Author

joelip commented Oct 18, 2018

Interesting, so as long as the object passed to content_type responds to id it works just fine?

@joelip
Copy link
Author

joelip commented Oct 19, 2018

After trying this myself, it looks like this:

new_entry = client.entries(space_id, environment_id).create(
  content_type: ContentTypeIdWrapper.new(content_type_id),
  # ... all the fields from the old entry you want to duplicate here ...
)

Doesn't work unfortunately. It fails in this method, which is called at create time. I am assuming that the client was created using Contentful::Management::Client.new('<management-api-token>') so if that assumption is wrong, then let me know and I can try something else.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants