Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit 6960cba

Browse files
authored
Merge pull request #196 from cloudant/186-support-show-functions
Added support for show functions
2 parents eb859cb + 22c15e4 commit 6960cba

File tree

6 files changed

+429
-19
lines changed

6 files changed

+429
-19
lines changed

CHANGES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- [NEW] Added support for Cloudant Search execution.
44
- [NEW] Added support for Cloudant Search index management.
55
- [NEW] Added support for managing and querying list functions.
6+
- [NEW] Added support for managing and querying show functions.
67
- [NEW] Added ``rewrites`` accessor property for URL rewriting.
78
- [NEW] Added ``st_indexes`` accessor property for Cloudant Geospatial indexes.
89
- [NEW] Added support for DesignDocument ``_info`` and ``_search_info`` endpoints.

src/cloudant/database.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,43 @@ def get_list_function_result(self, ddoc_id, list_name, view_name, **kwargs):
805805
**kwargs)
806806
return resp.text
807807

808+
def get_show_function_result(self, ddoc_id, show_name, doc_id):
809+
"""
810+
Retrieves a formatted document from the specified database
811+
based on the show function provided. Show functions, for example,
812+
are used when you want to access Cloudant directly from a browser,
813+
and need data to be returned in a different format, such as HTML.
814+
815+
For example:
816+
817+
.. code-block:: python
818+
819+
# Assuming that 'view001' exists as part of the
820+
# 'ddoc001' design document in the remote database...
821+
# Retrieve a formatted 'doc001' document where the show function is 'show001'
822+
resp = db.get_show_function_result('ddoc001', 'show001', 'doc001')
823+
for row in resp['rows']:
824+
# Process data (in text format).
825+
826+
For more detail on show functions, refer to the
827+
`Cloudant documentation <https://docs.cloudant.com/
828+
design_documents.html#show-functions>`_.
829+
830+
:param str ddoc_id: Design document id used to get the result.
831+
:param str show_name: Name used in part to identify the
832+
show function.
833+
:param str doc_id: The ID of the document to show.
834+
835+
:return: Formatted document result data in text format
836+
"""
837+
ddoc = DesignDocument(self, ddoc_id)
838+
headers = {'Content-Type': 'application/json'}
839+
resp = get_docs(self.r_session,
840+
'/'.join([ddoc.document_url, '_show', show_name, doc_id]),
841+
self.client.encoder,
842+
headers)
843+
return resp.text
844+
808845
class CloudantDatabase(CouchDatabase):
809846
"""
810847
Encapsulates a Cloudant database. A CloudantDatabase object is

src/cloudant/design_document.py

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def __init__(self, database, document_id=None):
4444
if document_id and not document_id.startswith('_design/'):
4545
document_id = '_design/{0}'.format(document_id)
4646
super(DesignDocument, self).__init__(database, document_id)
47-
self._nested_object_names = frozenset(['views', 'indexes', 'lists'])
47+
self._nested_object_names = frozenset(['views', 'indexes', 'lists', 'shows'])
4848
for prop in self._nested_object_names:
4949
self.setdefault(prop, dict())
5050

@@ -98,6 +98,17 @@ def lists(self):
9898
"""
9999
return self.get('lists')
100100

101+
@property
102+
def shows(self):
103+
"""
104+
Provides an accessor property to the shows dictionary in the
105+
locally cached DesignDocument.
106+
107+
:returns: Dictionary containing show names and functions
108+
as key/value
109+
"""
110+
return self.get('shows')
111+
101112
@property
102113
def rewrites(self):
103114
"""
@@ -215,6 +226,21 @@ def add_list_function(self, list_name, list_func):
215226

216227
self.lists.__setitem__(list_name, codify(list_func))
217228

229+
def add_show_function(self, show_name, show_func):
230+
"""
231+
Appends a show function to the locally cached DesignDocument
232+
shows dictionary.
233+
234+
:param show_name: Name used to identify the show function.
235+
:param show_func: Javascript show function.
236+
"""
237+
if self.get_show_function(show_name) is not None:
238+
msg = ('A show function with name {0} already exists in this design doc'
239+
.format(show_name))
240+
raise CloudantArgumentError(msg)
241+
242+
self.shows.__setitem__(show_name, show_func)
243+
218244
def update_view(self, view_name, map_func, reduce_func=None, **kwargs):
219245
"""
220246
Modifies/overwrites an existing MapReduce view definition in the
@@ -276,6 +302,21 @@ def update_list_function(self, list_name, list_func):
276302

277303
self.lists.__setitem__(list_name, codify(list_func))
278304

305+
def update_show_function(self, show_name, show_func):
306+
"""
307+
Modifies/overwrites an existing show function in the
308+
locally cached DesignDocument shows dictionary.
309+
310+
:param show_name: Name used to identify the show function.
311+
:param show_func: Javascript show function.
312+
"""
313+
if self.get_show_function(show_name) is None:
314+
msg = ('A show function with name {0} does not exist in this design doc'
315+
.format(show_name))
316+
raise CloudantArgumentError(msg)
317+
318+
self.shows.__setitem__(show_name, show_func)
319+
279320
def delete_view(self, view_name):
280321
"""
281322
Removes an existing MapReduce view definition from the locally cached
@@ -317,6 +358,18 @@ def delete_list_function(self, list_name):
317358
"""
318359
self.lists.__delitem__(list_name)
319360

