udata 9.0.1.dev29468__py2.py3-none-any.whl → 9.0.1.dev29504__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of udata might be problematic. Click here for more details.

udata/api_fields.py CHANGED
@@ -96,6 +96,7 @@ def generate_fields(**kwargs):
96
96
  read_fields = {}
97
97
  write_fields = {}
98
98
  sortables = []
99
+ filterables = []
99
100
 
100
101
  read_fields['id'] = restx_fields.String(required=True)
101
102
 
@@ -106,6 +107,23 @@ def generate_fields(**kwargs):
106
107
  if info.get('sortable', False):
107
108
  sortables.append(key)
108
109
 
110
+ filterable = info.get('filterable', None)
111
+ if filterable is not None:
112
+ if 'key' not in filterable:
113
+ filterable['key'] = key
114
+ if 'column' not in filterable:
115
+ filterable['column'] = key
116
+
117
+ if 'constraints' not in filterable:
118
+ filterable['constraints'] = []
119
+ if isinstance(field, mongo_fields.ReferenceField) or (isinstance(field, mongo_fields.ListField) and isinstance(field.field, mongo_fields.ReferenceField)):
120
+ filterable['constraints'].append('objectid')
121
+
122
+ # We may add more information later here:
123
+ # - type of mongo query to execute (right now only simple =)
124
+
125
+ filterables.append(filterable)
126
+
109
127
  read, write = convert_db_to_field(key, field)
110
128
 
111
129
  if read:
@@ -159,6 +177,9 @@ def generate_fields(**kwargs):
159
177
  choices = sortables + ['-' + k for k in sortables]
160
178
  parser.add_argument('sort', type=str, location='args', choices=choices, help='The field (and direction) on which sorting apply')
161
179
 
180
+ for filterable in filterables:
181
+ parser.add_argument(filterable['key'], type=str, location='args')
182
+
162
183
  cls.__index_parser__ = parser
163
184
  def apply_sort_filters_and_pagination(base_query):
164
185
  args = cls.__index_parser__.parse_args()
@@ -166,6 +187,16 @@ def generate_fields(**kwargs):
166
187
  if sortables and args['sort']:
167
188
  base_query = base_query.order_by(args['sort'])
168
189
 
190
+ for filterable in filterables:
191
+ if args.get(filterable['key']):
192
+ for constraint in filterable['constraints']:
193
+ if constraint == 'objectid' and not ObjectId.is_valid(args[filterable['key']]):
194
+ api.abort(400, f'`{filterable["key"]}` must be an identifier')
195
+
196
+ base_query = base_query.filter(**{
197
+ filterable['column']: args[filterable['key']],
198
+ })
199
+
169
200
  if paginable:
170
201
  base_query = base_query.paginate(args['page'], args['page_size'])
171
202
 
@@ -113,7 +113,10 @@ class Dataservice(WithMetrics, Owned, db.Document):
113
113
  db.ReferenceField(Dataset),
114
114
  nested_fields=datasets_api_fields.dataset_fields,
115
115
  )
116
- )
116
+ ),
117
+ filterable={
118
+ 'key': 'dataset',
119
+ },
117
120
  )
118
121
 
119
122
  @function_field(description="Link to the API endpoint for this dataservice")
udata/harvest/api.py CHANGED
@@ -1,3 +1,4 @@
1
+ from bson import ObjectId
1
2
  from werkzeug.exceptions import BadRequest
2
3
  from flask import request
3
4
 
@@ -190,6 +191,10 @@ class SourcesAPI(API):
190
191
  def get(self):
191
192
  '''List all harvest sources'''
192
193
  args = source_parser.parse_args()
