udata 9.1.2.dev30355__py2.py3-none-any.whl → 9.1.2.dev30382__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.
- tasks/__init__.py +109 -107
- tasks/helpers.py +18 -18
- udata/__init__.py +4 -4
- udata/admin/views.py +5 -5
- udata/api/__init__.py +135 -124
- udata/api/commands.py +45 -37
- udata/api/errors.py +5 -4
- udata/api/fields.py +23 -21
- udata/api/oauth2.py +55 -74
- udata/api/parsers.py +15 -15
- udata/api/signals.py +1 -1
- udata/api_fields.py +137 -89
- udata/app.py +56 -54
- udata/assets.py +5 -5
- udata/auth/__init__.py +37 -26
- udata/auth/forms.py +23 -15
- udata/auth/helpers.py +1 -1
- udata/auth/mails.py +3 -3
- udata/auth/password_validation.py +19 -15
- udata/auth/views.py +94 -68
- udata/commands/__init__.py +71 -69
- udata/commands/cache.py +7 -7
- udata/commands/db.py +201 -140
- udata/commands/dcat.py +36 -30
- udata/commands/fixtures.py +100 -84
- udata/commands/images.py +21 -20
- udata/commands/info.py +17 -20
- udata/commands/init.py +10 -10
- udata/commands/purge.py +12 -13
- udata/commands/serve.py +41 -29
- udata/commands/static.py +16 -18
- udata/commands/test.py +20 -20
- udata/commands/tests/fixtures.py +26 -24
- udata/commands/worker.py +31 -33
- udata/core/__init__.py +12 -12
- udata/core/activity/__init__.py +0 -1
- udata/core/activity/api.py +59 -49
- udata/core/activity/models.py +28 -26
- udata/core/activity/signals.py +1 -1
- udata/core/activity/tasks.py +16 -10
- udata/core/badges/api.py +6 -6
- udata/core/badges/commands.py +14 -13
- udata/core/badges/fields.py +8 -5
- udata/core/badges/forms.py +7 -4
- udata/core/badges/models.py +16 -31
- udata/core/badges/permissions.py +1 -3
- udata/core/badges/signals.py +2 -2
- udata/core/badges/tasks.py +3 -2
- udata/core/badges/tests/test_commands.py +10 -10
- udata/core/badges/tests/test_model.py +24 -31
- udata/core/contact_point/api.py +19 -18
- udata/core/contact_point/api_fields.py +21 -14
- udata/core/contact_point/factories.py +2 -2
- udata/core/contact_point/forms.py +7 -6
- udata/core/contact_point/models.py +3 -5
- udata/core/dataservices/api.py +26 -21
- udata/core/dataservices/factories.py +13 -11
- udata/core/dataservices/models.py +35 -40
- udata/core/dataservices/permissions.py +4 -4
- udata/core/dataservices/rdf.py +40 -17
- udata/core/dataservices/tasks.py +4 -3
- udata/core/dataset/actions.py +10 -10
- udata/core/dataset/activities.py +21 -23
- udata/core/dataset/api.py +321 -298
- udata/core/dataset/api_fields.py +443 -271
- udata/core/dataset/apiv2.py +305 -229
- udata/core/dataset/commands.py +38 -36
- udata/core/dataset/constants.py +61 -54
- udata/core/dataset/csv.py +70 -74
- udata/core/dataset/events.py +39 -32
- udata/core/dataset/exceptions.py +8 -4
- udata/core/dataset/factories.py +57 -65
- udata/core/dataset/forms.py +87 -63
- udata/core/dataset/models.py +336 -280
- udata/core/dataset/permissions.py +9 -6
- udata/core/dataset/preview.py +15 -17
- udata/core/dataset/rdf.py +156 -122
- udata/core/dataset/search.py +92 -77
- udata/core/dataset/signals.py +1 -1
- udata/core/dataset/tasks.py +63 -54
- udata/core/discussions/actions.py +5 -5
- udata/core/discussions/api.py +124 -120
- udata/core/discussions/factories.py +2 -2
- udata/core/discussions/forms.py +9 -7
- udata/core/discussions/metrics.py +1 -3
- udata/core/discussions/models.py +25 -24
- udata/core/discussions/notifications.py +18 -14
- udata/core/discussions/permissions.py +3 -3
- udata/core/discussions/signals.py +4 -4
- udata/core/discussions/tasks.py +24 -28
- udata/core/followers/api.py +32 -33
- udata/core/followers/models.py +9 -9
- udata/core/followers/signals.py +3 -3
- udata/core/jobs/actions.py +7 -7
- udata/core/jobs/api.py +99 -92
- udata/core/jobs/commands.py +48 -49
- udata/core/jobs/forms.py +11 -11
- udata/core/jobs/models.py +6 -6
- udata/core/metrics/__init__.py +2 -2
- udata/core/metrics/commands.py +34 -30
- udata/core/metrics/models.py +2 -4
- udata/core/metrics/signals.py +1 -1
- udata/core/metrics/tasks.py +3 -3
- udata/core/organization/activities.py +12 -15
- udata/core/organization/api.py +167 -174
- udata/core/organization/api_fields.py +183 -124
- udata/core/organization/apiv2.py +32 -32
- udata/core/organization/commands.py +20 -22
- udata/core/organization/constants.py +11 -11
- udata/core/organization/csv.py +17 -15
- udata/core/organization/factories.py +8 -11
- udata/core/organization/forms.py +32 -26
- udata/core/organization/metrics.py +2 -1
- udata/core/organization/models.py +87 -67
- udata/core/organization/notifications.py +18 -14
- udata/core/organization/permissions.py +10 -11
- udata/core/organization/rdf.py +14 -14
- udata/core/organization/search.py +30 -28
- udata/core/organization/signals.py +7 -7
- udata/core/organization/tasks.py +42 -61
- udata/core/owned.py +38 -27
- udata/core/post/api.py +82 -81
- udata/core/post/constants.py +8 -5
- udata/core/post/factories.py +4 -4
- udata/core/post/forms.py +13 -14
- udata/core/post/models.py +20 -22
- udata/core/post/tests/test_api.py +30 -32
- udata/core/reports/api.py +8 -7
- udata/core/reports/constants.py +1 -3
- udata/core/reports/models.py +10 -10
- udata/core/reuse/activities.py +15 -19
- udata/core/reuse/api.py +123 -126
- udata/core/reuse/api_fields.py +120 -85
- udata/core/reuse/apiv2.py +11 -10
- udata/core/reuse/constants.py +23 -23
- udata/core/reuse/csv.py +18 -18
- udata/core/reuse/factories.py +5 -9
- udata/core/reuse/forms.py +24 -21
- udata/core/reuse/models.py +55 -51
- udata/core/reuse/permissions.py +2 -2
- udata/core/reuse/search.py +49 -46
- udata/core/reuse/signals.py +1 -1
- udata/core/reuse/tasks.py +4 -5
- udata/core/site/api.py +47 -50
- udata/core/site/factories.py +2 -2
- udata/core/site/forms.py +4 -5
- udata/core/site/models.py +94 -63
- udata/core/site/rdf.py +14 -14
- udata/core/spam/api.py +16 -9
- udata/core/spam/constants.py +4 -4
- udata/core/spam/fields.py +13 -7
- udata/core/spam/models.py +27 -20
- udata/core/spam/signals.py +1 -1
- udata/core/spam/tests/test_spam.py +6 -5
- udata/core/spatial/api.py +72 -80
- udata/core/spatial/api_fields.py +73 -58
- udata/core/spatial/commands.py +67 -64
- udata/core/spatial/constants.py +3 -3
- udata/core/spatial/factories.py +37 -54
- udata/core/spatial/forms.py +27 -26
- udata/core/spatial/geoids.py +17 -17
- udata/core/spatial/models.py +43 -47
- udata/core/spatial/tasks.py +2 -1
- udata/core/spatial/tests/test_api.py +115 -130
- udata/core/spatial/tests/test_fields.py +74 -77
- udata/core/spatial/tests/test_geoid.py +22 -22
- udata/core/spatial/tests/test_models.py +5 -7
- udata/core/spatial/translations.py +16 -16
- udata/core/storages/__init__.py +16 -18
- udata/core/storages/api.py +66 -64
- udata/core/storages/tasks.py +7 -7
- udata/core/storages/utils.py +15 -15
- udata/core/storages/views.py +5 -6
- udata/core/tags/api.py +17 -14
- udata/core/tags/csv.py +4 -4
- udata/core/tags/models.py +8 -5
- udata/core/tags/tasks.py +11 -13
- udata/core/tags/views.py +4 -4
- udata/core/topic/api.py +84 -73
- udata/core/topic/apiv2.py +157 -127
- udata/core/topic/factories.py +3 -4
- udata/core/topic/forms.py +12 -14
- udata/core/topic/models.py +14 -19
- udata/core/topic/parsers.py +26 -26
- udata/core/user/activities.py +30 -29
- udata/core/user/api.py +151 -152
- udata/core/user/api_fields.py +132 -100
- udata/core/user/apiv2.py +7 -7
- udata/core/user/commands.py +38 -38
- udata/core/user/factories.py +8 -9
- udata/core/user/forms.py +14 -11
- udata/core/user/metrics.py +2 -2
- udata/core/user/models.py +68 -69
- udata/core/user/permissions.py +4 -5
- udata/core/user/rdf.py +7 -8
- udata/core/user/tasks.py +2 -2
- udata/core/user/tests/test_user_model.py +24 -16
- udata/db/tasks.py +2 -1
- udata/entrypoints.py +35 -31
- udata/errors.py +2 -1
- udata/event/values.py +6 -6
- udata/factories.py +2 -2
- udata/features/identicon/api.py +5 -6
- udata/features/identicon/backends.py +48 -55
- udata/features/identicon/tests/test_backends.py +4 -5
- udata/features/notifications/__init__.py +0 -1
- udata/features/notifications/actions.py +9 -9
- udata/features/notifications/api.py +17 -13
- udata/features/territories/__init__.py +12 -10
- udata/features/territories/api.py +14 -15
- udata/features/territories/models.py +23 -28
- udata/features/transfer/actions.py +8 -11
- udata/features/transfer/api.py +84 -77
- udata/features/transfer/factories.py +2 -1
- udata/features/transfer/models.py +11 -12
- udata/features/transfer/notifications.py +19 -15
- udata/features/transfer/permissions.py +5 -5
- udata/forms/__init__.py +5 -2
- udata/forms/fields.py +164 -172
- udata/forms/validators.py +19 -22
- udata/forms/widgets.py +9 -13
- udata/frontend/__init__.py +31 -26
- udata/frontend/csv.py +68 -58
- udata/frontend/markdown.py +40 -44
- udata/harvest/actions.py +89 -77
- udata/harvest/api.py +294 -238
- udata/harvest/backends/__init__.py +4 -4
- udata/harvest/backends/base.py +128 -111
- udata/harvest/backends/dcat.py +80 -66
- udata/harvest/commands.py +56 -60
- udata/harvest/csv.py +8 -8
- udata/harvest/exceptions.py +6 -3
- udata/harvest/filters.py +24 -23
- udata/harvest/forms.py +27 -28
- udata/harvest/models.py +88 -80
- udata/harvest/notifications.py +15 -10
- udata/harvest/signals.py +13 -13
- udata/harvest/tasks.py +11 -10
- udata/harvest/tests/factories.py +23 -24
- udata/harvest/tests/test_actions.py +136 -166
- udata/harvest/tests/test_api.py +220 -214
- udata/harvest/tests/test_base_backend.py +117 -112
- udata/harvest/tests/test_dcat_backend.py +380 -308
- udata/harvest/tests/test_filters.py +33 -22
- udata/harvest/tests/test_models.py +11 -14
- udata/harvest/tests/test_notifications.py +6 -7
- udata/harvest/tests/test_tasks.py +7 -6
- udata/i18n.py +237 -78
- udata/linkchecker/backends.py +5 -11
- udata/linkchecker/checker.py +23 -22
- udata/linkchecker/commands.py +4 -6
- udata/linkchecker/models.py +6 -6
- udata/linkchecker/tasks.py +18 -20
- udata/mail.py +21 -21
- udata/migrations/2020-07-24-remove-s-from-scope-oauth.py +9 -8
- udata/migrations/2020-08-24-add-fs-filename.py +9 -8
- udata/migrations/2020-09-28-update-reuses-datasets-metrics.py +5 -4
- udata/migrations/2020-10-16-migrate-ods-resources.py +9 -10
- udata/migrations/2021-04-08-update-schema-with-new-structure.py +8 -7
- udata/migrations/2021-05-27-fix-default-schema-name.py +7 -6
- udata/migrations/2021-07-05-remove-unused-badges.py +17 -15
- udata/migrations/2021-07-07-update-schema-for-community-resources.py +7 -6
- udata/migrations/2021-08-17-follow-integrity.py +5 -4
- udata/migrations/2021-08-17-harvest-integrity.py +13 -12
- udata/migrations/2021-08-17-oauth2client-integrity.py +5 -4
- udata/migrations/2021-08-17-transfer-integrity.py +5 -4
- udata/migrations/2021-08-17-users-integrity.py +9 -8
- udata/migrations/2021-12-14-reuse-topics.py +7 -6
- udata/migrations/2022-04-21-improve-extension-detection.py +8 -7
- udata/migrations/2022-09-22-clean-inactive-harvest-datasets.py +16 -14
- udata/migrations/2022-10-10-add-fs_uniquifier-to-user-model.py +6 -6
- udata/migrations/2022-10-10-migrate-harvest-extras.py +36 -26
- udata/migrations/2023-02-08-rename-internal-dates.py +46 -28
- udata/migrations/2024-01-29-fix-reuse-and-dataset-with-private-None.py +10 -8
- udata/migrations/2024-03-22-migrate-activity-kwargs-to-extras.py +6 -4
- udata/migrations/2024-06-11-fix-reuse-datasets-references.py +7 -6
- udata/migrations/__init__.py +123 -105
- udata/models/__init__.py +4 -4
- udata/mongo/__init__.py +13 -11
- udata/mongo/badges_field.py +3 -2
- udata/mongo/datetime_fields.py +13 -12
- udata/mongo/document.py +17 -16
- udata/mongo/engine.py +15 -16
- udata/mongo/errors.py +2 -1
- udata/mongo/extras_fields.py +30 -20
- udata/mongo/queryset.py +12 -12
- udata/mongo/slug_fields.py +38 -28
- udata/mongo/taglist_field.py +1 -2
- udata/mongo/url_field.py +5 -5
- udata/mongo/uuid_fields.py +4 -3
- udata/notifications/__init__.py +1 -1
- udata/notifications/mattermost.py +10 -9
- udata/rdf.py +167 -188
- udata/routing.py +40 -45
- udata/search/__init__.py +18 -19
- udata/search/adapter.py +17 -16
- udata/search/commands.py +44 -51
- udata/search/fields.py +13 -20
- udata/search/query.py +23 -18
- udata/search/result.py +9 -10
- udata/sentry.py +21 -19
- udata/settings.py +262 -198
- udata/sitemap.py +8 -6
- udata/static/chunks/{11.e9b9ca1f3e03d4020377.js → 11.52e531c19f8de80c00cf.js} +3 -3
- udata/static/chunks/{11.e9b9ca1f3e03d4020377.js.map → 11.52e531c19f8de80c00cf.js.map} +1 -1
- udata/static/chunks/{13.038c0d9aa0dfa0181c4b.js → 13.c3343a7f1070061c0e10.js} +2 -2
- udata/static/chunks/{13.038c0d9aa0dfa0181c4b.js.map → 13.c3343a7f1070061c0e10.js.map} +1 -1
- udata/static/chunks/{16.0baa2b64a74a2dcde25c.js → 16.8fa42440ad75ca172e6d.js} +2 -2
- udata/static/chunks/{16.0baa2b64a74a2dcde25c.js.map → 16.8fa42440ad75ca172e6d.js.map} +1 -1
- udata/static/chunks/{19.350a9f150b074b4ecefa.js → 19.9c6c8412729cd6d59cfa.js} +3 -3
- udata/static/chunks/{19.350a9f150b074b4ecefa.js.map → 19.9c6c8412729cd6d59cfa.js.map} +1 -1
- udata/static/chunks/{5.6ebbce2b9b3e696d3da5.js → 5.71d15c2e4f21feee2a9a.js} +3 -3
- udata/static/chunks/{5.6ebbce2b9b3e696d3da5.js.map → 5.71d15c2e4f21feee2a9a.js.map} +1 -1
- udata/static/chunks/{6.d8a5f7b017bcbd083641.js → 6.9139dc098b8ea640b890.js} +3 -3
- udata/static/chunks/{6.d8a5f7b017bcbd083641.js.map → 6.9139dc098b8ea640b890.js.map} +1 -1
- udata/static/common.js +1 -1
- udata/static/common.js.map +1 -1
- udata/storage/s3.py +20 -13
- udata/tags.py +4 -5
- udata/tasks.py +43 -42
- udata/tests/__init__.py +9 -6
- udata/tests/api/__init__.py +5 -6
- udata/tests/api/test_auth_api.py +395 -321
- udata/tests/api/test_base_api.py +31 -33
- udata/tests/api/test_contact_points.py +7 -9
- udata/tests/api/test_dataservices_api.py +211 -158
- udata/tests/api/test_datasets_api.py +823 -812
- udata/tests/api/test_follow_api.py +13 -15
- udata/tests/api/test_me_api.py +95 -112
- udata/tests/api/test_organizations_api.py +301 -339
- udata/tests/api/test_reports_api.py +35 -25
- udata/tests/api/test_reuses_api.py +134 -139
- udata/tests/api/test_swagger.py +5 -5
- udata/tests/api/test_tags_api.py +18 -25
- udata/tests/api/test_topics_api.py +94 -94
- udata/tests/api/test_transfer_api.py +53 -48
- udata/tests/api/test_user_api.py +128 -141
- udata/tests/apiv2/test_datasets.py +290 -198
- udata/tests/apiv2/test_me_api.py +10 -11
- udata/tests/apiv2/test_organizations.py +56 -74
- udata/tests/apiv2/test_swagger.py +5 -5
- udata/tests/apiv2/test_topics.py +69 -87
- udata/tests/cli/test_cli_base.py +8 -8
- udata/tests/cli/test_db_cli.py +21 -19
- udata/tests/dataservice/test_dataservice_tasks.py +8 -12
- udata/tests/dataset/test_csv_adapter.py +44 -35
- udata/tests/dataset/test_dataset_actions.py +2 -3
- udata/tests/dataset/test_dataset_commands.py +7 -8
- udata/tests/dataset/test_dataset_events.py +36 -29
- udata/tests/dataset/test_dataset_model.py +224 -217
- udata/tests/dataset/test_dataset_rdf.py +142 -131
- udata/tests/dataset/test_dataset_tasks.py +15 -15
- udata/tests/dataset/test_resource_preview.py +10 -13
- udata/tests/features/territories/__init__.py +9 -13
- udata/tests/features/territories/test_territories_api.py +71 -91
- udata/tests/forms/test_basic_fields.py +7 -7
- udata/tests/forms/test_current_user_field.py +39 -66
- udata/tests/forms/test_daterange_field.py +31 -39
- udata/tests/forms/test_dict_field.py +28 -26
- udata/tests/forms/test_extras_fields.py +102 -76
- udata/tests/forms/test_form_field.py +8 -8
- udata/tests/forms/test_image_field.py +33 -26
- udata/tests/forms/test_model_field.py +134 -123
- udata/tests/forms/test_model_list_field.py +7 -7
- udata/tests/forms/test_nested_model_list_field.py +117 -79
- udata/tests/forms/test_publish_as_field.py +36 -65
- udata/tests/forms/test_reference_field.py +34 -53
- udata/tests/forms/test_user_forms.py +23 -21
- udata/tests/forms/test_uuid_field.py +6 -10
- udata/tests/frontend/__init__.py +9 -6
- udata/tests/frontend/test_auth.py +7 -6
- udata/tests/frontend/test_csv.py +81 -96
- udata/tests/frontend/test_hooks.py +43 -43
- udata/tests/frontend/test_markdown.py +211 -191
- udata/tests/helpers.py +32 -37
- udata/tests/models.py +2 -2
- udata/tests/organization/test_csv_adapter.py +21 -16
- udata/tests/organization/test_notifications.py +11 -18
- udata/tests/organization/test_organization_model.py +13 -13
- udata/tests/organization/test_organization_rdf.py +29 -22
- udata/tests/organization/test_organization_tasks.py +16 -17
- udata/tests/plugin.py +76 -73
- udata/tests/reuse/test_reuse_model.py +21 -21
- udata/tests/reuse/test_reuse_task.py +11 -13
- udata/tests/search/__init__.py +11 -12
- udata/tests/search/test_adapter.py +60 -70
- udata/tests/search/test_query.py +16 -16
- udata/tests/search/test_results.py +10 -7
- udata/tests/site/test_site_api.py +11 -16
- udata/tests/site/test_site_metrics.py +20 -30
- udata/tests/site/test_site_model.py +4 -5
- udata/tests/site/test_site_rdf.py +94 -78
- udata/tests/test_activity.py +17 -17
- udata/tests/test_discussions.py +292 -299
- udata/tests/test_i18n.py +37 -40
- udata/tests/test_linkchecker.py +91 -85
- udata/tests/test_mail.py +13 -17
- udata/tests/test_migrations.py +219 -180
- udata/tests/test_model.py +164 -157
- udata/tests/test_notifications.py +17 -17
- udata/tests/test_owned.py +14 -14
- udata/tests/test_rdf.py +25 -23
- udata/tests/test_routing.py +89 -93
- udata/tests/test_storages.py +137 -128
- udata/tests/test_tags.py +44 -46
- udata/tests/test_topics.py +7 -7
- udata/tests/test_transfer.py +42 -49
- udata/tests/test_uris.py +160 -161
- udata/tests/test_utils.py +79 -71
- udata/tests/user/test_user_rdf.py +5 -9
- udata/tests/workers/test_jobs_commands.py +57 -58
- udata/tests/workers/test_tasks_routing.py +23 -29
- udata/tests/workers/test_workers_api.py +125 -131
- udata/tests/workers/test_workers_helpers.py +6 -6
- udata/tracking.py +4 -6
- udata/uris.py +45 -46
- udata/utils.py +68 -66
- udata/wsgi.py +1 -1
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/METADATA +3 -2
- udata-9.1.2.dev30382.dist-info/RECORD +704 -0
- udata-9.1.2.dev30355.dist-info/RECORD +0 -704
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/LICENSE +0 -0
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/WHEEL +0 -0
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/entry_points.txt +0 -0
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/top_level.txt +0 -0
|
@@ -1,189 +1,279 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
-
from flask import url_for
|
|
3
2
|
|
|
4
|
-
from
|
|
5
|
-
from udata.tests.api import APITestCase
|
|
3
|
+
from flask import url_for
|
|
6
4
|
|
|
7
5
|
from udata.core.dataset.apiv2 import DEFAULT_PAGE_SIZE
|
|
8
6
|
from udata.core.dataset.factories import (
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
CommunityResourceFactory,
|
|
8
|
+
DatasetFactory,
|
|
9
|
+
ResourceFactory,
|
|
10
|
+
)
|
|
11
|
+
from udata.core.dataset.models import ResourceMixin
|
|
12
|
+
from udata.core.organization.factories import Member, OrganizationFactory
|
|
13
|
+
from udata.models import Dataset, db
|
|
14
|
+
from udata.tests.api import APITestCase
|
|
12
15
|
from udata.tests.helpers import assert_not_emit
|
|
13
16
|
|
|
14
|
-
class DatasetAPIV2Test(APITestCase):
|
|
15
17
|
|
|
18
|
+
class DatasetAPIV2Test(APITestCase):
|
|
16
19
|
def test_get_dataset(self):
|
|
17
20
|
resources = [ResourceFactory() for _ in range(2)]
|
|
18
21
|
dataset = DatasetFactory(resources=resources)
|
|
19
22
|
|
|
20
|
-
response = self.get(url_for(
|
|
23
|
+
response = self.get(url_for("apiv2.dataset", dataset=dataset))
|
|
21
24
|
self.assert200(response)
|
|
22
25
|
data = response.json
|
|
23
|
-
assert data[
|
|
24
|
-
assert data[
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
assert data["resources"]["rel"] == "subsection"
|
|
27
|
+
assert data["resources"]["href"] == url_for(
|
|
28
|
+
"apiv2.resources",
|
|
29
|
+
dataset=dataset.id,
|
|
30
|
+
page=1,
|
|
31
|
+
page_size=DEFAULT_PAGE_SIZE,
|
|
32
|
+
_external=True,
|
|
33
|
+
)
|
|
34
|
+
assert data["resources"]["type"] == "GET"
|
|
35
|
+
assert data["resources"]["total"] == len(resources)
|
|
36
|
+
assert data["community_resources"]["rel"] == "subsection"
|
|
37
|
+
assert data["community_resources"]["href"] == url_for(
|
|
38
|
+
"api.community_resources",
|
|
39
|
+
dataset=dataset.id,
|
|
40
|
+
page=1,
|
|
41
|
+
page_size=DEFAULT_PAGE_SIZE,
|
|
42
|
+
_external=True,
|
|
43
|
+
)
|
|
44
|
+
assert data["community_resources"]["type"] == "GET"
|
|
45
|
+
assert data["community_resources"]["total"] == 0
|
|
31
46
|
|
|
32
47
|
|
|
33
48
|
class DatasetResourceAPIV2Test(APITestCase):
|
|
34
|
-
|
|
35
49
|
def test_get_specific(self):
|
|
36
|
-
|
|
50
|
+
"""Should fetch serialized resource from the API based on rid"""
|
|
37
51
|
resources = [ResourceFactory() for _ in range(7)]
|
|
38
|
-
specific_resource = ResourceFactory(
|
|
52
|
+
specific_resource = ResourceFactory(
|
|
53
|
+
id="817204ac-2202-8b4a-98e7-4284d154d10c", title="my-resource"
|
|
54
|
+
)
|
|
39
55
|
resources.append(specific_resource)
|
|
40
56
|
dataset = DatasetFactory(resources=resources)
|
|
41
|
-
response = self.get(url_for(
|
|
57
|
+
response = self.get(url_for("apiv2.resource", rid=specific_resource.id))
|
|
42
58
|
self.assert200(response)
|
|
43
59
|
data = response.json
|
|
44
|
-
assert data[
|
|
45
|
-
assert data[
|
|
46
|
-
assert data[
|
|
47
|
-
response = self.get(url_for(
|
|
60
|
+
assert data["dataset_id"] == str(dataset.id)
|
|
61
|
+
assert data["resource"]["id"] == str(specific_resource.id)
|
|
62
|
+
assert data["resource"]["title"] == specific_resource.title
|
|
63
|
+
response = self.get(url_for("apiv2.resource", rid="111111ac-1111-1b1a-11e1-1111d111d11c"))
|
|
48
64
|
self.assert404(response)
|
|
49
65
|
com_resource = CommunityResourceFactory()
|
|
50
|
-
response = self.get(url_for(
|
|
66
|
+
response = self.get(url_for("apiv2.resource", rid=com_resource.id))
|
|
51
67
|
self.assert200(response)
|
|
52
68
|
data = response.json
|
|
53
|
-
assert data[
|
|
54
|
-
assert data[
|
|
55
|
-
assert data[
|
|
69
|
+
assert data["dataset_id"] is None
|
|
70
|
+
assert data["resource"]["id"] == str(com_resource.id)
|
|
71
|
+
assert data["resource"]["title"] == com_resource.title
|
|
56
72
|
|
|
57
73
|
def test_get(self):
|
|
58
|
-
|
|
74
|
+
"""Should fetch 1 page of resources from the API"""
|
|
59
75
|
resources = [ResourceFactory() for _ in range(7)]
|
|
60
76
|
dataset = DatasetFactory(resources=resources)
|
|
61
|
-
response = self.get(
|
|
77
|
+
response = self.get(
|
|
78
|
+
url_for("apiv2.resources", dataset=dataset.id, page=1, page_size=DEFAULT_PAGE_SIZE)
|
|
79
|
+
)
|
|
62
80
|
self.assert200(response)
|
|
63
81
|
data = response.json
|
|
64
|
-
assert len(data[
|
|
65
|
-
assert data[
|
|
66
|
-
assert data[
|
|
67
|
-
assert data[
|
|
68
|
-
assert data[
|
|
69
|
-
assert data[
|
|
82
|
+
assert len(data["data"]) == len(resources)
|
|
83
|
+
assert data["total"] == len(resources)
|
|
84
|
+
assert data["page"] == 1
|
|
85
|
+
assert data["page_size"] == DEFAULT_PAGE_SIZE
|
|
86
|
+
assert data["next_page"] == None
|
|
87
|
+
assert data["previous_page"] is None
|
|
70
88
|
|
|
71
89
|
def test_get_missing_param(self):
|
|
72
|
-
|
|
90
|
+
"""Should fetch 1 page of resources from the API using its default parameters"""
|
|
73
91
|
resources = [ResourceFactory() for _ in range(7)]
|
|
74
92
|
dataset = DatasetFactory(resources=resources)
|
|
75
|
-
response = self.get(url_for(
|
|
93
|
+
response = self.get(url_for("apiv2.resources", dataset=dataset.id))
|
|
76
94
|
self.assert200(response)
|
|
77
95
|
data = response.json
|
|
78
|
-
assert len(data[
|
|
79
|
-
assert data[
|
|
80
|
-
assert data[
|
|
81
|
-
assert data[
|
|
82
|
-
assert data[
|
|
83
|
-
assert data[
|
|
96
|
+
assert len(data["data"]) == len(resources)
|
|
97
|
+
assert data["total"] == len(resources)
|
|
98
|
+
assert data["page"] == 1
|
|
99
|
+
assert data["page_size"] == DEFAULT_PAGE_SIZE
|
|
100
|
+
assert data["next_page"] == None
|
|
101
|
+
assert data["previous_page"] is None
|
|
84
102
|
|
|
85
103
|
def test_get_next_page(self):
|
|
86
|
-
|
|
104
|
+
"""Should fetch 2 pages of resources from the API"""
|
|
87
105
|
resources = [ResourceFactory() for _ in range(80)]
|
|
88
106
|
dataset = DatasetFactory(resources=resources)
|
|
89
|
-
response = self.get(
|
|
107
|
+
response = self.get(
|
|
108
|
+
url_for("apiv2.resources", dataset=dataset.id, page=1, page_size=DEFAULT_PAGE_SIZE)
|
|
109
|
+
)
|
|
90
110
|
self.assert200(response)
|
|
91
111
|
data = response.json
|
|
92
|
-
assert len(data[
|
|
93
|
-
assert data[
|
|
94
|
-
assert data[
|
|
95
|
-
assert data[
|
|
96
|
-
assert data[
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
112
|
+
assert len(data["data"]) == DEFAULT_PAGE_SIZE
|
|
113
|
+
assert data["total"] == len(resources)
|
|
114
|
+
assert data["page"] == 1
|
|
115
|
+
assert data["page_size"] == DEFAULT_PAGE_SIZE
|
|
116
|
+
assert data["next_page"] == url_for(
|
|
117
|
+
"apiv2.resources",
|
|
118
|
+
dataset=dataset.id,
|
|
119
|
+
page=2,
|
|
120
|
+
page_size=DEFAULT_PAGE_SIZE,
|
|
121
|
+
_external=True,
|
|
122
|
+
)
|
|
123
|
+
assert data["previous_page"] is None
|
|
124
|
+
|
|
125
|
+
response = self.get(data["next_page"])
|
|
100
126
|
self.assert200(response)
|
|
101
127
|
data = response.json
|
|
102
|
-
assert len(data[
|
|
103
|
-
assert data[
|
|
104
|
-
assert data[
|
|
105
|
-
assert data[
|
|
106
|
-
assert data[
|
|
107
|
-
assert data[
|
|
128
|
+
assert len(data["data"]) == len(resources) - DEFAULT_PAGE_SIZE
|
|
129
|
+
assert data["total"] == len(resources)
|
|
130
|
+
assert data["page"] == 2
|
|
131
|
+
assert data["page_size"] == DEFAULT_PAGE_SIZE
|
|
132
|
+
assert data["next_page"] == None
|
|
133
|
+
assert data["previous_page"] == url_for(
|
|
134
|
+
"apiv2.resources",
|
|
135
|
+
dataset=dataset.id,
|
|
136
|
+
page=1,
|
|
137
|
+
page_size=DEFAULT_PAGE_SIZE,
|
|
138
|
+
_external=True,
|
|
139
|
+
)
|
|
108
140
|
|
|
109
141
|
def test_get_specific_type(self):
|
|
110
|
-
|
|
142
|
+
"""Should fetch resources of type main from the API"""
|
|
111
143
|
nb_resources__of_specific_type = 80
|
|
112
144
|
resources = [ResourceFactory() for _ in range(40)]
|
|
113
|
-
resources += [ResourceFactory(type=
|
|
145
|
+
resources += [ResourceFactory(type="main") for _ in range(nb_resources__of_specific_type)]
|
|
114
146
|
dataset = DatasetFactory(resources=resources)
|
|
115
147
|
# Try without resource type filter
|
|
116
|
-
response = self.get(
|
|
148
|
+
response = self.get(
|
|
149
|
+
url_for("apiv2.resources", dataset=dataset.id, page=1, page_size=DEFAULT_PAGE_SIZE)
|
|
150
|
+
)
|
|
117
151
|
self.assert200(response)
|
|
118
152
|
data = response.json
|
|
119
|
-
assert len(data[
|
|
120
|
-
assert data[
|
|
121
|
-
assert data[
|
|
122
|
-
assert data[
|
|
123
|
-
assert data[
|
|
124
|
-
|
|
153
|
+
assert len(data["data"]) == DEFAULT_PAGE_SIZE
|
|
154
|
+
assert data["total"] == len(resources)
|
|
155
|
+
assert data["page"] == 1
|
|
156
|
+
assert data["page_size"] == DEFAULT_PAGE_SIZE
|
|
157
|
+
assert data["next_page"] == url_for(
|
|
158
|
+
"apiv2.resources",
|
|
159
|
+
dataset=dataset.id,
|
|
160
|
+
page=2,
|
|
161
|
+
page_size=DEFAULT_PAGE_SIZE,
|
|
162
|
+
_external=True,
|
|
163
|
+
)
|
|
164
|
+
assert data["previous_page"] is None
|
|
125
165
|
|
|
126
166
|
# Try with resource type filter
|
|
127
|
-
response = self.get(
|
|
167
|
+
response = self.get(
|
|
168
|
+
url_for(
|
|
169
|
+
"apiv2.resources",
|
|
170
|
+
dataset=dataset.id,
|
|
171
|
+
page=1,
|
|
172
|
+
page_size=DEFAULT_PAGE_SIZE,
|
|
173
|
+
type="main",
|
|
174
|
+
)
|
|
175
|
+
)
|
|
128
176
|
self.assert200(response)
|
|
129
177
|
data = response.json
|
|
130
|
-
assert len(data[
|
|
131
|
-
assert data[
|
|
132
|
-
assert data[
|
|
133
|
-
assert data[
|
|
134
|
-
assert data[
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
178
|
+
assert len(data["data"]) == DEFAULT_PAGE_SIZE
|
|
179
|
+
assert data["total"] == nb_resources__of_specific_type
|
|
180
|
+
assert data["page"] == 1
|
|
181
|
+
assert data["page_size"] == DEFAULT_PAGE_SIZE
|
|
182
|
+
assert data["next_page"] == url_for(
|
|
183
|
+
"apiv2.resources",
|
|
184
|
+
dataset=dataset.id,
|
|
185
|
+
page=2,
|
|
186
|
+
page_size=DEFAULT_PAGE_SIZE,
|
|
187
|
+
type="main",
|
|
188
|
+
_external=True,
|
|
189
|
+
)
|
|
190
|
+
assert data["previous_page"] is None
|
|
191
|
+
|
|
192
|
+
response = self.get(data["next_page"])
|
|
138
193
|
self.assert200(response)
|
|
139
194
|
data = response.json
|
|
140
|
-
assert len(data[
|
|
141
|
-
assert data[
|
|
142
|
-
assert data[
|
|
143
|
-
assert data[
|
|
144
|
-
assert data[
|
|
145
|
-
assert data[
|
|
195
|
+
assert len(data["data"]) == nb_resources__of_specific_type - DEFAULT_PAGE_SIZE
|
|
196
|
+
assert data["total"] == nb_resources__of_specific_type
|
|
197
|
+
assert data["page"] == 2
|
|
198
|
+
assert data["page_size"] == DEFAULT_PAGE_SIZE
|
|
199
|
+
assert data["next_page"] == None
|
|
200
|
+
assert data["previous_page"] == url_for(
|
|
201
|
+
"apiv2.resources",
|
|
202
|
+
dataset=dataset.id,
|
|
203
|
+
page=1,
|
|
204
|
+
page_size=DEFAULT_PAGE_SIZE,
|
|
205
|
+
type="main",
|
|
206
|
+
_external=True,
|
|
207
|
+
)
|
|
146
208
|
|
|
147
209
|
def test_get_with_query_string(self):
|
|
148
|
-
|
|
210
|
+
"""Should fetch resources according to query string from the API"""
|
|
149
211
|
nb_resources_with_specific_title = 20
|
|
150
212
|
resources = [ResourceFactory() for _ in range(40)]
|
|
151
213
|
for i in range(nb_resources_with_specific_title):
|
|
152
|
-
resources += [
|
|
214
|
+
resources += [
|
|
215
|
+
ResourceFactory(title="primary-{0}".format(i))
|
|
216
|
+
if i % 2
|
|
217
|
+
else ResourceFactory(title="secondary-{0}".format(i))
|
|
218
|
+
]
|
|
153
219
|
dataset = DatasetFactory(resources=resources)
|
|
154
220
|
|
|
155
221
|
# Try without query string filter
|
|
156
|
-
response = self.get(
|
|
222
|
+
response = self.get(
|
|
223
|
+
url_for("apiv2.resources", dataset=dataset.id, page=1, page_size=DEFAULT_PAGE_SIZE)
|
|
224
|
+
)
|
|
157
225
|
self.assert200(response)
|
|
158
226
|
data = response.json
|
|
159
|
-
assert len(data[
|
|
160
|
-
assert data[
|
|
161
|
-
assert data[
|
|
162
|
-
assert data[
|
|
163
|
-
assert data[
|
|
164
|
-
|
|
227
|
+
assert len(data["data"]) == DEFAULT_PAGE_SIZE
|
|
228
|
+
assert data["total"] == len(resources)
|
|
229
|
+
assert data["page"] == 1
|
|
230
|
+
assert data["page_size"] == DEFAULT_PAGE_SIZE
|
|
231
|
+
assert data["next_page"] == url_for(
|
|
232
|
+
"apiv2.resources",
|
|
233
|
+
dataset=dataset.id,
|
|
234
|
+
page=2,
|
|
235
|
+
page_size=DEFAULT_PAGE_SIZE,
|
|
236
|
+
_external=True,
|
|
237
|
+
)
|
|
238
|
+
assert data["previous_page"] is None
|
|
165
239
|
|
|
166
240
|
# Try with query string filter
|
|
167
|
-
response = self.get(
|
|
241
|
+
response = self.get(
|
|
242
|
+
url_for(
|
|
243
|
+
"apiv2.resources",
|
|
244
|
+
dataset=dataset.id,
|
|
245
|
+
page=1,
|
|
246
|
+
page_size=DEFAULT_PAGE_SIZE,
|
|
247
|
+
q="primary",
|
|
248
|
+
)
|
|
249
|
+
)
|
|
168
250
|
self.assert200(response)
|
|
169
251
|
data = response.json
|
|
170
|
-
assert len(data[
|
|
171
|
-
assert data[
|
|
172
|
-
assert data[
|
|
173
|
-
assert data[
|
|
174
|
-
assert data[
|
|
175
|
-
assert data[
|
|
252
|
+
assert len(data["data"]) == 10
|
|
253
|
+
assert data["total"] == 10
|
|
254
|
+
assert data["page"] == 1
|
|
255
|
+
assert data["page_size"] == DEFAULT_PAGE_SIZE
|
|
256
|
+
assert data["next_page"] is None
|
|
257
|
+
assert data["previous_page"] is None
|
|
176
258
|
|
|
177
259
|
# Try with query string filter to check case-insensitivity
|
|
178
|
-
response = self.get(
|
|
260
|
+
response = self.get(
|
|
261
|
+
url_for(
|
|
262
|
+
"apiv2.resources",
|
|
263
|
+
dataset=dataset.id,
|
|
264
|
+
page=1,
|
|
265
|
+
page_size=DEFAULT_PAGE_SIZE,
|
|
266
|
+
q="PriMarY",
|
|
267
|
+
)
|
|
268
|
+
)
|
|
179
269
|
self.assert200(response)
|
|
180
270
|
data = response.json
|
|
181
|
-
assert len(data[
|
|
182
|
-
assert data[
|
|
183
|
-
assert data[
|
|
184
|
-
assert data[
|
|
185
|
-
assert data[
|
|
186
|
-
assert data[
|
|
271
|
+
assert len(data["data"]) == 10
|
|
272
|
+
assert data["total"] == 10
|
|
273
|
+
assert data["page"] == 1
|
|
274
|
+
assert data["page_size"] == DEFAULT_PAGE_SIZE
|
|
275
|
+
assert data["next_page"] is None
|
|
276
|
+
assert data["previous_page"] is None
|
|
187
277
|
|
|
188
278
|
|
|
189
279
|
class DatasetExtrasAPITest(APITestCase):
|
|
@@ -194,108 +284,108 @@ class DatasetExtrasAPITest(APITestCase):
|
|
|
194
284
|
self.dataset = DatasetFactory(owner=self.user)
|
|
195
285
|
|
|
196
286
|
def test_get_dataset_extras(self):
|
|
197
|
-
Dataset.extras.register(
|
|
198
|
-
self.dataset.extras = {
|
|
287
|
+
Dataset.extras.register("check::date", db.DateTimeField)
|
|
288
|
+
self.dataset.extras = {
|
|
289
|
+
"test::extra": "test-value",
|
|
290
|
+
"check::date": datetime.fromisoformat("2024-04-14 08:42:00"),
|
|
291
|
+
}
|
|
199
292
|
self.dataset.save()
|
|
200
|
-
response = self.get(url_for(
|
|
293
|
+
response = self.get(url_for("apiv2.dataset_extras", dataset=self.dataset))
|
|
201
294
|
self.assert200(response)
|
|
202
295
|
data = response.json
|
|
203
|
-
assert data[
|
|
204
|
-
assert data[
|
|
296
|
+
assert data["test::extra"] == "test-value"
|
|
297
|
+
assert data["check::date"] == "2024-04-14T08:42:00"
|
|
205
298
|
|
|
206
299
|
def test_update_dataset_extras(self):
|
|
207
300
|
self.dataset.extras = {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
301
|
+
"test::extra": "test-value",
|
|
302
|
+
"test::extra-second": "test-value-second",
|
|
303
|
+
"test::none-will-be-deleted": "test-value",
|
|
211
304
|
}
|
|
212
305
|
self.dataset.save()
|
|
213
306
|
|
|
214
|
-
data = [
|
|
215
|
-
response = self.put(url_for(
|
|
307
|
+
data = ["test::extra-second", "another::key"]
|
|
308
|
+
response = self.put(url_for("apiv2.dataset_extras", dataset=self.dataset), data)
|
|
216
309
|
self.assert400(response)
|
|
217
|
-
assert response.json[
|
|
310
|
+
assert response.json["message"] == "Wrong payload format, dict expected"
|
|
218
311
|
|
|
219
312
|
data = {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
313
|
+
"test::extra-second": "test-value-changed",
|
|
314
|
+
"another::key": "another-value",
|
|
315
|
+
"test::none": None,
|
|
316
|
+
"test::none-will-be-deleted": None,
|
|
224
317
|
}
|
|
225
318
|
|
|
226
319
|
# We don't expect post save signals on extras update
|
|
227
320
|
unexpected_signals = Dataset.after_save, Dataset.on_update
|
|
228
321
|
with assert_not_emit(*unexpected_signals):
|
|
229
|
-
response = self.put(url_for(
|
|
322
|
+
response = self.put(url_for("apiv2.dataset_extras", dataset=self.dataset), data)
|
|
230
323
|
self.assert200(response)
|
|
231
324
|
|
|
232
325
|
self.dataset.reload()
|
|
233
|
-
assert self.dataset.extras[
|
|
234
|
-
assert self.dataset.extras[
|
|
235
|
-
assert self.dataset.extras[
|
|
236
|
-
assert
|
|
237
|
-
assert
|
|
326
|
+
assert self.dataset.extras["test::extra"] == "test-value"
|
|
327
|
+
assert self.dataset.extras["test::extra-second"] == "test-value-changed"
|
|
328
|
+
assert self.dataset.extras["another::key"] == "another-value"
|
|
329
|
+
assert "test::none" not in self.dataset.extras
|
|
330
|
+
assert "test::none-will-be-deleted" not in self.dataset.extras
|
|
238
331
|
|
|
239
332
|
def test_delete_dataset_extras(self):
|
|
240
|
-
self.dataset.extras = {
|
|
333
|
+
self.dataset.extras = {"test::extra": "test-value", "another::key": "another-value"}
|
|
241
334
|
self.dataset.save()
|
|
242
335
|
|
|
243
|
-
data = {
|
|
244
|
-
response = self.delete(url_for(
|
|
336
|
+
data = {"another::key": "another-value"}
|
|
337
|
+
response = self.delete(url_for("apiv2.dataset_extras", dataset=self.dataset), data)
|
|
245
338
|
self.assert400(response)
|
|
246
|
-
assert response.json[
|
|
339
|
+
assert response.json["message"] == "Wrong payload format, list expected"
|
|
247
340
|
|
|
248
|
-
data = [
|
|
341
|
+
data = ["another::key"]
|
|
249
342
|
|
|
250
343
|
# We don't expect post save signals on extras update
|
|
251
344
|
unexpected_signals = Dataset.after_save, Dataset.on_update
|
|
252
345
|
with assert_not_emit(*unexpected_signals):
|
|
253
|
-
response = self.delete(url_for(
|
|
346
|
+
response = self.delete(url_for("apiv2.dataset_extras", dataset=self.dataset), data)
|
|
254
347
|
self.assert204(response)
|
|
255
348
|
|
|
256
349
|
self.dataset.reload()
|
|
257
350
|
assert len(self.dataset.extras) == 1
|
|
258
|
-
assert self.dataset.extras[
|
|
351
|
+
assert self.dataset.extras["test::extra"] == "test-value"
|
|
259
352
|
|
|
260
353
|
def test_dataset_custom_extras_str(self):
|
|
261
|
-
member = Member(user=self.user, role=
|
|
354
|
+
member = Member(user=self.user, role="admin")
|
|
262
355
|
org = OrganizationFactory(members=[member])
|
|
263
356
|
org.extras = {
|
|
264
357
|
"custom": [
|
|
265
358
|
{
|
|
266
359
|
"title": "color",
|
|
267
360
|
"description": "the banner color of the dataset (Hex code)",
|
|
268
|
-
"type": "str"
|
|
361
|
+
"type": "str",
|
|
269
362
|
}
|
|
270
363
|
]
|
|
271
364
|
}
|
|
272
365
|
org.save()
|
|
273
366
|
dataset = DatasetFactory(organization=org)
|
|
274
367
|
|
|
275
|
-
data = {
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
response = self.put(url_for('apiv2.dataset_extras', dataset=dataset), data)
|
|
368
|
+
data = {"custom:test": "FFFFFFF"}
|
|
369
|
+
response = self.put(url_for("apiv2.dataset_extras", dataset=dataset), data)
|
|
279
370
|
self.assert400(response)
|
|
280
|
-
assert
|
|
371
|
+
assert (
|
|
372
|
+
"Dataset's organization did not define the requested custom metadata"
|
|
373
|
+
in response.json["message"]
|
|
374
|
+
)
|
|
281
375
|
|
|
282
|
-
data = {
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
response = self.put(url_for('apiv2.dataset_extras', dataset=dataset), data)
|
|
376
|
+
data = {"custom:color": 123}
|
|
377
|
+
response = self.put(url_for("apiv2.dataset_extras", dataset=dataset), data)
|
|
286
378
|
self.assert400(response)
|
|
287
|
-
assert
|
|
379
|
+
assert "Custom metadata is not of the right type" in response.json["message"]
|
|
288
380
|
|
|
289
|
-
data = {
|
|
290
|
-
|
|
291
|
-
}
|
|
292
|
-
response = self.put(url_for('apiv2.dataset_extras', dataset=dataset), data)
|
|
381
|
+
data = {"custom:color": "FFFFFFF"}
|
|
382
|
+
response = self.put(url_for("apiv2.dataset_extras", dataset=dataset), data)
|
|
293
383
|
self.assert200(response)
|
|
294
384
|
dataset.reload()
|
|
295
|
-
assert dataset.extras[
|
|
385
|
+
assert dataset.extras["custom:color"] == "FFFFFFF"
|
|
296
386
|
|
|
297
387
|
def test_dataset_custom_extras_choices(self):
|
|
298
|
-
member = Member(user=self.user, role=
|
|
388
|
+
member = Member(user=self.user, role="admin")
|
|
299
389
|
org = OrganizationFactory(members=[member])
|
|
300
390
|
org.extras = {
|
|
301
391
|
"custom": [
|
|
@@ -303,27 +393,23 @@ class DatasetExtrasAPITest(APITestCase):
|
|
|
303
393
|
"title": "color",
|
|
304
394
|
"description": "the colors of the dataset (Hex code)",
|
|
305
395
|
"type": "choice",
|
|
306
|
-
"choices": ["yellow", "blue"]
|
|
396
|
+
"choices": ["yellow", "blue"],
|
|
307
397
|
}
|
|
308
398
|
]
|
|
309
399
|
}
|
|
310
400
|
org.save()
|
|
311
401
|
dataset = DatasetFactory(organization=org)
|
|
312
402
|
|
|
313
|
-
data = {
|
|
314
|
-
|
|
315
|
-
}
|
|
316
|
-
response = self.put(url_for('apiv2.dataset_extras', dataset=dataset), data)
|
|
403
|
+
data = {"custom:color": "FFFFFFF"}
|
|
404
|
+
response = self.put(url_for("apiv2.dataset_extras", dataset=dataset), data)
|
|
317
405
|
self.assert400(response)
|
|
318
|
-
assert
|
|
406
|
+
assert "Custom metadata choice is not defined by organization" in response.json["message"]
|
|
319
407
|
|
|
320
|
-
data = {
|
|
321
|
-
|
|
322
|
-
}
|
|
323
|
-
response = self.put(url_for('apiv2.dataset_extras', dataset=dataset), data)
|
|
408
|
+
data = {"custom:color": "yellow"}
|
|
409
|
+
response = self.put(url_for("apiv2.dataset_extras", dataset=dataset), data)
|
|
324
410
|
self.assert200(response)
|
|
325
411
|
dataset.reload()
|
|
326
|
-
assert dataset.extras[
|
|
412
|
+
assert dataset.extras["custom:color"] == "yellow"
|
|
327
413
|
|
|
328
414
|
|
|
329
415
|
class DatasetResourceExtrasAPITest(APITestCase):
|
|
@@ -334,75 +420,81 @@ class DatasetResourceExtrasAPITest(APITestCase):
|
|
|
334
420
|
self.dataset = DatasetFactory(owner=self.user)
|
|
335
421
|
|
|
336
422
|
def test_get_ressource_extras(self):
|
|
337
|
-
|
|
338
|
-
ResourceMixin.extras.register(
|
|
423
|
+
"""It should fetch a resource from the API"""
|
|
424
|
+
ResourceMixin.extras.register("check:date", db.DateTimeField)
|
|
339
425
|
|
|
340
426
|
resource = ResourceFactory()
|
|
341
|
-
resource.extras = {
|
|
427
|
+
resource.extras = {
|
|
428
|
+
"test::extra": "test-value",
|
|
429
|
+
"check:date": datetime(2023, 4, 20, 13, 57, 31, 289000),
|
|
430
|
+
}
|
|
342
431
|
self.dataset.resources.append(resource)
|
|
343
432
|
self.dataset.save()
|
|
344
|
-
response = self.get(url_for(
|
|
345
|
-
rid=resource.id))
|
|
433
|
+
response = self.get(url_for("apiv2.resource_extras", dataset=self.dataset, rid=resource.id))
|
|
346
434
|
self.assert200(response)
|
|
347
435
|
data = response.json
|
|
348
|
-
assert data[
|
|
349
|
-
assert data[
|
|
436
|
+
assert data["test::extra"] == "test-value"
|
|
437
|
+
assert data["check:date"] == "2023-04-20T13:57:31.289000"
|
|
350
438
|
|
|
351
439
|
def test_update_resource_extras(self):
|
|
352
440
|
resource = ResourceFactory()
|
|
353
441
|
resource.extras = {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
442
|
+
"test::extra": "test-value",
|
|
443
|
+
"test::extra-second": "test-value-second",
|
|
444
|
+
"test::none-will-be-deleted": "test-value",
|
|
357
445
|
}
|
|
358
446
|
self.dataset.resources.append(resource)
|
|
359
447
|
self.dataset.save()
|
|
360
448
|
|
|
361
|
-
data = [
|
|
362
|
-
response = self.put(
|
|
363
|
-
|
|
449
|
+
data = ["test::extra-second", "another::key"]
|
|
450
|
+
response = self.put(
|
|
451
|
+
url_for("apiv2.resource_extras", dataset=self.dataset, rid=resource.id), data
|
|
452
|
+
)
|
|
364
453
|
self.assert400(response)
|
|
365
|
-
assert response.json[
|
|
454
|
+
assert response.json["message"] == "Wrong payload format, dict expected"
|
|
366
455
|
|
|
367
456
|
data = {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
457
|
+
"test::extra-second": "test-value-changed",
|
|
458
|
+
"another::key": "another-value",
|
|
459
|
+
"test::none": None,
|
|
460
|
+
"test::none-will-be-deleted": None,
|
|
372
461
|
}
|
|
373
462
|
# We don't expect post save signals on extras update
|
|
374
463
|
unexpected_signals = Dataset.after_save, Dataset.on_update
|
|
375
464
|
with assert_not_emit(*unexpected_signals):
|
|
376
|
-
response = self.put(
|
|
377
|
-
|
|
465
|
+
response = self.put(
|
|
466
|
+
url_for("apiv2.resource_extras", dataset=self.dataset, rid=resource.id), data
|
|
467
|
+
)
|
|
378
468
|
self.assert200(response)
|
|
379
469
|
|
|
380
470
|
self.dataset.reload()
|
|
381
|
-
assert self.dataset.resources[0].extras[
|
|
382
|
-
assert self.dataset.resources[0].extras[
|
|
383
|
-
assert self.dataset.resources[0].extras[
|
|
384
|
-
assert
|
|
385
|
-
assert
|
|
471
|
+
assert self.dataset.resources[0].extras["test::extra"] == "test-value"
|
|
472
|
+
assert self.dataset.resources[0].extras["test::extra-second"] == "test-value-changed"
|
|
473
|
+
assert self.dataset.resources[0].extras["another::key"] == "another-value"
|
|
474
|
+
assert "test::none" not in self.dataset.resources[0].extras
|
|
475
|
+
assert "test::none-will-be-deleted" not in self.dataset.resources[0].extras
|
|
386
476
|
|
|
387
477
|
def test_delete_resource_extras(self):
|
|
388
478
|
resource = ResourceFactory()
|
|
389
|
-
resource.extras = {
|
|
479
|
+
resource.extras = {"test::extra": "test-value", "another::key": "another-value"}
|
|
390
480
|
self.dataset.resources.append(resource)
|
|
391
481
|
self.dataset.save()
|
|
392
482
|
|
|
393
|
-
data = {
|
|
394
|
-
response = self.delete(
|
|
395
|
-
|
|
483
|
+
data = {"another::key": "another-value"}
|
|
484
|
+
response = self.delete(
|
|
485
|
+
url_for("apiv2.resource_extras", dataset=self.dataset, rid=resource.id), data
|
|
486
|
+
)
|
|
396
487
|
self.assert400(response)
|
|
397
|
-
assert response.json[
|
|
488
|
+
assert response.json["message"] == "Wrong payload format, list expected"
|
|
398
489
|
|
|
399
|
-
data = [
|
|
490
|
+
data = ["another::key"]
|
|
400
491
|
# We don't expect post save signals on extras update
|
|
401
492
|
unexpected_signals = Dataset.after_save, Dataset.on_update
|
|
402
493
|
with assert_not_emit(*unexpected_signals):
|
|
403
|
-
response = self.delete(
|
|
404
|
-
|
|
494
|
+
response = self.delete(
|
|
495
|
+
url_for("apiv2.resource_extras", dataset=self.dataset, rid=resource.id), data
|
|
496
|
+
)
|
|
405
497
|
self.assert204(response)
|
|
406
498
|
self.dataset.reload()
|
|
407
499
|
assert len(self.dataset.resources[0].extras) == 1
|
|
408
|
-
assert self.dataset.resources[0].extras[
|
|
500
|
+
assert self.dataset.resources[0].extras["test::extra"] == "test-value"
|