361+
def delete_show_function(self, show_name):
362+
"""
363+
Removes an existing show function in the locally cached DesignDocument
364+
shows dictionary.
365+
366+
:param show_name: Name used to identify the list.
367+
"""
368+
if self.get_show_function(show_name) is None:
369+
return
370+
371+
self.shows.__delitem__(show_name)
372+
320373
def fetch(self):
321374
"""
322375
Retrieves the remote design document content and populates the locally
@@ -346,7 +399,7 @@ def fetch(self):
346399
)
347400

348401
for prop in self._nested_object_names:
349-
# Ensure views, indexes, and lists dict exist in locally cached DesignDocument.
402+
# Ensure dict for each sub-object exists in locally cached DesignDocument.
350403
getattr(self, prop, self.setdefault(prop, dict()))
351404

352405
# pylint: disable=too-many-branches
@@ -394,7 +447,7 @@ def save(self):
394447

395448
for prop in self._nested_object_names:
396449
if not getattr(self, prop):
397-
# Ensure empty views, indexes, or lists dict is not saved remotely.
450+
# Ensure empty dict for each sub-object is not saved remotely.
398451
self.__delitem__(prop)
399452

400453
super(DesignDocument, self).save()
@@ -462,6 +515,17 @@ def iterlists(self):
462515
for list_name, list_func in iteritems_(self.lists):
463516
yield list_name, list_func
464517

518+
def itershows(self):
519+
"""
520+
Provides a way to iterate over the locally cached DesignDocument
521+
shows dictionary.
522+
523+
:returns: Iterable containing show function name and associated
524+
show function
525+
"""
526+
for show_name, show_func in iteritems_(self.shows):
527+
yield show_name, show_func
528+
465529
def list_views(self):
466530
"""
467531
Retrieves a list of available View objects in the locally cached
@@ -489,6 +553,15 @@ def list_list_functions(self):
489553
"""
490554
return list(self.lists.keys())
491555

556+
def list_show_functions(self):
557+
"""
558+
Retrieves a list of available show functions in the locally cached
559+
DesignDocument shows dictionary.
560+
561+
:returns: List of show function names
562+
"""
563+
return list(self.shows.keys())
564+
492565
def get_view(self, view_name):
493566
"""
494567
Retrieves a specific View from the locally cached DesignDocument by
@@ -518,10 +591,21 @@ def get_list_function(self, list_name):
518591
519592
:param str list_name: Name used to identify the list function.
520593
521-
:returns: Index dictionary for the specified list function name
594+
:returns: String form of the specified list function
522595
"""
523596
return self.lists.get(list_name)
524597

598+
def get_show_function(self, show_name):
599+
"""
600+
Retrieves a specific show function from the locally cached DesignDocument
601+
shows dictionary by name.
602+
603+
:param str show_name: Name used to identify the show function.
604+
605+
:returns: String form of the specified show function
606+
"""
607+
return self.shows.get(show_name)
608+
525609
def info(self):
526610
"""
527611
Retrieves the design document view information data, returns dictionary

tests/unit/database_tests.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ def test_retrieve_design_document(self):
254254
# Get an empty design document object that does not exist remotely
255255
local_ddoc = self.db.get_design_document('_design/ddoc01')
256256
self.assertEqual(local_ddoc, {'_id': '_design/ddoc01', 'indexes': {},
257-
'views': {}, 'lists': {}})
257+
'views': {}, 'lists': {}, 'shows': {}})
258258
# Add the design document to the database
259259
map_func = 'function(doc) {\n emit(doc._id, 1); \n}'
260260
local_ddoc.add_view('view01', map_func)
@@ -667,6 +667,31 @@ def test_get_list_function_result(self):
667667
'</ol></body></html>'
668668
)
669669

670+
def test_get_show_result(self):
671+
"""
672+
Test get_show_result executes a show function against a document.
673+
"""
674+
self.populate_db_with_documents()
675+
ddoc = DesignDocument(self.db, '_design/ddoc001')
676+
ddoc.add_show_function(
677+
'show001',
678+
'function(doc, req) { '
679+
'if (doc) { return \'Hello from \' + doc._id + \'!\'; } '
680+
'else { return \'Hello, world!\'; } }')
681+
ddoc.save()
682+
doc = Document(self.db, 'doc001')
683+
doc.save()
684+
# Execute show function
685+
resp = self.db.get_show_function_result(
686+
'_design/ddoc001',
687+
'show001',
688+
'doc001'
689+
)
690+
self.assertEqual(
691+
resp,
692+
'Hello from doc001!'
693+
)
694+
670695
@unittest.skipUnless(
671696
os.environ.get('RUN_CLOUDANT_TESTS') is not None,
672697
'Skipping Cloudant specific Database tests'
@@ -900,6 +925,7 @@ def test_create_json_index(self):
900925
'_rev': ddoc['_rev'],
901926
'indexes': {},
902927
'lists': {},
928+
'shows': {},
903929
'language': 'query',
904930
'views': {index.name: {'map': {'fields': {'name': 'asc',
905931
'age': 'asc'}},
@@ -927,6 +953,7 @@ def test_create_text_index(self):
927953
'language': 'query',
928954
'views': {},
929955
'lists': {},
956+
'shows': {},
930957
'indexes': {index.name: {'index': {'index_array_lengths': True,
931958
'fields': [{'name': 'name', 'type': 'string'},
932959
{'name': 'age', 'type': 'number'}],
@@ -952,6 +979,7 @@ def test_create_all_fields_text_index(self):
952979
'language': 'query',
953980
'views': {},
954981
'lists': {},
982+
'shows': {},
955983
'indexes': {index.name: {'index': {'index_array_lengths': True,
956984
'fields': 'all_fields',
957985
'default_field': {},
@@ -988,6 +1016,7 @@ def test_create_multiple_indexes_one_ddoc(self):
9881016
'_rev': ddoc['_rev'],
9891017
'language': 'query',
9901018
'lists': {},
1019+
'shows': {},
9911020
'views': {'json-index-001': {
9921021
'map': {'fields': {'name': 'asc',
9931022
'age': 'asc'}},

0 commit comments

Comments
 (0)