Skip to content

Commit f1b0196

Browse files
authored
Merge pull request #190 from open-data/feature/ds-dump-filename
DataStore Dump Filename
2 parents 55365e1 + 975ae94 commit f1b0196

File tree

2 files changed

+26
-4
lines changed

2 files changed

+26
-4
lines changed

changes/190.canada.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added a `filename` option to the DataStore Dump endpoint.

ckanext/datastore/blueprint.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@
2828
json_writer,
2929
xml_writer,
3030
)
31+
# (canada fork only): filename to save stream to
32+
import re
33+
34+
35+
# (canada fork only): filename to save stream to
36+
FILENAME_MATCH = re.compile('^[\w\-\.]+$')
3137

3238
int_validator = get_validator(u'int_validator')
3339
boolean_validator = get_validator(u'boolean_validator')
@@ -75,6 +81,18 @@ def exclude_id_from_ds_dump(key, data, errors, context):
7581
data[key] = value
7682

7783

84+
# (canada fork only): filename to save stream to
85+
def filename_safe(key, data, errors, context):
86+
"""
87+
Makes sure the passed filename is safe to stream back in the response.
88+
"""
89+
value = data.get(key)
90+
91+
if not re.search(FILENAME_MATCH, value):
92+
errors[key].append(_('Invalid characters in filename'))
93+
raise StopOnError
94+
95+
7896
def dump_schema() -> Schema:
7997
return {
8098
u'offset': [default(0), int_validator],
@@ -88,6 +106,7 @@ def dump_schema() -> Schema:
88106
u'language': [ignore_missing, unicode_only],
89107
u'fields': [exclude_id_from_ds_dump, ignore_missing, list_of_strings_or_string], # (canada fork only): exclude _id field from Blueprint dump
90108
u'sort': [default(u'_id'), list_of_strings_or_string],
109+
'filename': [ignore_missing, unicode_only, filename_safe] # (canada fork only): filename to save stream to
91110
}
92111

93112

@@ -111,6 +130,8 @@ def dump(resource_id: str):
111130
limit = data.get('limit')
112131
options = {'bom': data['bom']}
113132
sort = data['sort']
133+
# (canada fork only): filename to save stream to
134+
filename = data.get('filename', resource_id)
114135
search_params = {
115136
k: v
116137
for k, v in data.items()
@@ -127,19 +148,19 @@ def dump(resource_id: str):
127148

128149
if fmt == 'csv':
129150
content_disposition = 'attachment; filename="{name}.csv"'.format(
130-
name=resource_id)
151+
name=filename) # (canada fork only): filename to save stream to
131152
content_type = b'text/csv; charset=utf-8'
132153
elif fmt == 'tsv':
133154
content_disposition = 'attachment; filename="{name}.tsv"'.format(
134-
name=resource_id)
155+
name=filename) # (canada fork only): filename to save stream to
135156
content_type = b'text/tab-separated-values; charset=utf-8'
136157
elif fmt == 'json':
137158
content_disposition = 'attachment; filename="{name}.json"'.format(
138-
name=resource_id)
159+
name=filename) # (canada fork only): filename to save stream to
139160
content_type = b'application/json; charset=utf-8'
140161
elif fmt == 'xml':
141162
content_disposition = 'attachment; filename="{name}.xml"'.format(
142-
name=resource_id)
163+
name=filename) # (canada fork only): filename to save stream to
143164
content_type = b'text/xml; charset=utf-8'
144165
else:
145166
abort(404, _('Unsupported format'))

0 commit comments

Comments
 (0)