diff --git a/tests/test_api/betamax/TestHelpCenterCreateArticle.test_create_article_false_notify_subscribers-false.json b/tests/test_api/betamax/TestHelpCenterCreateArticle.test_create_article_false_notify_subscribers-false.json new file mode 100644 index 0000000..08f441b --- /dev/null +++ b/tests/test_api/betamax/TestHelpCenterCreateArticle.test_create_article_false_notify_subscribers-false.json @@ -0,0 +1,131 @@ +{ + "http_interactions": [ + { + "recorded_at": "2024-09-06T23:09:57", + "request": { + "body": { + "encoding": "utf-8", + "string": "{\"article\": {\"body\": \"

Article html content body does not notifies subscribers

\", \"created_at\": \"2024-09-06\", \"locale\": \"en-us\", \"name\": \"Notify Off Article Name\", \"permission_group_id\": 33152086795411, \"section_id\": 33152317085843, \"title\": \"Notify Off Article Name\", \"updated_at\": \"2024-09-06\", \"user_segment_id\": 33152086785683}, \"notify_subscribers\": false}" + }, + "headers": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Authorization": [ + "Basic " + ], + "Connection": [ + "keep-alive" + ], + "Content-Length": [ + "366" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "Zenpy/2.0.51" + ] + }, + "method": "POST", + "uri": "https://d3v-zenpydev.zendesk.com/api/v2/help_center/sections/33152317085843/articles.json" + }, + "response": { + "body": { + "encoding": "utf-8", + "string": "{\"article\":{\"id\":33153706537619,\"url\":\"https://d3v-zenpydev.zendesk.com/api/v2/help_center/en-us/articles/33153706537619-Notify-Off-Article-Name.json\",\"html_url\":\"https://d3v-zenpydev.zendesk.com/hc/en-us/articles/33153706537619-Notify-Off-Article-Name\",\"author_id\":26155588908179,\"comments_disabled\":false,\"content_tag_ids\":[],\"label_names\":[],\"draft\":false,\"promoted\":false,\"position\":0,\"vote_sum\":0,\"vote_count\":0,\"section_id\":33152317085843,\"created_at\":\"2024-09-06T23:09:57Z\",\"updated_at\":\"2024-09-06T23:09:57Z\",\"edited_at\":\"2024-09-06T23:09:57Z\",\"name\":\"Notify Off Article Name\",\"title\":\"Notify Off Article Name\",\"body\":\"\\u003cp\\u003eArticle html content body does not notifies subscribers\\u003c/p\\u003e\",\"source_locale\":\"en-us\",\"locale\":\"en-us\",\"outdated\":false,\"outdated_locales\":[],\"permission_group_id\":33152086795411,\"user_segment_id\":33152086785683,\"user_segment_ids\":[33152086785683]}}" + }, + "headers": { + "CF-Cache-Status": [ + "DYNAMIC" + ], + "CF-RAY": [ + "8bf2212d0edccf1a-SJC" + ], + "Connection": [ + "keep-alive" + ], + "Content-Type": [ + "application/json; charset=utf-8" + ], + "Date": [ + "Fri, 06 Sep 2024 23:09:57 GMT" + ], + "NEL": [ + "{\"success_fraction\":0.01,\"report_to\":\"cf-nel\",\"max_age\":604800}" + ], + "Protocol": [ + "HTTP/1.1 always" + ], + "Report-To": [ + "{\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=XeS0RBtAD%2Bym7E%2F1awh%2Bdv0UDeRalu4ra2FvK3Upyc9dskGV1ZtAOz7TDS4FAvsbUVy%2BH4QI1O3CY%2BT5KIVb%2F1AJ32VyiB0EuD1KIgUsKfbgeNQm3maIB9a7cwaIFI8TBF3Au89X\"}],\"group\":\"cf-nel\",\"max_age\":604800}" + ], + "Server": [ + "cloudflare" + ], + "Set-Cookie": [ + "__cfruid=9911ad9bc786469bf7b1df6300c1a0b168342663-1725664197; path=/; domain=.d3v-zenpydev.zendesk.com; HttpOnly; Secure; SameSite=None", + "_cfuvid=biv.vXIFmdw.24tKV.QaY5nTPMPMBDQE7hhRB0aDw24-1725664197283-0.0.1.1-604800000; path=/; domain=.d3v-zenpydev.zendesk.com; HttpOnly; Secure; SameSite=None" + ], + "Transfer-Encoding": [ + "chunked" + ], + "X-Request-ID": [ + "8bf2212d0edccf1a-SJC" + ], + "X-Zendesk-API-Gateway": [ + "yes" + ], + "X-Zendesk-Zorg": [ + "yes" + ], + "cache-control": [ + "max-age=0, private, must-revalidate" + ], + "etag": [ + "W/\"16471f092a9e0c05946ecc98d5867add\"" + ], + "strict-transport-security": [ + "max-age=31536000; includeSubDomains" + ], + "x-frame-options": [ + "SAMEORIGIN" + ], + "x-rate-limit": [ + "700" + ], + "x-rate-limit-remaining": [ + "699" + ], + "x-runtime": [ + "0.242352" + ], + "x-ua-compatible": [ + "IE=edge" + ], + "x-xss-protection": [ + "1; mode=block" + ], + "x-zendesk-api-version": [ + "v2" + ], + "x-zendesk-origin-server": [ + "app-server-cd59c7886-5wv7h" + ], + "x-zendesk-processed-host-header": [ + "d3v-zenpydev.zendesk.com" + ] + }, + "status": { + "code": 201, + "message": "Created" + }, + "url": "https://d3v-zenpydev.zendesk.com/api/v2/help_center/sections/33152317085843/articles.json" + } + } + ], + "recorded_with": "betamax/0.8.1" +} \ No newline at end of file diff --git a/tests/test_api/betamax/TestHelpCenterCreateArticle.test_create_article_with_notify_subscribers-true.json b/tests/test_api/betamax/TestHelpCenterCreateArticle.test_create_article_with_notify_subscribers-true.json new file mode 100644 index 0000000..4eaeaa6 --- /dev/null +++ b/tests/test_api/betamax/TestHelpCenterCreateArticle.test_create_article_with_notify_subscribers-true.json @@ -0,0 +1,130 @@ +{ + "http_interactions": [ + { + "recorded_at": "2024-09-06T23:09:57", + "request": { + "body": { + "encoding": "utf-8", + "string": "{\"article\": {\"body\": \"

Article html content body notifies subscribers

\", \"created_at\": \"2024-09-06\", \"locale\": \"en-us\", \"name\": \"Article html content body notifies subscribers\", \"permission_group_id\": 33152086795411, \"section_id\": 33152317085843, \"title\": \"Article html content body notifies subscribers\", \"updated_at\": \"2024-09-06\", \"user_segment_id\": 33152086785683}, \"notify_subscribers\": true}" + }, + "headers": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Authorization": [ + "Basic " + ], + "Connection": [ + "keep-alive" + ], + "Content-Length": [ + "402" + ], + "Content-Type": [ + "application/json" + ], + "Cookie": [ + "__cfruid=9911ad9bc786469bf7b1df6300c1a0b168342663-1725664197; _cfuvid=biv.vXIFmdw.24tKV.QaY5nTPMPMBDQE7hhRB0aDw24-1725664197283-0.0.1.1-604800000" + ], + "User-Agent": [ + "Zenpy/2.0.51" + ] + }, + "method": "POST", + "uri": "https://d3v-zenpydev.zendesk.com/api/v2/help_center/sections/33152317085843/articles.json" + }, + "response": { + "body": { + "encoding": "utf-8", + "string": "{\"article\":{\"id\":33153695249043,\"url\":\"https://d3v-zenpydev.zendesk.com/api/v2/help_center/en-us/articles/33153695249043-Article-html-content-body-notifies-subscribers.json\",\"html_url\":\"https://d3v-zenpydev.zendesk.com/hc/en-us/articles/33153695249043-Article-html-content-body-notifies-subscribers\",\"author_id\":26155588908179,\"comments_disabled\":false,\"content_tag_ids\":[],\"label_names\":[],\"draft\":false,\"promoted\":false,\"position\":0,\"vote_sum\":0,\"vote_count\":0,\"section_id\":33152317085843,\"created_at\":\"2024-09-06T23:09:57Z\",\"updated_at\":\"2024-09-06T23:09:57Z\",\"edited_at\":\"2024-09-06T23:09:57Z\",\"name\":\"Article html content body notifies subscribers\",\"title\":\"Article html content body notifies subscribers\",\"body\":\"\\u003cp\\u003eArticle html content body notifies subscribers\\u003c/p\\u003e\",\"source_locale\":\"en-us\",\"locale\":\"en-us\",\"outdated\":false,\"outdated_locales\":[],\"permission_group_id\":33152086795411,\"user_segment_id\":33152086785683,\"user_segment_ids\":[33152086785683]}}" + }, + "headers": { + "CF-Cache-Status": [ + "DYNAMIC" + ], + "CF-RAY": [ + "8bf221312d90cf1a-SJC" + ], + "Connection": [ + "keep-alive" + ], + "Content-Type": [ + "application/json; charset=utf-8" + ], + "Date": [ + "Fri, 06 Sep 2024 23:09:58 GMT" + ], + "NEL": [ + "{\"success_fraction\":0.01,\"report_to\":\"cf-nel\",\"max_age\":604800}" + ], + "Protocol": [ + "HTTP/1.1 always" + ], + "Report-To": [ + "{\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=cjzCH4jAajCmuhYTa9vWZnnnnMTo04ntNP7Hfhqmg2IHIMU2xX23G%2BYdPzhISLs3%2F5fw4REFAAPOP9t9s6A0RZugWbTVSI4C7r%2BPjPKeZY7kymJA51%2BekfCWpkhZ5N3j1%2BX6QEnO\"}],\"group\":\"cf-nel\",\"max_age\":604800}" + ], + "Server": [ + "cloudflare" + ], + "Transfer-Encoding": [ + "chunked" + ], + "X-Request-ID": [ + "8bf221312d90cf1a-SJC" + ], + "X-Zendesk-API-Gateway": [ + "yes" + ], + "X-Zendesk-Zorg": [ + "yes" + ], + "cache-control": [ + "max-age=0, private, must-revalidate" + ], + "etag": [ + "W/\"9c9aef545ada4618cf6c5f1cbcc83c70\"" + ], + "strict-transport-security": [ + "max-age=31536000; includeSubDomains" + ], + "x-frame-options": [ + "SAMEORIGIN" + ], + "x-rate-limit": [ + "700" + ], + "x-rate-limit-remaining": [ + "698" + ], + "x-runtime": [ + "0.297770" + ], + "x-ua-compatible": [ + "IE=edge" + ], + "x-xss-protection": [ + "1; mode=block" + ], + "x-zendesk-api-version": [ + "v2" + ], + "x-zendesk-origin-server": [ + "app-server-cd59c7886-b49cd" + ], + "x-zendesk-processed-host-header": [ + "d3v-zenpydev.zendesk.com" + ] + }, + "status": { + "code": 201, + "message": "Created" + }, + "url": "https://d3v-zenpydev.zendesk.com/api/v2/help_center/sections/33152317085843/articles.json" + } + } + ], + "recorded_with": "betamax/0.8.1" +} \ No newline at end of file diff --git a/tests/test_api/betamax/TestHelpCenterCreateArticle.test_create_article_without_notify_subscribers-None.json b/tests/test_api/betamax/TestHelpCenterCreateArticle.test_create_article_without_notify_subscribers-None.json new file mode 100644 index 0000000..b43226d --- /dev/null +++ b/tests/test_api/betamax/TestHelpCenterCreateArticle.test_create_article_without_notify_subscribers-None.json @@ -0,0 +1,130 @@ +{ + "http_interactions": [ + { + "recorded_at": "2024-09-06T23:09:58", + "request": { + "body": { + "encoding": "utf-8", + "string": "{\"article\": {\"body\": \"

Article html content body does not notifies subscribers

\", \"created_at\": \"2024-09-06\", \"locale\": \"en-us\", \"name\": \"Notify None Article Name\", \"permission_group_id\": 33152086795411, \"section_id\": 33152317085843, \"title\": \"Notify None Article Name\", \"updated_at\": \"2024-09-06\", \"user_segment_id\": 33152086785683}}" + }, + "headers": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Authorization": [ + "Basic " + ], + "Connection": [ + "keep-alive" + ], + "Content-Length": [ + "339" + ], + "Content-Type": [ + "application/json" + ], + "Cookie": [ + "__cfruid=9911ad9bc786469bf7b1df6300c1a0b168342663-1725664197; _cfuvid=biv.vXIFmdw.24tKV.QaY5nTPMPMBDQE7hhRB0aDw24-1725664197283-0.0.1.1-604800000" + ], + "User-Agent": [ + "Zenpy/2.0.51" + ] + }, + "method": "POST", + "uri": "https://d3v-zenpydev.zendesk.com/api/v2/help_center/sections/33152317085843/articles.json" + }, + "response": { + "body": { + "encoding": "utf-8", + "string": "{\"article\":{\"id\":33153710247315,\"url\":\"https://d3v-zenpydev.zendesk.com/api/v2/help_center/en-us/articles/33153710247315-Notify-None-Article-Name.json\",\"html_url\":\"https://d3v-zenpydev.zendesk.com/hc/en-us/articles/33153710247315-Notify-None-Article-Name\",\"author_id\":26155588908179,\"comments_disabled\":false,\"content_tag_ids\":[],\"label_names\":[],\"draft\":false,\"promoted\":false,\"position\":0,\"vote_sum\":0,\"vote_count\":0,\"section_id\":33152317085843,\"created_at\":\"2024-09-06T23:09:58Z\",\"updated_at\":\"2024-09-06T23:09:58Z\",\"edited_at\":\"2024-09-06T23:09:58Z\",\"name\":\"Notify None Article Name\",\"title\":\"Notify None Article Name\",\"body\":\"\\u003cp\\u003eArticle html content body does not notifies subscribers\\u003c/p\\u003e\",\"source_locale\":\"en-us\",\"locale\":\"en-us\",\"outdated\":false,\"outdated_locales\":[],\"permission_group_id\":33152086795411,\"user_segment_id\":33152086785683,\"user_segment_ids\":[33152086785683]}}" + }, + "headers": { + "CF-Cache-Status": [ + "DYNAMIC" + ], + "CF-RAY": [ + "8bf22135bdf7cf1a-SJC" + ], + "Connection": [ + "keep-alive" + ], + "Content-Type": [ + "application/json; charset=utf-8" + ], + "Date": [ + "Fri, 06 Sep 2024 23:09:58 GMT" + ], + "NEL": [ + "{\"success_fraction\":0.01,\"report_to\":\"cf-nel\",\"max_age\":604800}" + ], + "Protocol": [ + "HTTP/1.1 always" + ], + "Report-To": [ + "{\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=0fVVUi1YUmMDgjHXRsUFKs%2BZG2zSNUScZ2RLWl5lsX4u8WZehmUpaZLrfytf0HbwebV3XHag65%2FZBi9WeJ1ELGXH4lQSGkwF2ugBdD58NjkWPB%2BLnfP2lAojiCTozOkUoyz3Ij9z\"}],\"group\":\"cf-nel\",\"max_age\":604800}" + ], + "Server": [ + "cloudflare" + ], + "Transfer-Encoding": [ + "chunked" + ], + "X-Request-ID": [ + "8bf22135bdf7cf1a-SJC" + ], + "X-Zendesk-API-Gateway": [ + "yes" + ], + "X-Zendesk-Zorg": [ + "yes" + ], + "cache-control": [ + "max-age=0, private, must-revalidate" + ], + "etag": [ + "W/\"567b3dbd807d32c9ef503890d9ab68dc\"" + ], + "strict-transport-security": [ + "max-age=31536000; includeSubDomains" + ], + "x-frame-options": [ + "SAMEORIGIN" + ], + "x-rate-limit": [ + "700" + ], + "x-rate-limit-remaining": [ + "697" + ], + "x-runtime": [ + "0.242651" + ], + "x-ua-compatible": [ + "IE=edge" + ], + "x-xss-protection": [ + "1; mode=block" + ], + "x-zendesk-api-version": [ + "v2" + ], + "x-zendesk-origin-server": [ + "app-server-cd59c7886-5cngk" + ], + "x-zendesk-processed-host-header": [ + "d3v-zenpydev.zendesk.com" + ] + }, + "status": { + "code": 201, + "message": "Created" + }, + "url": "https://d3v-zenpydev.zendesk.com/api/v2/help_center/sections/33152317085843/articles.json" + } + } + ], + "recorded_with": "betamax/0.8.1" +} \ No newline at end of file diff --git a/tests/test_api/test_create_update_delete_helpcentre.py b/tests/test_api/test_create_update_delete_helpcentre.py index 0502d26..7061fbe 100644 --- a/tests/test_api/test_create_update_delete_helpcentre.py +++ b/tests/test_api/test_create_update_delete_helpcentre.py @@ -1,10 +1,11 @@ -from zenpy.lib.api_objects.help_centre_objects import Category, Topic, ContentTag +from zenpy.lib.api_objects.help_centre_objects import Category, Topic, ContentTag, Article from test_api.fixtures import ( SingleUpdateApiTestCase, SingleCreateApiTestCase, SingleDeleteApiTestCase, + ZenpyApiTestCase ) - +from datetime import datetime class TestContentTagCreateUpdateDelete( SingleUpdateApiTestCase, SingleCreateApiTestCase, SingleDeleteApiTestCase @@ -50,3 +51,69 @@ class TestCommunityTopicCreateUpdateDelete( ZenpyType = Topic object_kwargs = dict(name="test", description="this is a Topic test") api_name = "help_center.topics" + + +class TestHelpCenterCreateArticle(ZenpyApiTestCase): + __test__ = True + def test_create_article_with_notify_subscribers(self): + cassette_name = "{}-true".format(self.generate_cassette_name()) + with self.recorder.use_cassette(cassette_name, serialize_with="prettyjson"): + section_id = 33152317085843 # In my test instance + new_article = self.zenpy_client.help_center.articles.create( + section=section_id, + article=Article( + name="Article html content body notifies subscribers", + body="

Article html content body notifies subscribers

", + locale="en-us", + title="Article html content body notifies subscribers", + user_segment_id=33152086785683, + permission_group_id=33152086795411, + section_id=section_id, + created_at=datetime.now(), + updated_at=datetime.now() + ), + notify_subscribers=True + ) + self.assertTrue((new_article is not None) and (new_article.name == "Article html content body notifies subscribers")) + + + def test_create_article_false_notify_subscribers(self): + cassette_name = "{}-false".format(self.generate_cassette_name()) + with self.recorder.use_cassette(cassette_name, serialize_with="prettyjson"): + section_id = 33152317085843 # In my test instance + new_article = self.zenpy_client.help_center.articles.create( + section=section_id, + article=Article( + name="Notify Off Article Name", + body="

Article html content body does not notifies subscribers

", + locale="en-us", + title="Notify Off Article Name", + user_segment_id=33152086785683, + permission_group_id=33152086795411, + section_id=section_id, + created_at=datetime.now(), + updated_at=datetime.now() + ), + notify_subscribers=False + ) + self.assertTrue((new_article is not None) and (new_article.name == "Notify Off Article Name")) + + def test_create_article_without_notify_subscribers(self): + cassette_name = "{}-None".format(self.generate_cassette_name()) + with self.recorder.use_cassette(cassette_name, serialize_with="prettyjson"): + section_id = 33152317085843 # In my test instance + new_article = self.zenpy_client.help_center.articles.create( + section=section_id, + article=Article( + name="Notify None Article Name", + body="

Article html content body does not notifies subscribers

", + locale="en-us", + title="Notify None Article Name", + user_segment_id=33152086785683, + permission_group_id=33152086795411, + section_id=section_id, + created_at=datetime.now(), + updated_at=datetime.now() + ), + ) + self.assertTrue((new_article is not None) and (new_article.name == "Notify None Article Name")) diff --git a/zenpy/lib/api.py b/zenpy/lib/api.py index 0f29b3a..0de75f1 100644 --- a/zenpy/lib/api.py +++ b/zenpy/lib/api.py @@ -25,7 +25,7 @@ ChatObjectMapping, HelpCentreObjectMapping, \ TalkObjectMapping, CallPEObjectMapping from zenpy.lib.request import AccessPolicyRequest, AccountRequest, AgentRequest, \ - CRUDRequest, ChatApiRequest, HelpCentreRequest, \ + CRUDRequest, ArticleCRUDRequest, ChatApiRequest, HelpCentreRequest, \ HelpdeskAttachmentRequest, HelpdeskCommentRequest, \ OrganizationFieldReorderRequest, PostCommentRequest, \ RateRequest, SatisfactionRatingRequest, SubscriptionRequest, \ @@ -2296,7 +2296,7 @@ def vote_comment_down(self, help_centre_object, comment): class ArticleApi(HelpCentreApiBase, TranslationApi, SubscriptionApi, VoteApi, VoteCommentApi, IncrementalApi): @extract_id(Section) - def create(self, section, article): + def create(self, section, article, notify_subscribers=None): """ Create (POST) an Article - See: Zendesk API `Reference `__. @@ -2304,7 +2304,7 @@ def create(self, section, article): :param section: Section ID or object :param article: Article to create """ - return CRUDRequest(self).post(article, create=True, id=section) + return ArticleCRUDRequest(self).post(article, create=True, id=section, notify_subscribers=notify_subscribers) def update(self, article): """ diff --git a/zenpy/lib/request.py b/zenpy/lib/request.py index 9039e23..502d68f 100644 --- a/zenpy/lib/request.py +++ b/zenpy/lib/request.py @@ -33,6 +33,13 @@ def delete(self, api_objects, *args, **kwargs): class BaseZendeskRequest(RequestHandler): """ + Classes can decorate the payload right before it goes out, and RIGHT before the URL is built. + This is because some simple kwargs need placement in the payload body. + """ + + def preflight_payload(self, payload, **kwargs): + return kwargs + """ Base class for Zendesk requests. Provides a few handy methods. """ def build_payload(self, api_objects): @@ -78,6 +85,7 @@ def post(self, api_objects, *args, **kwargs): endpoint = self.api.endpoint payload = self.build_payload(api_objects) + kwargs = self.preflight_payload(payload, **kwargs) url = self.api._build_url(endpoint(*args, **kwargs)) return self.api._post(url, payload) @@ -118,6 +126,14 @@ def delete(self, return response +class ArticleCRUDRequest(CRUDRequest): + + def preflight_payload(self, payload, **kwargs): + notify_subscribers = kwargs.pop('notify_subscribers', None) + if notify_subscribers is not None: + payload['notify_subscribers'] = bool(notify_subscribers) + return kwargs + class SuspendedTicketRequest(BaseZendeskRequest): """ Handle updating and deleting SuspendedTickets.