contentful-management.rb
contentful-management.rb copied to clipboard
Getting save failures for lack of an X-Contentful-Content-Type header
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?
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
@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?
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
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?
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
Interesting, so as long as the object passed to content_type responds to id it works just fine?
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.