From 2d5c157747a56c4f24663972d94578bb0f7913af Mon Sep 17 00:00:00 2001 From: David Litvak Date: Thu, 22 Nov 2018 15:00:16 +0100 Subject: [PATCH] Prevent rehydrating RichText fields --- CHANGELOG.md | 2 + contentful/content_type_field_types.py | 6 +- fixtures/integration/issue-41.yaml | 180 +++++++++++++++++++++++++ tests/client_test.py | 14 ++ 4 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 fixtures/integration/issue-41.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index e739091..016c5db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # CHANGELOG ## Unreleased +### Fixed +* Entries that had already been processed when coercing RichText fields are now properly omitted. [#41](https://github.com/contentful/contentful.py/issues/41) ## v1.11.3 ### Fixed diff --git a/contentful/content_type_field_types.py b/contentful/content_type_field_types.py index f13912c..463fd90 100644 --- a/contentful/content_type_field_types.py +++ b/contentful/content_type_field_types.py @@ -6,7 +6,7 @@ import dateutil.parser from collections import namedtuple from .utils import unicode_class, resource_for_link, unresolvable -from .resource import FieldsResource, Link +from .resource import FieldsResource, Link, Resource """ contentful.content_type_field_types @@ -188,6 +188,10 @@ def _coerce_block(self, value, includes=None, errors=None, resources=None, defau coerced_nodes = {} for index, node in enumerate(value['content']): if node.get('data', None) and node['data'].get('target', None): + # Resource has already been hydrated previously + if isinstance(node['data']['target'], Resource): + continue + link = self._coerce_link( node, includes=includes, diff --git a/fixtures/integration/issue-41.yaml b/fixtures/integration/issue-41.yaml new file mode 100644 index 0000000..0bcf1f5 --- /dev/null +++ b/fixtures/integration/issue-41.yaml @@ -0,0 +1,180 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: [identity] + Authorization: [Bearer 45ba81cc69423fcd2e3f0a4779de29481bb5c11495bc7e14649a996cf984e98e] + Connection: [keep-alive] + Content-Type: [application/vnd.contentful.delivery.v1+json] + User-Agent: [python-requests/2.20.0] + X-Contentful-User-Agent: [sdk contentful.py/1.11.3; platform python/3.6.3; os + macOS/16.7.0;] + method: GET + uri: https://cdn.contentful.com/spaces/fds721b88p6b/environments/master/content_types + response: + body: {string: "{\n \"sys\": {\n \"type\": \"Array\"\n },\n \"total\": 2,\n + \ \"skip\": 0,\n \"limit\": 100,\n \"items\": [\n {\n \"sys\": {\n + \ \"space\": {\n \"sys\": {\n \"type\": \"Link\",\n + \ \"linkType\": \"Space\",\n \"id\": \"fds721b88p6b\"\n + \ }\n },\n \"id\": \"parent\",\n \"type\": \"ContentType\",\n + \ \"createdAt\": \"2018-11-22T13:46:07.272Z\",\n \"updatedAt\": + \"2018-11-22T13:46:07.272Z\",\n \"environment\": {\n \"sys\": + {\n \"id\": \"master\",\n \"type\": \"Link\",\n \"linkType\": + \"Environment\"\n }\n },\n \"revision\": 1\n },\n + \ \"displayField\": \"title\",\n \"name\": \"Parent\",\n \"description\": + \"\",\n \"fields\": [\n {\n \"id\": \"title\",\n \"name\": + \"Title\",\n \"type\": \"Symbol\",\n \"localized\": false,\n + \ \"required\": false,\n \"disabled\": false,\n \"omitted\": + false\n },\n {\n \"id\": \"children\",\n \"name\": + \"Children\",\n \"type\": \"Array\",\n \"localized\": false,\n + \ \"required\": false,\n \"disabled\": false,\n \"omitted\": + false,\n \"items\": {\n \"type\": \"Link\",\n \"validations\": + [],\n \"linkType\": \"Entry\"\n }\n }\n ]\n + \ },\n {\n \"sys\": {\n \"space\": {\n \"sys\": + {\n \"type\": \"Link\",\n \"linkType\": \"Space\",\n + \ \"id\": \"fds721b88p6b\"\n }\n },\n \"id\": + \"child\",\n \"type\": \"ContentType\",\n \"createdAt\": \"2018-11-22T13:48:47.928Z\",\n + \ \"updatedAt\": \"2018-11-22T13:48:47.928Z\",\n \"environment\": + {\n \"sys\": {\n \"id\": \"master\",\n \"type\": + \"Link\",\n \"linkType\": \"Environment\"\n }\n },\n + \ \"revision\": 1\n },\n \"displayField\": \"title\",\n \"name\": + \"Child\",\n \"description\": \"\",\n \"fields\": [\n {\n + \ \"id\": \"title\",\n \"name\": \"Title\",\n \"type\": + \"Symbol\",\n \"localized\": false,\n \"required\": false,\n + \ \"disabled\": false,\n \"omitted\": false\n },\n + \ {\n \"id\": \"body\",\n \"name\": \"Body\",\n \"type\": + \"RichText\",\n \"localized\": false,\n \"required\": false,\n + \ \"disabled\": false,\n \"omitted\": false\n }\n + \ ]\n }\n ]\n}\n"} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Headers: ['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'] + Access-Control-Allow-Methods: ['GET,HEAD,OPTIONS'] + Access-Control-Allow-Origin: ['*'] + Access-Control-Expose-Headers: [Etag] + Access-Control-Max-Age: ['86400'] + Age: ['0'] + Cache-Control: [max-age=0] + Connection: [keep-alive] + Content-Length: ['2357'] + Content-Type: [application/vnd.contentful.delivery.v1+json] + Contentful-Api: [cda_cached] + Date: ['Thu, 22 Nov 2018 13:58:02 GMT'] + ETag: [W/"c2b8adf3ad96fc1cb1b7d1d679e17d7c"] + Server: [Contentful] + Vary: [Accept-Encoding] + Via: [1.1 varnish] + X-Cache: [MISS] + X-Cache-Hits: ['0'] + X-Content-Type-Options: [nosniff] + X-Contentful-Region: [us-east-1] + X-Contentful-Request-Id: [1ad1c6ab1ba098f56907d007cf499365] + X-Served-By: [cache-fra19144-FRA] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: [identity] + Authorization: [Bearer 45ba81cc69423fcd2e3f0a4779de29481bb5c11495bc7e14649a996cf984e98e] + Connection: [keep-alive] + Content-Type: [application/vnd.contentful.delivery.v1+json] + User-Agent: [python-requests/2.20.0] + X-Contentful-User-Agent: [sdk contentful.py/1.11.3; platform python/3.6.3; os + macOS/16.7.0;] + method: GET + uri: https://cdn.contentful.com/spaces/fds721b88p6b/environments/master/entries?sys.id=1tBAu0wP9qAQEg6qCqMics + response: + body: {string: "{\n \"sys\": {\n \"type\": \"Array\"\n },\n \"total\": 1,\n + \ \"skip\": 0,\n \"limit\": 100,\n \"items\": [\n {\n \"sys\": {\n + \ \"space\": {\n \"sys\": {\n \"type\": \"Link\",\n + \ \"linkType\": \"Space\",\n \"id\": \"fds721b88p6b\"\n + \ }\n },\n \"id\": \"1tBAu0wP9qAQEg6qCqMics\",\n \"type\": + \"Entry\",\n \"createdAt\": \"2018-11-22T13:49:04.334Z\",\n \"updatedAt\": + \"2018-11-22T13:50:01.448Z\",\n \"environment\": {\n \"sys\": + {\n \"id\": \"master\",\n \"type\": \"Link\",\n \"linkType\": + \"Environment\"\n }\n },\n \"revision\": 2,\n \"contentType\": + {\n \"sys\": {\n \"type\": \"Link\",\n \"linkType\": + \"ContentType\",\n \"id\": \"parent\"\n }\n },\n + \ \"locale\": \"en-US\"\n },\n \"fields\": {\n \"title\": + \"Parent\",\n \"children\": [\n {\n \"sys\": {\n + \ \"type\": \"Link\",\n \"linkType\": \"Entry\",\n + \ \"id\": \"2RJFAOGr1KeCQuYiASOY82\"\n }\n },\n + \ {\n \"sys\": {\n \"type\": \"Link\",\n \"linkType\": + \"Entry\",\n \"id\": \"2RJFAOGr1KeCQuYiASOY82\"\n }\n + \ },\n {\n \"sys\": {\n \"type\": + \"Link\",\n \"linkType\": \"Entry\",\n \"id\": \"5YcYYsGv3q4Oq68WQcOiAA\"\n + \ }\n }\n ]\n }\n }\n ],\n \"includes\": + {\n \"Entry\": [\n {\n \"sys\": {\n \"space\": {\n + \ \"sys\": {\n \"type\": \"Link\",\n \"linkType\": + \"Space\",\n \"id\": \"fds721b88p6b\"\n }\n },\n + \ \"id\": \"2RJFAOGr1KeCQuYiASOY82\",\n \"type\": \"Entry\",\n + \ \"createdAt\": \"2018-11-22T13:49:38.406Z\",\n \"updatedAt\": + \"2018-11-22T13:49:38.406Z\",\n \"environment\": {\n \"sys\": + {\n \"id\": \"master\",\n \"type\": \"Link\",\n + \ \"linkType\": \"Environment\"\n }\n },\n + \ \"revision\": 1,\n \"contentType\": {\n \"sys\": + {\n \"type\": \"Link\",\n \"linkType\": \"ContentType\",\n + \ \"id\": \"child\"\n }\n },\n \"locale\": + \"en-US\"\n },\n \"fields\": {\n \"title\": \"Child\",\n + \ \"body\": {\n \"data\": {},\n \"content\": + [\n {\n \"data\": {},\n \"content\": + [\n {\n \"data\": {},\n \"marks\": + [],\n \"value\": \"Some stuff here\",\n \"nodeType\": + \"text\"\n }\n ],\n \"nodeType\": + \"paragraph\"\n },\n {\n \"data\": + {\n \"target\": {\n \"sys\": {\n \"id\": + \"3VDkQdyZ9YYaEceWKgGcAG\",\n \"type\": \"Link\",\n \"linkType\": + \"Entry\"\n }\n }\n },\n + \ \"content\": [],\n \"nodeType\": \"embedded-entry-block\"\n + \ },\n {\n \"data\": {},\n \"content\": + [\n {\n \"data\": {},\n \"marks\": + [],\n \"value\": \"\",\n \"nodeType\": + \"text\"\n }\n ],\n \"nodeType\": + \"paragraph\"\n }\n ],\n \"nodeType\": + \"document\"\n }\n }\n },\n {\n \"sys\": + {\n \"space\": {\n \"sys\": {\n \"type\": + \"Link\",\n \"linkType\": \"Space\",\n \"id\": \"fds721b88p6b\"\n + \ }\n },\n \"id\": \"5YcYYsGv3q4Oq68WQcOiAA\",\n + \ \"type\": \"Entry\",\n \"createdAt\": \"2018-11-22T13:49:58.353Z\",\n + \ \"updatedAt\": \"2018-11-22T13:49:58.353Z\",\n \"environment\": + {\n \"sys\": {\n \"id\": \"master\",\n \"type\": + \"Link\",\n \"linkType\": \"Environment\"\n }\n },\n + \ \"revision\": 1,\n \"contentType\": {\n \"sys\": + {\n \"type\": \"Link\",\n \"linkType\": \"ContentType\",\n + \ \"id\": \"child\"\n }\n },\n \"locale\": + \"en-US\"\n },\n \"fields\": {\n \"title\": \"Other + Child\",\n \"body\": {\n \"data\": {},\n \"content\": + [\n {\n \"data\": {},\n \"content\": + [\n {\n \"data\": {},\n \"marks\": + [],\n \"value\": \"Some other stuff here\",\n \"nodeType\": + \"text\"\n }\n ],\n \"nodeType\": + \"paragraph\"\n }\n ],\n \"nodeType\": + \"document\"\n }\n }\n }\n ]\n }\n}\n"} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Headers: ['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'] + Access-Control-Allow-Methods: ['GET,HEAD,OPTIONS'] + Access-Control-Allow-Origin: ['*'] + Access-Control-Expose-Headers: [Etag] + Access-Control-Max-Age: ['86400'] + Age: ['0'] + Cache-Control: [max-age=0] + Connection: [keep-alive] + Content-Length: ['4825'] + Content-Type: [application/vnd.contentful.delivery.v1+json] + Contentful-Api: [cda_cached] + Date: ['Thu, 22 Nov 2018 13:58:02 GMT'] + ETag: [W/"c5d3dbad3fcadc2086a73db56968fd73"] + Server: [Contentful] + Vary: [Accept-Encoding] + Via: [1.1 varnish] + X-Cache: [MISS] + X-Cache-Hits: ['0'] + X-Content-Type-Options: [nosniff] + X-Contentful-Region: [us-east-1] + X-Contentful-Request-Id: [1321ee31210755c76387cdeb0ecc23ba] + X-Served-By: [cache-fra19147-FRA] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/client_test.py b/tests/client_test.py index af11140..841ca11 100644 --- a/tests/client_test.py +++ b/tests/client_test.py @@ -444,3 +444,17 @@ def test_rich_text_field_with_embeds_in_lists(self): self.assertEqual(entry.body['content'][4]['nodeType'], 'ordered-list') self.assertEqual(str(entry.body['content'][4]['content'][2]['content'][0]['data']['target']), "") + + @vcr.use_cassette('fixtures/integration/issue-41.yaml') + def test_rich_text_fields_should_not_get_hydrated_twice(self): + client = Client( + 'fds721b88p6b', + '45ba81cc69423fcd2e3f0a4779de29481bb5c11495bc7e14649a996cf984e98e', + gzip_encoded=False + ) + + entry = client.entry('1tBAu0wP9qAQEg6qCqMics') + + # Not failing is already a success + self.assertEqual(str(entry.children[0]), str(entry.children[1])) + self.assertEqual(str(entry.children[0].body), str(entry.children[1].body))