194
+
195
+ if args.get('owner') and not ObjectId.is_valid(args.get('owner')):
196
+ api.abort(400, '`owner` arg must be an identifier')
197
+
193
198
  return actions.paginate_sources(args.get('owner'),
194
199
  page=args['page'],
195
200
  page_size=args['page_size'],
@@ -85,23 +85,30 @@ class DataserviceAPITest(APITestCase):
85
85
 
86
86
 
87
87
  def test_dataservice_api_index(self):
88
+ dataset_a = DatasetFactory()
89
+ dataset_b = DatasetFactory()
90
+
88
91
  self.login()
89
92
  self.post(url_for('api.dataservices'), {
90
93
  'title': 'B',
91
94
  'base_api_url': 'https://example.org/B',
95
+ 'datasets': [dataset_b.id],
92
96
  })
93
97
  self.post(url_for('api.dataservices'), {
94
98
  'title': 'C',
95
99
  'base_api_url': 'https://example.org/C',
100
+ 'datasets': [dataset_a.id, dataset_b.id],
96
101
  })
97
102
  self.post(url_for('api.dataservices'), {
98
103
  'title': 'A',
99
104
  'base_api_url': 'https://example.org/A',
105
+ 'datasets': [dataset_a.id],
100
106
  })
101
- response = self.post(url_for('api.dataservices'), {
107
+ self.post(url_for('api.dataservices'), {
102
108
  'title': 'X',
103
109
  'base_api_url': 'https://example.org/X',
104
110
  'private': True,
111
+ 'datasets': [dataset_a.id],
105
112
  })
106
113
 
107
114
  self.assertEqual(Dataservice.objects.count(), 4)
@@ -115,8 +122,12 @@ class DataserviceAPITest(APITestCase):
115
122
  self.assertEqual(response.json['total'], 3)
116
123
  self.assertEqual(len(response.json['data']), 3)
117
124
  self.assertEqual(response.json['data'][0]['title'], 'B')
125
+ self.assertEqual(response.json['data'][0]['datasets'][0]['id'], str(dataset_b.id))
118
126
  self.assertEqual(response.json['data'][1]['title'], 'C')
127
+ self.assertEqual(response.json['data'][1]['datasets'][0]['id'], str(dataset_a.id))
128
+ self.assertEqual(response.json['data'][1]['datasets'][1]['id'], str(dataset_b.id))
119
129
  self.assertEqual(response.json['data'][2]['title'], 'A')
130
+ self.assertEqual(response.json['data'][2]['datasets'][0]['id'], str(dataset_a.id))
120
131
 
121
132
  response = self.get(url_for('api.dataservices', sort='title'))
122
133
  self.assert200(response)
@@ -153,6 +164,20 @@ class DataserviceAPITest(APITestCase):
153
164
  self.assertEqual(len(response.json['data']), 1)
154
165
  self.assertEqual(response.json['data'][0]['title'], 'B')
155
166
 
167
+ response = self.get(url_for('api.dataservices', sort='title', dataset=str(dataset_a.id)))
168
+ self.assert200(response)
169
+
170
+ self.assertEqual(response.json['total'], 2)
171
+ self.assertEqual(response.json['data'][0]['title'], 'A')
172
+ self.assertEqual(response.json['data'][0]['datasets'][0]['id'], str(dataset_a.id))
173
+ self.assertEqual(response.json['data'][1]['title'], 'C')
174
+ self.assertEqual(response.json['data'][1]['datasets'][0]['id'], str(dataset_a.id))
175
+ self.assertEqual(response.json['data'][1]['datasets'][1]['id'], str(dataset_b.id))
176
+
177
+ def test_dataservice_api_index_with_wrong_dataset_id(self):
178
+ response = self.get(url_for('api.dataservices', sort='title', dataset=str("xxx")))
179
+ self.assert400(response)
180
+
156
181
  def test_dataservice_api_create_with_validation_error(self):
157
182
  self.login()
158
183
  response = self.post(url_for('api.dataservices'), {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: udata
3
- Version: 9.0.1.dev29468
3
+ Version: 9.0.1.dev29504
4
4
  Summary: Open data portal
5
5
  Home-page: https://github.com/opendatateam/udata
6
6
  Author: Opendata Team
@@ -138,8 +138,10 @@ It is collectively taken care of by members of the
138
138
  ## Current (in progress)
139
139
 
140
140
  - Refactor catalog exports [#3052](https://github.com/opendatateam/udata/pull/3052)
141
+ - Add a filter to filter dataservices by dataset [#3056](https://github.com/opendatateam/udata/pull/3056)
141
142
  - Fix reuses' datasets references [#3057](https://github.com/opendatateam/udata/pull/3057)
142
143
  - Save and show harvest logs [#3053](https://github.com/opendatateam/udata/pull/3053)
144
+ - Fix missing `ObjectId` validation on `/sources` endpoint [#3060](https://github.com/opendatateam/udata/pull/3060)
143
145
 
144
146
  ## 9.0.0 (2024-06-07)
145
147
 
@@ -1,7 +1,7 @@
1
1
  tasks/__init__.py,sha256=CnVhb_TV-6nMhxVR6itnBmvuU2OSCs02AfNB4irVBTE,8132
2
2
  tasks/helpers.py,sha256=k_HiuiEJNgQLvWdeHqczPOAcrYpFjEepBeKo7EQzY8M,994
3
3
  udata/__init__.py,sha256=SslifvQytly1ztvHCc4bdlP3Va1F5UV60hSJC6a_guk,101
4
- udata/api_fields.py,sha256=myHkndhedV3g9cn2FWBu1Hx24Fy-fxaGedEP1ZMOoi0,11506
4
+ udata/api_fields.py,sha256=PMD206aJmntO4wizzL5Bv9_qU3HivAHRNO9ypV4nlXg,12959
5
5
  udata/app.py,sha256=6upwrImLaWrSYtsXPW1zH84_oRxp3B6XFuocMe2D6NU,7329
6
6
  udata/assets.py,sha256=aMa-MnAEXVSTavptSn2V8sUE6jL_N0MrYCQ6_QpsuHs,645
7
7
  udata/entrypoints.py,sha256=8bZUvZ8ICO3kZxa8xDUaen6YS_OAGNKHFvaD7d8BOk0,2687
@@ -80,7 +80,7 @@ udata/core/contact_point/forms.py,sha256=ggLhSJ1IRn5MclrhydckjAxwr4fFZxgAD4huSSu
80
80
  udata/core/contact_point/models.py,sha256=NlNKureCpzgTLJuGviZPjNx-ABYRp4j2L-ur9Gmixao,324
81
81
  udata/core/dataservices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
82
82
  udata/core/dataservices/api.py,sha256=rjCU55NNGgCDRlurfhJUT2byBGJWN5coM8b7AApzEew,3090
83
- udata/core/dataservices/models.py,sha256=6cE7tQ6GahnFaz1U_8yNmJqlCzoHQxZdet4qXQaqa7k,4449
83
+ udata/core/dataservices/models.py,sha256=rCJW_Bh9Ovc5oL_RH69nyexRYH_JPpudCT_L-RKzsvc,4512
84
84
  udata/core/dataservices/permissions.py,sha256=X9Bh8e0pnx6OgeEf6NowXZUiwyreUa6UY479B16cCqs,175
85
85
  udata/core/dataservices/tasks.py,sha256=NOWcTPoLasMrrvq9EkwQMGlUbQQmi_l3s815K-mtZTM,971
86
86
  udata/core/dataset/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -267,7 +267,7 @@ udata/frontend/csv.py,sha256=SDV8GMhNpHyE9NYy9cfHulsFUzwclfc7roWAAm-PZ9M,8257
267
267
  udata/frontend/markdown.py,sha256=41bOiU6AKng4U-5v3otBed3dyCu63NI9fnznUQThbIk,4377
268
268
  udata/harvest/__init__.py,sha256=C4y5w4vGb_F9Opy62lzV3eHo4DkNyRgPCq-wsarPXiQ,28
269
269
  udata/harvest/actions.py,sha256=6f9bkIITLHes0vGU-yioRGfFOghAS3nXThXEVKaHFpA,10082
270
- udata/harvest/api.py,sha256=dK-3useKbaKtZL3NL6R0mPgeuPHBruPK958oeXxqg_Y,14378
270
+ udata/harvest/api.py,sha256=GDFqjAyKh4a22QDniyZZC9AyTFi4ndhS94VzW6plBZE,14545
271
271
  udata/harvest/commands.py,sha256=Vo1KxO7GAOgQ87khqlGIyjZIlwAOlQ8ztadqmWdMvwk,4922
272
272
  udata/harvest/csv.py,sha256=c2fPnMB6q99wRxLncl8L0ILcdF4SI8Lsl8tViNrcW6A,396
273
273
  udata/harvest/exceptions.py,sha256=YaXw0ESmSCcibfUmP5uc1uRedKD2mvUBXUOnbaSXtNw,299
@@ -590,7 +590,7 @@ udata/tests/api/__init__.py,sha256=gIIB9OPiSs1A-u1bEIHlOesaCQzJ08SOLPuJd34LesE,1
590
590
  udata/tests/api/test_auth_api.py,sha256=3Zhn2A29poZIcCJ_R9_-LkR3xOFUTw1aTquiZVXQ2F0,20306
591
591
  udata/tests/api/test_base_api.py,sha256=DRX5nuFIj51GFmMIAxUzoW1yiq1apNgr1vS4U4agzeg,2319
592
592
  udata/tests/api/test_contact_points.py,sha256=MJm8B06iaUqIZCqxll3NViFwUCxwqZZ4u9e9s1h8MgU,1056
593
- udata/tests/api/test_dataservices_api.py,sha256=eIo0b18BSSRocyf9pTChzDnKotxxLMq8R1tX53JdH_M,9743
593
+ udata/tests/api/test_dataservices_api.py,sha256=51NU5d4iqfJT8iiyi0aLdWXDqZLvmyD-qh-Sflk9Lsg,11122
594
594
  udata/tests/api/test_datasets_api.py,sha256=zqRtC61NvYUNum4kBTjA1W87JAIobA4k564rm-U6c98,81704
595
595
  udata/tests/api/test_fields.py,sha256=OW85Z5MES5HeWOpapeem8OvR1cIcrqW-xMWpdZO4LZ8,1033
596
596
  udata/tests/api/test_follow_api.py,sha256=0h54P_Dfbo07u6tg0Rbai1WWgWb19ZLN2HGv4oLCWfg,3383
@@ -685,9 +685,9 @@ udata/translations/pt/LC_MESSAGES/udata.mo,sha256=uttB2K8VsqzkEQG-5HfTtFms_3LtV9
685
685
  udata/translations/pt/LC_MESSAGES/udata.po,sha256=8Ql1Lp7Z9KLnvp-qRxw-NhFu1p35Xj-q6Jg9JHsYhcw,43733
686
686
  udata/translations/sr/LC_MESSAGES/udata.mo,sha256=US8beNIMPxP5h-zD_jfP1TheDDd4DdRVS5UIiY5XVZ8,28553
687
687
  udata/translations/sr/LC_MESSAGES/udata.po,sha256=TM0yMDvKRljyOzgZZMlTX6OfpF6OC4Ngf_9Zc8n6ayA,50313
688
- udata-9.0.1.dev29468.dist-info/LICENSE,sha256=V8j_M8nAz8PvAOZQocyRDX7keai8UJ9skgmnwqETmdY,34520
689
- udata-9.0.1.dev29468.dist-info/METADATA,sha256=eUDNQF9Yl-zU7kLY9aCI32D0i3m3rIMjrBH53m_I6BE,124274
690
- udata-9.0.1.dev29468.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
691
- udata-9.0.1.dev29468.dist-info/entry_points.txt,sha256=3SKiqVy4HUqxf6iWspgMqH8d88Htk6KoLbG1BU-UddQ,451
692
- udata-9.0.1.dev29468.dist-info/top_level.txt,sha256=39OCg-VWFWOq4gCKnjKNu-s3OwFlZIu_dVH8Gl6ndHw,12
693
- udata-9.0.1.dev29468.dist-info/RECORD,,
688
+ udata-9.0.1.dev29504.dist-info/LICENSE,sha256=V8j_M8nAz8PvAOZQocyRDX7keai8UJ9skgmnwqETmdY,34520
689
+ udata-9.0.1.dev29504.dist-info/METADATA,sha256=4pr78Vn0p9iHB9HmYAtWWp6mw6yOqRuvdlFg40cmuWg,124496
690
+ udata-9.0.1.dev29504.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
691
+ udata-9.0.1.dev29504.dist-info/entry_points.txt,sha256=3SKiqVy4HUqxf6iWspgMqH8d88Htk6KoLbG1BU-UddQ,451
692
+ udata-9.0.1.dev29504.dist-info/top_level.txt,sha256=39OCg-VWFWOq4gCKnjKNu-s3OwFlZIu_dVH8Gl6ndHw,12
693
+ udata-9.0.1.dev29504.dist-info/RECORD,,