rucio 32.8.6__py3-none-any.whl → 35.8.0__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 rucio might be problematic. Click here for more details.

Files changed (502) hide show
  1. rucio/__init__.py +0 -1
  2. rucio/alembicrevision.py +1 -2
  3. rucio/client/__init__.py +0 -1
  4. rucio/client/accountclient.py +45 -25
  5. rucio/client/accountlimitclient.py +37 -9
  6. rucio/client/baseclient.py +199 -154
  7. rucio/client/client.py +2 -3
  8. rucio/client/configclient.py +19 -6
  9. rucio/client/credentialclient.py +9 -4
  10. rucio/client/didclient.py +238 -63
  11. rucio/client/diracclient.py +13 -5
  12. rucio/client/downloadclient.py +162 -51
  13. rucio/client/exportclient.py +4 -4
  14. rucio/client/fileclient.py +3 -4
  15. rucio/client/importclient.py +4 -4
  16. rucio/client/lifetimeclient.py +21 -5
  17. rucio/client/lockclient.py +18 -8
  18. rucio/client/{metaclient.py → metaconventionsclient.py} +18 -15
  19. rucio/client/pingclient.py +0 -1
  20. rucio/client/replicaclient.py +15 -5
  21. rucio/client/requestclient.py +35 -19
  22. rucio/client/rseclient.py +133 -51
  23. rucio/client/ruleclient.py +29 -22
  24. rucio/client/scopeclient.py +8 -6
  25. rucio/client/subscriptionclient.py +47 -35
  26. rucio/client/touchclient.py +8 -4
  27. rucio/client/uploadclient.py +166 -82
  28. rucio/common/__init__.py +0 -1
  29. rucio/common/cache.py +4 -4
  30. rucio/common/config.py +52 -47
  31. rucio/common/constants.py +69 -2
  32. rucio/common/constraints.py +0 -1
  33. rucio/common/didtype.py +24 -22
  34. rucio/common/dumper/__init__.py +70 -41
  35. rucio/common/dumper/consistency.py +26 -22
  36. rucio/common/dumper/data_models.py +16 -23
  37. rucio/common/dumper/path_parsing.py +0 -1
  38. rucio/common/exception.py +281 -222
  39. rucio/common/extra.py +0 -1
  40. rucio/common/logging.py +54 -38
  41. rucio/common/pcache.py +122 -101
  42. rucio/common/plugins.py +153 -0
  43. rucio/common/policy.py +4 -4
  44. rucio/common/schema/__init__.py +17 -10
  45. rucio/common/schema/atlas.py +7 -5
  46. rucio/common/schema/belleii.py +7 -5
  47. rucio/common/schema/domatpc.py +7 -5
  48. rucio/common/schema/escape.py +7 -5
  49. rucio/common/schema/generic.py +8 -6
  50. rucio/common/schema/generic_multi_vo.py +7 -5
  51. rucio/common/schema/icecube.py +7 -5
  52. rucio/common/stomp_utils.py +0 -1
  53. rucio/common/stopwatch.py +0 -1
  54. rucio/common/test_rucio_server.py +2 -2
  55. rucio/common/types.py +262 -17
  56. rucio/common/utils.py +743 -451
  57. rucio/core/__init__.py +0 -1
  58. rucio/core/account.py +99 -29
  59. rucio/core/account_counter.py +89 -24
  60. rucio/core/account_limit.py +90 -24
  61. rucio/core/authentication.py +86 -29
  62. rucio/core/config.py +108 -38
  63. rucio/core/credential.py +14 -7
  64. rucio/core/did.py +680 -782
  65. rucio/core/did_meta_plugins/__init__.py +8 -6
  66. rucio/core/did_meta_plugins/did_column_meta.py +17 -12
  67. rucio/core/did_meta_plugins/did_meta_plugin_interface.py +60 -11
  68. rucio/core/did_meta_plugins/filter_engine.py +90 -50
  69. rucio/core/did_meta_plugins/json_meta.py +41 -16
  70. rucio/core/did_meta_plugins/mongo_meta.py +25 -8
  71. rucio/core/did_meta_plugins/postgres_meta.py +3 -4
  72. rucio/core/dirac.py +46 -17
  73. rucio/core/distance.py +66 -43
  74. rucio/core/exporter.py +5 -5
  75. rucio/core/heartbeat.py +181 -81
  76. rucio/core/identity.py +22 -12
  77. rucio/core/importer.py +23 -12
  78. rucio/core/lifetime_exception.py +32 -32
  79. rucio/core/lock.py +244 -142
  80. rucio/core/message.py +79 -38
  81. rucio/core/{meta.py → meta_conventions.py} +57 -44
  82. rucio/core/monitor.py +19 -13
  83. rucio/core/naming_convention.py +68 -27
  84. rucio/core/nongrid_trace.py +17 -5
  85. rucio/core/oidc.py +151 -29
  86. rucio/core/permission/__init__.py +18 -6
  87. rucio/core/permission/atlas.py +50 -35
  88. rucio/core/permission/belleii.py +6 -5
  89. rucio/core/permission/escape.py +8 -6
  90. rucio/core/permission/generic.py +82 -80
  91. rucio/core/permission/generic_multi_vo.py +9 -7
  92. rucio/core/quarantined_replica.py +91 -58
  93. rucio/core/replica.py +1303 -772
  94. rucio/core/replica_sorter.py +10 -12
  95. rucio/core/request.py +1133 -285
  96. rucio/core/rse.py +142 -102
  97. rucio/core/rse_counter.py +49 -18
  98. rucio/core/rse_expression_parser.py +6 -7
  99. rucio/core/rse_selector.py +41 -16
  100. rucio/core/rule.py +1538 -474
  101. rucio/core/rule_grouping.py +213 -68
  102. rucio/core/scope.py +50 -22
  103. rucio/core/subscription.py +92 -44
  104. rucio/core/topology.py +66 -24
  105. rucio/core/trace.py +42 -28
  106. rucio/core/transfer.py +543 -259
  107. rucio/core/vo.py +36 -18
  108. rucio/core/volatile_replica.py +59 -32
  109. rucio/daemons/__init__.py +0 -1
  110. rucio/daemons/abacus/__init__.py +0 -1
  111. rucio/daemons/abacus/account.py +29 -19
  112. rucio/daemons/abacus/collection_replica.py +21 -10
  113. rucio/daemons/abacus/rse.py +22 -12
  114. rucio/daemons/atropos/__init__.py +0 -1
  115. rucio/daemons/atropos/atropos.py +1 -2
  116. rucio/daemons/auditor/__init__.py +56 -28
  117. rucio/daemons/auditor/hdfs.py +17 -6
  118. rucio/daemons/auditor/srmdumps.py +116 -45
  119. rucio/daemons/automatix/__init__.py +0 -1
  120. rucio/daemons/automatix/automatix.py +30 -18
  121. rucio/daemons/badreplicas/__init__.py +0 -1
  122. rucio/daemons/badreplicas/minos.py +29 -18
  123. rucio/daemons/badreplicas/minos_temporary_expiration.py +5 -7
  124. rucio/daemons/badreplicas/necromancer.py +9 -13
  125. rucio/daemons/bb8/__init__.py +0 -1
  126. rucio/daemons/bb8/bb8.py +10 -13
  127. rucio/daemons/bb8/common.py +151 -154
  128. rucio/daemons/bb8/nuclei_background_rebalance.py +15 -9
  129. rucio/daemons/bb8/t2_background_rebalance.py +15 -8
  130. rucio/daemons/c3po/__init__.py +0 -1
  131. rucio/daemons/c3po/algorithms/__init__.py +0 -1
  132. rucio/daemons/c3po/algorithms/simple.py +8 -5
  133. rucio/daemons/c3po/algorithms/t2_free_space.py +10 -7
  134. rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +10 -7
  135. rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +30 -15
  136. rucio/daemons/c3po/c3po.py +81 -52
  137. rucio/daemons/c3po/collectors/__init__.py +0 -1
  138. rucio/daemons/c3po/collectors/agis.py +17 -17
  139. rucio/daemons/c3po/collectors/free_space.py +32 -13
  140. rucio/daemons/c3po/collectors/jedi_did.py +14 -5
  141. rucio/daemons/c3po/collectors/mock_did.py +11 -6
  142. rucio/daemons/c3po/collectors/network_metrics.py +12 -4
  143. rucio/daemons/c3po/collectors/workload.py +21 -19
  144. rucio/daemons/c3po/utils/__init__.py +0 -1
  145. rucio/daemons/c3po/utils/dataset_cache.py +15 -5
  146. rucio/daemons/c3po/utils/expiring_dataset_cache.py +16 -5
  147. rucio/daemons/c3po/utils/expiring_list.py +6 -7
  148. rucio/daemons/c3po/utils/popularity.py +5 -2
  149. rucio/daemons/c3po/utils/timeseries.py +25 -12
  150. rucio/daemons/cache/__init__.py +0 -1
  151. rucio/daemons/cache/consumer.py +21 -15
  152. rucio/daemons/common.py +42 -18
  153. rucio/daemons/conveyor/__init__.py +0 -1
  154. rucio/daemons/conveyor/common.py +69 -37
  155. rucio/daemons/conveyor/finisher.py +83 -46
  156. rucio/daemons/conveyor/poller.py +101 -69
  157. rucio/daemons/conveyor/preparer.py +35 -28
  158. rucio/daemons/conveyor/receiver.py +64 -21
  159. rucio/daemons/conveyor/stager.py +33 -28
  160. rucio/daemons/conveyor/submitter.py +71 -47
  161. rucio/daemons/conveyor/throttler.py +99 -35
  162. rucio/daemons/follower/__init__.py +0 -1
  163. rucio/daemons/follower/follower.py +12 -8
  164. rucio/daemons/hermes/__init__.py +0 -1
  165. rucio/daemons/hermes/hermes.py +57 -21
  166. rucio/daemons/judge/__init__.py +0 -1
  167. rucio/daemons/judge/cleaner.py +27 -17
  168. rucio/daemons/judge/evaluator.py +31 -18
  169. rucio/daemons/judge/injector.py +31 -23
  170. rucio/daemons/judge/repairer.py +28 -18
  171. rucio/daemons/oauthmanager/__init__.py +0 -1
  172. rucio/daemons/oauthmanager/oauthmanager.py +7 -8
  173. rucio/daemons/reaper/__init__.py +0 -1
  174. rucio/daemons/reaper/dark_reaper.py +15 -9
  175. rucio/daemons/reaper/reaper.py +109 -67
  176. rucio/daemons/replicarecoverer/__init__.py +0 -1
  177. rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +255 -116
  178. rucio/{api → daemons/rsedecommissioner}/__init__.py +0 -1
  179. rucio/daemons/rsedecommissioner/config.py +81 -0
  180. rucio/daemons/rsedecommissioner/profiles/__init__.py +24 -0
  181. rucio/daemons/rsedecommissioner/profiles/atlas.py +60 -0
  182. rucio/daemons/rsedecommissioner/profiles/generic.py +451 -0
  183. rucio/daemons/rsedecommissioner/profiles/types.py +92 -0
  184. rucio/daemons/rsedecommissioner/rse_decommissioner.py +280 -0
  185. rucio/daemons/storage/__init__.py +0 -1
  186. rucio/daemons/storage/consistency/__init__.py +0 -1
  187. rucio/daemons/storage/consistency/actions.py +152 -59
  188. rucio/daemons/tracer/__init__.py +0 -1
  189. rucio/daemons/tracer/kronos.py +47 -24
  190. rucio/daemons/transmogrifier/__init__.py +0 -1
  191. rucio/daemons/transmogrifier/transmogrifier.py +35 -26
  192. rucio/daemons/undertaker/__init__.py +0 -1
  193. rucio/daemons/undertaker/undertaker.py +10 -10
  194. rucio/db/__init__.py +0 -1
  195. rucio/db/sqla/__init__.py +16 -2
  196. rucio/db/sqla/constants.py +10 -1
  197. rucio/db/sqla/migrate_repo/__init__.py +0 -1
  198. rucio/db/sqla/migrate_repo/env.py +0 -1
  199. rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +0 -1
  200. rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +0 -3
  201. rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +1 -3
  202. rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +0 -3
  203. rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +1 -3
  204. rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +1 -3
  205. rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +0 -3
  206. rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +1 -4
  207. rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +0 -1
  208. rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +0 -2
  209. rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +0 -1
  210. rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +0 -1
  211. rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +0 -2
  212. rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +0 -1
  213. rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +1 -3
  214. rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +0 -1
  215. rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +0 -3
  216. rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +0 -1
  217. rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +1 -2
  218. rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +0 -1
  219. rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +0 -3
  220. rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +1 -3
  221. rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +1 -4
  222. rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +0 -2
  223. rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +0 -3
  224. rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +0 -3
  225. rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +1 -2
  226. rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +0 -1
  227. rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +0 -1
  228. rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +0 -2
  229. rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +0 -3
  230. rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +1 -3
  231. rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +0 -2
  232. rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +1 -4
  233. rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +0 -3
  234. rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +1 -4
  235. rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +0 -1
  236. rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +1 -3
  237. rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +0 -2
  238. rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +1 -3
  239. rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +1 -3
  240. rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +1 -2
  241. rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +1 -3
  242. rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +1 -3
  243. rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +0 -2
  244. rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +1 -3
  245. rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +2 -3
  246. rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +0 -3
  247. rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +1 -4
  248. rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +0 -1
  249. rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +0 -1
  250. rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +0 -3
  251. rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +0 -1
  252. rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +0 -2
  253. rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +0 -3
  254. rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +0 -2
  255. rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +2 -4
  256. rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +0 -2
  257. rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +1 -3
  258. rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +1 -4
  259. rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +0 -3
  260. rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +0 -3
  261. rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +0 -2
  262. rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +1 -3
  263. rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +0 -3
  264. rucio/db/sqla/migrate_repo/versions/4df2c5ddabc0_remove_temporary_dids.py +55 -0
  265. rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +0 -2
  266. rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +0 -2
  267. rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +0 -3
  268. rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +0 -3
  269. rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +0 -3
  270. rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +1 -2
  271. rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +0 -3
  272. rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +1 -3
  273. rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +1 -3
  274. rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +1 -2
  275. rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +0 -3
  276. rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +0 -3
  277. rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +1 -2
  278. rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +2 -4
  279. rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +0 -1
  280. rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +1 -4
  281. rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +0 -2
  282. rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +0 -3
  283. rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +1 -2
  284. rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +0 -3
  285. rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +1 -3
  286. rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +0 -3
  287. rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +0 -1
  288. rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +1 -2
  289. rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +0 -2
  290. rucio/db/sqla/migrate_repo/versions/a08fa8de1545_transfer_stats_table.py +55 -0
  291. rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +1 -3
  292. rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +0 -2
  293. rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +1 -4
  294. rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +0 -1
  295. rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +0 -1
  296. rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +1 -3
  297. rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +1 -4
  298. rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +0 -1
  299. rucio/db/sqla/migrate_repo/versions/b0070f3695c8_add_deletedidmeta_table.py +57 -0
  300. rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +0 -3
  301. rucio/db/sqla/migrate_repo/versions/b5493606bbf5_fix_primary_key_for_subscription_history.py +41 -0
  302. rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +1 -2
  303. rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +1 -3
  304. rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +0 -3
  305. rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +1 -5
  306. rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +1 -3
  307. rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +0 -3
  308. rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +1 -3
  309. rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +1 -2
  310. rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +0 -3
  311. rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +1 -4
  312. rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +1 -2
  313. rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +1 -4
  314. rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +1 -3
  315. rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +1 -4
  316. rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +0 -2
  317. rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +1 -3
  318. rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +0 -3
  319. rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +1 -3
  320. rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +0 -1
  321. rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +1 -2
  322. rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +1 -3
  323. rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +0 -2
  324. rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +0 -1
  325. rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +1 -2
  326. rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +0 -3
  327. rucio/db/sqla/models.py +122 -216
  328. rucio/db/sqla/sautils.py +12 -5
  329. rucio/db/sqla/session.py +71 -43
  330. rucio/db/sqla/types.py +3 -4
  331. rucio/db/sqla/util.py +91 -69
  332. rucio/gateway/__init__.py +13 -0
  333. rucio/{api → gateway}/account.py +119 -46
  334. rucio/{api → gateway}/account_limit.py +12 -13
  335. rucio/{api → gateway}/authentication.py +106 -33
  336. rucio/{api → gateway}/config.py +12 -13
  337. rucio/{api → gateway}/credential.py +15 -4
  338. rucio/{api → gateway}/did.py +384 -140
  339. rucio/{api → gateway}/dirac.py +16 -6
  340. rucio/{api → gateway}/exporter.py +3 -4
  341. rucio/{api → gateway}/heartbeat.py +17 -5
  342. rucio/{api → gateway}/identity.py +63 -19
  343. rucio/{api → gateway}/importer.py +3 -4
  344. rucio/{api → gateway}/lifetime_exception.py +35 -10
  345. rucio/{api → gateway}/lock.py +34 -12
  346. rucio/{api/meta.py → gateway/meta_conventions.py} +18 -16
  347. rucio/{api → gateway}/permission.py +4 -5
  348. rucio/{api → gateway}/quarantined_replica.py +13 -4
  349. rucio/{api → gateway}/replica.py +12 -11
  350. rucio/{api → gateway}/request.py +129 -28
  351. rucio/{api → gateway}/rse.py +11 -12
  352. rucio/{api → gateway}/rule.py +117 -35
  353. rucio/{api → gateway}/scope.py +24 -14
  354. rucio/{api → gateway}/subscription.py +65 -43
  355. rucio/{api → gateway}/vo.py +17 -7
  356. rucio/rse/__init__.py +3 -4
  357. rucio/rse/protocols/__init__.py +0 -1
  358. rucio/rse/protocols/bittorrent.py +184 -0
  359. rucio/rse/protocols/cache.py +1 -2
  360. rucio/rse/protocols/dummy.py +1 -2
  361. rucio/rse/protocols/gfal.py +12 -10
  362. rucio/rse/protocols/globus.py +7 -7
  363. rucio/rse/protocols/gsiftp.py +2 -3
  364. rucio/rse/protocols/http_cache.py +1 -2
  365. rucio/rse/protocols/mock.py +1 -2
  366. rucio/rse/protocols/ngarc.py +1 -2
  367. rucio/rse/protocols/posix.py +12 -13
  368. rucio/rse/protocols/protocol.py +116 -52
  369. rucio/rse/protocols/rclone.py +6 -7
  370. rucio/rse/protocols/rfio.py +4 -5
  371. rucio/rse/protocols/srm.py +9 -10
  372. rucio/rse/protocols/ssh.py +8 -9
  373. rucio/rse/protocols/storm.py +2 -3
  374. rucio/rse/protocols/webdav.py +17 -14
  375. rucio/rse/protocols/xrootd.py +23 -17
  376. rucio/rse/rsemanager.py +19 -7
  377. rucio/tests/__init__.py +0 -1
  378. rucio/tests/common.py +43 -17
  379. rucio/tests/common_server.py +3 -3
  380. rucio/transfertool/__init__.py +0 -1
  381. rucio/transfertool/bittorrent.py +199 -0
  382. rucio/transfertool/bittorrent_driver.py +52 -0
  383. rucio/transfertool/bittorrent_driver_qbittorrent.py +133 -0
  384. rucio/transfertool/fts3.py +250 -138
  385. rucio/transfertool/fts3_plugins.py +152 -0
  386. rucio/transfertool/globus.py +9 -8
  387. rucio/transfertool/globus_library.py +1 -2
  388. rucio/transfertool/mock.py +21 -12
  389. rucio/transfertool/transfertool.py +33 -24
  390. rucio/vcsversion.py +4 -4
  391. rucio/version.py +5 -13
  392. rucio/web/__init__.py +0 -1
  393. rucio/web/rest/__init__.py +0 -1
  394. rucio/web/rest/flaskapi/__init__.py +0 -1
  395. rucio/web/rest/flaskapi/authenticated_bp.py +0 -1
  396. rucio/web/rest/flaskapi/v1/__init__.py +0 -1
  397. rucio/web/rest/flaskapi/v1/accountlimits.py +15 -13
  398. rucio/web/rest/flaskapi/v1/accounts.py +49 -48
  399. rucio/web/rest/flaskapi/v1/archives.py +12 -10
  400. rucio/web/rest/flaskapi/v1/auth.py +146 -144
  401. rucio/web/rest/flaskapi/v1/common.py +82 -41
  402. rucio/web/rest/flaskapi/v1/config.py +5 -6
  403. rucio/web/rest/flaskapi/v1/credentials.py +7 -8
  404. rucio/web/rest/flaskapi/v1/dids.py +158 -28
  405. rucio/web/rest/flaskapi/v1/dirac.py +8 -8
  406. rucio/web/rest/flaskapi/v1/export.py +3 -5
  407. rucio/web/rest/flaskapi/v1/heartbeats.py +3 -5
  408. rucio/web/rest/flaskapi/v1/identities.py +3 -5
  409. rucio/web/rest/flaskapi/v1/import.py +3 -4
  410. rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +6 -9
  411. rucio/web/rest/flaskapi/v1/locks.py +2 -4
  412. rucio/web/rest/flaskapi/v1/main.py +10 -2
  413. rucio/web/rest/flaskapi/v1/{meta.py → meta_conventions.py} +26 -11
  414. rucio/web/rest/flaskapi/v1/metrics.py +1 -2
  415. rucio/web/rest/flaskapi/v1/nongrid_traces.py +4 -4
  416. rucio/web/rest/flaskapi/v1/ping.py +6 -7
  417. rucio/web/rest/flaskapi/v1/redirect.py +8 -9
  418. rucio/web/rest/flaskapi/v1/replicas.py +43 -19
  419. rucio/web/rest/flaskapi/v1/requests.py +178 -21
  420. rucio/web/rest/flaskapi/v1/rses.py +61 -26
  421. rucio/web/rest/flaskapi/v1/rules.py +48 -18
  422. rucio/web/rest/flaskapi/v1/scopes.py +3 -5
  423. rucio/web/rest/flaskapi/v1/subscriptions.py +22 -18
  424. rucio/web/rest/flaskapi/v1/traces.py +4 -4
  425. rucio/web/rest/flaskapi/v1/types.py +20 -0
  426. rucio/web/rest/flaskapi/v1/vos.py +3 -5
  427. rucio/web/rest/main.py +0 -1
  428. rucio/web/rest/metrics.py +0 -1
  429. rucio/web/rest/ping.py +27 -0
  430. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/ldap.cfg.template +1 -1
  431. rucio-35.8.0.data/data/rucio/requirements.server.txt +268 -0
  432. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/tools/bootstrap.py +3 -3
  433. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/tools/merge_rucio_configs.py +2 -5
  434. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/tools/reset_database.py +3 -3
  435. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio +87 -85
  436. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-abacus-account +0 -1
  437. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-abacus-collection-replica +0 -1
  438. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-abacus-rse +0 -1
  439. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-admin +45 -32
  440. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-atropos +0 -1
  441. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-auditor +13 -7
  442. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-automatix +1 -2
  443. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-bb8 +0 -1
  444. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-c3po +0 -1
  445. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-cache-client +2 -3
  446. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-cache-consumer +0 -1
  447. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-finisher +1 -2
  448. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-poller +0 -1
  449. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-preparer +0 -1
  450. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-receiver +0 -1
  451. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-stager +0 -1
  452. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-submitter +2 -3
  453. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-throttler +0 -1
  454. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-dark-reaper +0 -1
  455. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-dumper +11 -10
  456. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-follower +0 -1
  457. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-hermes +0 -1
  458. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-cleaner +0 -1
  459. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-evaluator +2 -3
  460. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-injector +0 -1
  461. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-repairer +0 -1
  462. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-kronos +1 -3
  463. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-minos +0 -1
  464. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-minos-temporary-expiration +0 -1
  465. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-necromancer +1 -2
  466. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-oauth-manager +2 -3
  467. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-reaper +0 -1
  468. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-replica-recoverer +6 -7
  469. rucio-35.8.0.data/scripts/rucio-rse-decommissioner +66 -0
  470. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-storage-consistency-actions +0 -1
  471. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-transmogrifier +0 -1
  472. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-undertaker +1 -2
  473. rucio-35.8.0.dist-info/METADATA +72 -0
  474. rucio-35.8.0.dist-info/RECORD +493 -0
  475. {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/WHEEL +1 -1
  476. {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/licenses/AUTHORS.rst +3 -0
  477. rucio/api/temporary_did.py +0 -49
  478. rucio/common/schema/cms.py +0 -478
  479. rucio/common/schema/lsst.py +0 -423
  480. rucio/core/permission/cms.py +0 -1166
  481. rucio/core/temporary_did.py +0 -188
  482. rucio/daemons/reaper/light_reaper.py +0 -255
  483. rucio/web/rest/flaskapi/v1/tmp_dids.py +0 -115
  484. rucio-32.8.6.data/data/rucio/requirements.txt +0 -55
  485. rucio-32.8.6.data/scripts/rucio-light-reaper +0 -53
  486. rucio-32.8.6.dist-info/METADATA +0 -83
  487. rucio-32.8.6.dist-info/RECORD +0 -481
  488. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/alembic.ini.template +0 -0
  489. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/alembic_offline.ini.template +0 -0
  490. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/globus-config.yml.template +0 -0
  491. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
  492. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
  493. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
  494. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
  495. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
  496. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
  497. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
  498. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rucio.cfg.atlas.client.template +0 -0
  499. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rucio.cfg.template +0 -0
  500. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rucio_multi_vo.cfg.template +0 -0
  501. {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/licenses/LICENSE +0 -0
  502. {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
1
  # Copyright European Organization for Nuclear Research (CERN) since 2012
3
2
  #
4
3
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,7 +19,7 @@ import re
20
19
  from configparser import NoOptionError, NoSectionError
21
20
  from functools import wraps
22
21
  from time import time
23
- from typing import TYPE_CHECKING
22
+ from typing import TYPE_CHECKING, Any, Literal, Optional, TypeVar, Union
24
23
 
25
24
  import flask
26
25
  from flask.views import MethodView
@@ -28,30 +27,36 @@ from werkzeug.datastructures import Headers
28
27
  from werkzeug.exceptions import HTTPException
29
28
  from werkzeug.wrappers import Request, Response
30
29
 
31
- from rucio.api.authentication import validate_auth_token
32
30
  from rucio.common import config
33
- from rucio.common.exception import DatabaseException, RucioException, CannotAuthenticate, UnsupportedRequestedContentType
31
+ from rucio.common.exception import CannotAuthenticate, DatabaseException, IdentityError, RucioException, UnsupportedRequestedContentType
34
32
  from rucio.common.schema import get_schema_value
35
33
  from rucio.common.utils import generate_uuid, render_json
36
34
  from rucio.core.vo import map_vo
35
+ from rucio.gateway.authentication import validate_auth_token
36
+ from rucio.gateway.identity import get_default_account, list_accounts_for_identity, verify_identity
37
37
 
38
38
  if TYPE_CHECKING:
39
- from collections.abc import Callable, Iterable, Sequence
40
- from typing import Optional, Union, Any
39
+ from collections.abc import Callable, Iterable
41
40
 
42
- HeadersType = Union[Headers, dict[str, str], Sequence[tuple[str, str]]]
41
+ from _typeshed import SupportsIter
42
+ from _typeshed.wsgi import StartResponse, WSGIApplication, WSGIEnvironment
43
+ from flask.typing import ResponseReturnValue
43
44
 
45
+ from rucio.web.rest.flaskapi.v1.types import HeadersType
44
46
 
45
- class CORSMiddleware(object):
47
+ ResponseTypeVar = TypeVar('ResponseTypeVar', bound=flask.wrappers.Response)
48
+
49
+
50
+ class CORSMiddleware:
46
51
  """
47
52
  WebUI 2.0 makes preflight requests to the API, which are not handled by the API.
48
53
  This middleware intercepts the preflight OPTIONS requests and returns a 200 OK response.
49
54
  """
50
55
 
51
- def __init__(self, app: flask.Flask) -> None:
56
+ def __init__(self, app: 'WSGIApplication') -> None:
52
57
  self.app = app
53
58
 
54
- def __call__(self, environ: dict, start_response: 'Callable') -> 'Union[Response, Iterable[bytes]]':
59
+ def __call__(self, environ: 'WSGIEnvironment', start_response: 'StartResponse') -> 'Iterable[bytes]':
55
60
  request: Request = Request(environ)
56
61
 
57
62
  if request.environ.get('REQUEST_METHOD') == 'OPTIONS':
@@ -59,12 +64,12 @@ class CORSMiddleware(object):
59
64
  webui_urls = config.config_get_list('webui', 'urls')
60
65
  except (NoOptionError, NoSectionError, RuntimeError) as error:
61
66
  logging.exception('Could not get webui urls from config file')
62
- return str(error), 500
67
+ return str(error), 500 # type: ignore (return type incompatible with Flask middleware)
63
68
  if request.origin in webui_urls:
64
69
  response: Response = Response(status=200)
65
70
  response.headers['Access-Control-Allow-Origin'] = request.origin
66
71
  response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
67
- response.headers['Access-Control-Allow-Headers'] = '*'
72
+ response.headers['Access-Control-Allow-Headers'] = request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS') # type: ignore (value could be None)
68
73
  response.headers['Access-Control-Allow-Credentials'] = 'true'
69
74
  return response(environ, start_response)
70
75
  response: Response = Response(status=403)
@@ -80,11 +85,11 @@ class ErrorHandlingMethodView(MethodView):
80
85
  Exceptions for all defined methods automatically.
81
86
  """
82
87
 
83
- def get_headers(self) -> "Optional[HeadersType]":
88
+ def get_headers(self) -> Optional['HeadersType']:
84
89
  """Can be overridden to add headers to generic error responses."""
85
90
  return None
86
91
 
87
- def dispatch_request(self, *args, **kwargs):
92
+ def dispatch_request(self, *args, **kwargs) -> Union['ResponseReturnValue', flask.wrappers.Response]:
88
93
  headers = self.get_headers() or None
89
94
  try:
90
95
  return super(ErrorHandlingMethodView, self).dispatch_request(*args, **kwargs)
@@ -108,7 +113,7 @@ class ErrorHandlingMethodView(MethodView):
108
113
  return generate_http_error_flask(
109
114
  status_code=500,
110
115
  exc=error.__class__.__name__,
111
- exc_msg='An unknown Database Exception has ocurred.',
116
+ exc_msg='An unknown Database Exception has occurred.',
112
117
  headers=headers
113
118
  )
114
119
 
@@ -132,7 +137,7 @@ class ErrorHandlingMethodView(MethodView):
132
137
  return str(error), 500
133
138
 
134
139
 
135
- def request_auth_env():
140
+ def request_auth_env() -> Optional['ResponseReturnValue']:
136
141
  if flask.request.environ.get('REQUEST_METHOD') == 'OPTIONS':
137
142
  return '', 200
138
143
 
@@ -155,9 +160,9 @@ def request_auth_env():
155
160
  flask.request.environ['start_time'] = time()
156
161
 
157
162
 
158
- def response_headers(response):
159
- response.headers['Access-Control-Allow-Origin'] = flask.request.environ.get('HTTP_ORIGIN')
160
- response.headers['Access-Control-Allow-Headers'] = flask.request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS')
163
+ def response_headers(response: ResponseTypeVar) -> ResponseTypeVar:
164
+ response.headers['Access-Control-Allow-Origin'] = flask.request.environ.get('HTTP_ORIGIN') # type: ignore (value could be None)
165
+ response.headers['Access-Control-Allow-Headers'] = flask.request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS') # type: ignore (value could be None)
161
166
  response.headers['Access-Control-Allow-Methods'] = '*'
162
167
  response.headers['Access-Control-Allow-Credentials'] = 'true'
163
168
 
@@ -169,7 +174,7 @@ def response_headers(response):
169
174
  return response
170
175
 
171
176
 
172
- def check_accept_header_wrapper_flask(supported_content_types):
177
+ def check_accept_header_wrapper_flask(supported_content_types: 'Iterable[str]'):
173
178
  """ Decorator to check if an endpoint supports the requested content type. """
174
179
 
175
180
  def wrapper(f):
@@ -195,7 +200,7 @@ def check_accept_header_wrapper_flask(supported_content_types):
195
200
  return wrapper
196
201
 
197
202
 
198
- def parse_scope_name(scope_name, vo):
203
+ def parse_scope_name(scope_name: str, vo: Optional[str]) -> tuple[str, ...]:
199
204
  """
200
205
  Parses the given scope_name according to the schema's
201
206
  SCOPE_NAME_REGEXP and returns a (scope, name) tuple.
@@ -205,14 +210,20 @@ def parse_scope_name(scope_name, vo):
205
210
  :raises ValueError: when scope_name could not be parsed.
206
211
  :returns: a (scope, name) tuple.
207
212
  """
213
+ if not vo:
214
+ vo = 'def'
215
+
208
216
  # why again does that regex start with a slash?
209
- scope_name = re.match(get_schema_value('SCOPE_NAME_REGEXP', vo), '/' + scope_name)
210
- if scope_name is None:
217
+ scope_regex = re.match(get_schema_value('SCOPE_NAME_REGEXP', vo), '/' + scope_name)
218
+ if scope_regex is None:
211
219
  raise ValueError('cannot parse scope and name')
212
- return scope_name.group(1, 2)
220
+ return scope_regex.group(1, 2)
213
221
 
214
222
 
215
- def try_stream(generator, content_type=None) -> "flask.Response":
223
+ def try_stream(
224
+ generator: 'SupportsIter',
225
+ content_type: Optional[str] = None
226
+ ) -> flask.Response:
216
227
  """
217
228
  Peeks at the first element of the passed generator and raises
218
229
  an error, if yielding raises. Otherwise returns
@@ -234,15 +245,12 @@ def try_stream(generator, content_type=None) -> "flask.Response":
234
245
  return flask.Response('', content_type=content_type)
235
246
 
236
247
 
237
- def error_headers(exc_cls: str, exc_msg):
238
- def strip_newlines(msg):
239
- if msg is None:
240
- return None
241
-
248
+ def error_headers(exc_cls: str, exc_msg: str) -> dict[str, str]:
249
+ def strip_newlines(msg: str) -> str:
242
250
  return msg.replace('\n', ' ').replace('\r', ' ')
243
251
 
244
- exc_msg = strip_newlines(exc_msg)
245
252
  if exc_msg:
253
+ exc_msg = strip_newlines(exc_msg)
246
254
  # Truncate too long exc_msg
247
255
  oldlen = len(exc_msg)
248
256
  exc_msg = exc_msg[:min(oldlen, 125)]
@@ -254,7 +262,7 @@ def error_headers(exc_cls: str, exc_msg):
254
262
  }
255
263
 
256
264
 
257
- def _error_response(exc_cls, exc_msg):
265
+ def _error_response(exc_cls: str, exc_msg: str) -> tuple[dict[str, str], dict[str, str]]:
258
266
  data = {'ExceptionClass': exc_cls,
259
267
  'ExceptionMessage': exc_msg}
260
268
  headers = {'Content-Type': 'application/octet-stream'}
@@ -263,12 +271,12 @@ def _error_response(exc_cls, exc_msg):
263
271
 
264
272
 
265
273
  def generate_http_error_flask(
266
- status_code: "int",
267
- exc: "Union[str, BaseException]",
268
- exc_msg: "Optional[str]" = None,
269
- headers: "Optional[HeadersType]" = None,
274
+ status_code: int,
275
+ exc: Union[str, BaseException],
276
+ exc_msg: Optional[str] = None,
277
+ headers: Optional['HeadersType'] = None,
270
278
  ) -> "flask.Response":
271
- """Utitily function to generate a complete HTTP error response.
279
+ """Utility function to generate a complete HTTP error response.
272
280
 
273
281
  :param status_code: The HTTP status code to generate a response for.
274
282
  :param exc: The name of the exception class or a RucioException object.
@@ -322,13 +330,13 @@ def json_list(json_loads: "Callable[[str], Any]" = json.loads, optional: bool =
322
330
 
323
331
 
324
332
  def json_parse(types: tuple, json_loads: "Callable[[str], Any]" = json.loads, **kwargs):
325
- def clstostr(cls):
333
+ def clstostr(cls) -> str:
326
334
  if cls.__name__ == "dict":
327
335
  return "dictionary"
328
336
  else:
329
337
  return cls.__name__
330
338
 
331
- def typestostr(_types: tuple):
339
+ def typestostr(_types: tuple) -> str:
332
340
  return " or ".join(map(clstostr, _types))
333
341
 
334
342
  data = flask.request.get_data(as_text=True)
@@ -355,7 +363,7 @@ def json_parse(types: tuple, json_loads: "Callable[[str], Any]" = json.loads, **
355
363
  )
356
364
 
357
365
 
358
- def param_get(parameters: dict, name: str, **kwargs):
366
+ def param_get(parameters: dict[str, Any], name: str, **kwargs) -> Any:
359
367
  if 'default' in kwargs:
360
368
  return parameters.get(name, kwargs['default'])
361
369
  else:
@@ -370,7 +378,7 @@ def param_get(parameters: dict, name: str, **kwargs):
370
378
  return parameters[name]
371
379
 
372
380
 
373
- def extract_vo(headers: "HeadersType") -> "str":
381
+ def extract_vo(headers: Headers) -> str:
374
382
  """ Extract the VO name from the given request.headers object and
375
383
  does any name mapping. Returns the short VO name or raise a
376
384
  flask.abort if the VO name doesn't meet the name specification.
@@ -383,3 +391,36 @@ def extract_vo(headers: "HeadersType") -> "str":
383
391
  except RucioException as err:
384
392
  # VO Name doesn't match allowed spec
385
393
  flask.abort(generate_http_error_flask(status_code=400, exc=err))
394
+
395
+
396
+ def get_account_from_verified_identity(
397
+ identity_key: str,
398
+ id_type: Literal["USERPASS", "X509"],
399
+ password: Optional[str] = None
400
+ ) -> list[str]:
401
+ """ Verifies the provided identity and tries to return a matching account.
402
+ If no account is found, raises an IdentityError after trying to verify the identity.
403
+ If multiple accounts are found, returns the default account if available, otherwise all accounts.
404
+ :param identity_key: The identity key name. For example x509 DN, or a username.
405
+ :param id_type: The type of the authentication (x509, USERPASS).
406
+ :param password: required only if id_type==USERPASS.
407
+ :raises IdentityError: if no account is found for the identity or if the identity could not be verified.
408
+ :returns: a list of account names.
409
+ """
410
+ accounts = list_accounts_for_identity(identity_key=identity_key, id_type=id_type)
411
+ if accounts is None or len(accounts) == 0:
412
+ if id_type == 'USERPASS':
413
+ verify_identity(identity_key=identity_key, id_type=id_type, password=password)
414
+ elif id_type == 'X509':
415
+ verify_identity(identity_key=identity_key, id_type=id_type)
416
+ else:
417
+ raise IdentityError('No account found for identity')
418
+ if len(accounts) > 1:
419
+ try:
420
+ default_account = get_default_account(identity_key=identity_key, id_type=id_type)
421
+ return [default_account]
422
+ except IdentityError:
423
+ return accounts
424
+ else:
425
+ account = accounts[0]
426
+ return [account]
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
1
  # Copyright European Organization for Nuclear Research (CERN) since 2012
3
2
  #
4
3
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,13 +12,13 @@
13
12
  # See the License for the specific language governing permissions and
14
13
  # limitations under the License.
15
14
 
16
- from flask import Flask, request as request, jsonify
15
+ from flask import Flask, jsonify
16
+ from flask import request as request
17
17
 
18
- from rucio.api import config
19
- from rucio.common.exception import ConfigurationError, AccessDenied, ConfigNotFound
18
+ from rucio.common.exception import AccessDenied, ConfigNotFound, ConfigurationError
19
+ from rucio.gateway import config
20
20
  from rucio.web.rest.flaskapi.authenticated_bp import AuthenticatedBlueprint
21
- from rucio.web.rest.flaskapi.v1.common import response_headers, check_accept_header_wrapper_flask, \
22
- generate_http_error_flask, ErrorHandlingMethodView, json_parameters
21
+ from rucio.web.rest.flaskapi.v1.common import ErrorHandlingMethodView, check_accept_header_wrapper_flask, generate_http_error_flask, json_parameters, response_headers
23
22
 
24
23
 
25
24
  class Config(ErrorHandlingMethodView):
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
1
  # Copyright European Organization for Nuclear Research (CERN) since 2012
3
2
  #
4
3
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,15 +17,15 @@ from typing import TYPE_CHECKING
18
17
  from flask import Flask, request
19
18
  from werkzeug.datastructures import Headers
20
19
 
21
- from rucio.api.credential import get_signed_url
22
20
  from rucio.common.exception import CannotAuthenticate
21
+ from rucio.gateway.credential import get_signed_url
23
22
  from rucio.web.rest.flaskapi.authenticated_bp import AuthenticatedBlueprint
24
- from rucio.web.rest.flaskapi.v1.common import check_accept_header_wrapper_flask, extract_vo, \
25
- generate_http_error_flask, ErrorHandlingMethodView, response_headers
23
+ from rucio.web.rest.flaskapi.v1.common import ErrorHandlingMethodView, check_accept_header_wrapper_flask, extract_vo, generate_http_error_flask, response_headers
26
24
 
27
25
  if TYPE_CHECKING:
28
26
  from typing import Optional
29
- from rucio.web.rest.flaskapi.v1.common import HeadersType
27
+
28
+ from rucio.web.rest.flaskapi.v1.types import HeadersType
30
29
 
31
30
 
32
31
  class SignURL(ErrorHandlingMethodView):
@@ -61,7 +60,7 @@ class SignURL(ErrorHandlingMethodView):
61
60
  Access-Control-Allow-Headers:
62
61
  schema:
63
62
  type: string
64
- description: The http access controll request headers.
63
+ description: The http access control request headers.
65
64
  Access-Control-Allow-Methods:
66
65
  schema:
67
66
  type: string
@@ -76,7 +75,7 @@ class SignURL(ErrorHandlingMethodView):
76
75
  schema:
77
76
  type: string
78
77
  enum: ['X-Rucio-Auth-Token']
79
- description: The exposed access controll header.
78
+ description: The exposed access control header.
80
79
  404:
81
80
  description: Not found
82
81
  """
@@ -117,7 +116,7 @@ class SignURL(ErrorHandlingMethodView):
117
116
  required: false
118
117
  - name: url
119
118
  in: query
120
- description: The Url of the authentification.
119
+ description: The Url of the authentication.
121
120
  schema:
122
121
  type: string
123
122
  required: true
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
1
  # Copyright European Organization for Nuclear Research (CERN) since 2012
3
2
  #
4
3
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,20 +17,61 @@ from json import dumps
18
17
 
19
18
  from flask import Flask, Response, request
20
19
 
21
- from rucio.api.did import add_did, add_dids, list_content, list_content_history, list_dids, \
22
- list_files, scope_list, get_did, set_metadata, get_metadata, get_metadata_bulk, set_status, attach_dids, \
23
- detach_dids, attach_dids_to_dids, get_dataset_by_guid, list_parent_dids, create_did_sample, list_new_dids, \
24
- resurrect, get_users_following_did, remove_did_from_followed, add_did_to_followed, delete_metadata, \
25
- set_metadata_bulk, set_dids_metadata_bulk
26
- from rucio.api.rule import list_replication_rules, list_associated_replication_rules_for_file
27
- from rucio.common.exception import ScopeNotFound, DatabaseException, DataIdentifierNotFound, DataIdentifierAlreadyExists, \
28
- DuplicateContent, AccessDenied, KeyNotFound, Duplicate, InvalidValueForKey, UnsupportedStatus, \
29
- UnsupportedOperation, RSENotFound, RuleNotFound, InvalidMetadata, InvalidPath, FileAlreadyExists, InvalidObject, FileConsistencyMismatch
30
- from rucio.common.utils import render_json, APIEncoder
20
+ from rucio.common.exception import (
21
+ AccessDenied,
22
+ DatabaseException,
23
+ DataIdentifierAlreadyExists,
24
+ DataIdentifierNotFound,
25
+ Duplicate,
26
+ DuplicateContent,
27
+ FileAlreadyExists,
28
+ FileConsistencyMismatch,
29
+ InvalidMetadata,
30
+ InvalidObject,
31
+ InvalidPath,
32
+ InvalidValueForKey,
33
+ KeyNotFound,
34
+ RSENotFound,
35
+ RuleNotFound,
36
+ ScopeNotFound,
37
+ UnsupportedMetadataPlugin,
38
+ UnsupportedOperation,
39
+ UnsupportedStatus,
40
+ )
41
+ from rucio.common.utils import APIEncoder, parse_response, render_json
31
42
  from rucio.db.sqla.constants import DIDType
43
+ from rucio.gateway.did import (
44
+ add_did,
45
+ add_did_to_followed,
46
+ add_dids,
47
+ attach_dids,
48
+ attach_dids_to_dids,
49
+ bulk_list_files,
50
+ create_did_sample,
51
+ delete_metadata,
52
+ detach_dids,
53
+ get_dataset_by_guid,
54
+ get_did,
55
+ get_metadata,
56
+ get_metadata_bulk,
57
+ get_users_following_did,
58
+ list_content,
59
+ list_content_history,
60
+ list_dids,
61
+ list_files,
62
+ list_new_dids,
63
+ list_parent_dids,
64
+ remove_did_from_followed,
65
+ resurrect,
66
+ scope_list,
67
+ set_dids_metadata_bulk,
68
+ set_metadata,
69
+ set_metadata_bulk,
70
+ set_status,
71
+ )
72
+ from rucio.gateway.rule import list_associated_replication_rules_for_file, list_replication_rules
32
73
  from rucio.web.rest.flaskapi.authenticated_bp import AuthenticatedBlueprint
33
- from rucio.web.rest.flaskapi.v1.common import response_headers, check_accept_header_wrapper_flask, \
34
- parse_scope_name, try_stream, generate_http_error_flask, ErrorHandlingMethodView, json_parameters, json_list, param_get, json_parse
74
+ from rucio.web.rest.flaskapi.v1.common import ErrorHandlingMethodView, check_accept_header_wrapper_flask, generate_http_error_flask, json_list, json_parameters, json_parse, param_get, parse_scope_name, response_headers, try_stream
35
75
 
36
76
 
37
77
  class Scope(ErrorHandlingMethodView):
@@ -69,7 +109,7 @@ class Scope(ErrorHandlingMethodView):
69
109
  content:
70
110
  application/x-json-stream:
71
111
  schema:
72
- description: Line seperated dictionary of dids.
112
+ description: Line separated dictionary of dids.
73
113
  type: array
74
114
  items:
75
115
  type: object
@@ -220,7 +260,7 @@ class Search(ErrorHandlingMethodView):
220
260
  if filters is not None:
221
261
  filters = ast.literal_eval(filters)
222
262
  else:
223
- # backwards compatability for created*, length* and name filters passed through as request args
263
+ # backwards compatibility for created*, length* and name filters passed through as request args
224
264
  filters = {}
225
265
  for arg, value in request.args.copy().items():
226
266
  if arg not in ['type', 'limit', 'long', 'recursive']:
@@ -414,10 +454,11 @@ class Attachments(ErrorHandlingMethodView):
414
454
  if isinstance(parameters, list):
415
455
  attachments = parameters
416
456
  ignore_duplicate = False
417
- else:
418
- assert isinstance(parameters, dict)
457
+ elif isinstance(parameters, dict):
419
458
  attachments = param_get(parameters, 'attachments')
420
459
  ignore_duplicate = param_get(parameters, 'ignore_duplicate', default=False)
460
+ else:
461
+ return generate_http_error_flask(406, exc="Invalid attachment format.")
421
462
 
422
463
  try:
423
464
  attach_dids_to_dids(attachments=attachments, ignore_duplicate=ignore_duplicate, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
@@ -742,7 +783,7 @@ class Attachment(ErrorHandlingMethodView):
742
783
  content:
743
784
  application/x-json-stream:
744
785
  schema:
745
- description: The contents of a did. Items are line seperated.
786
+ description: The contents of a did. Items are line separated.
746
787
  type: array
747
788
  items:
748
789
  type: object
@@ -955,7 +996,7 @@ class AttachmentHistory(ErrorHandlingMethodView):
955
996
  content:
956
997
  application/x-json-stream:
957
998
  schema:
958
- description: The dids with their information and history. Elements are seperated by new line characters.
999
+ description: The dids with their information and history. Elements are separated by new line characters.
959
1000
  type: array
960
1001
  items:
961
1002
  type: object
@@ -1063,7 +1104,7 @@ class Files(ErrorHandlingMethodView):
1063
1104
  description: The adler32 checksum.
1064
1105
  type: string
1065
1106
  lumiblocknr:
1066
- description: The lumi block nr. Only availabe if `long` is defined in the query.
1107
+ description: The lumi block nr. Only available if `long` is defined in the query.
1067
1108
  type: integer
1068
1109
  - description: All replica information.
1069
1110
  type: array
@@ -1111,6 +1152,86 @@ class Files(ErrorHandlingMethodView):
1111
1152
  return generate_http_error_flask(404, error)
1112
1153
 
1113
1154
 
1155
+ class BulkFiles(ErrorHandlingMethodView):
1156
+
1157
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
1158
+ def post(self):
1159
+ """
1160
+ ---
1161
+ summary: List files bulk
1162
+ description: List files in multiple dids
1163
+ tags:
1164
+ - Data Identifiers
1165
+ requestBody:
1166
+ content:
1167
+ application/json:
1168
+ schema:
1169
+ type: array
1170
+ items:
1171
+ description: One did to list files.
1172
+ type: object
1173
+ required:
1174
+ - scope
1175
+ - name
1176
+ properties:
1177
+ scope:
1178
+ description: The did scope.
1179
+ type: string
1180
+ name:
1181
+ description: The did name.
1182
+ type: string
1183
+ responses:
1184
+ 201:
1185
+ description: OK
1186
+ content:
1187
+ application/x-json-stream:
1188
+ schema:
1189
+ description: All collections file content.
1190
+ type: array
1191
+ items:
1192
+ description: Collections file content.
1193
+ type: object
1194
+ properties:
1195
+ parent_scope:
1196
+ description: The scope of the parent did.
1197
+ type: string
1198
+ parent_name:
1199
+ description: The name of the parent did.
1200
+ type: string
1201
+ scope:
1202
+ description: The scope of the did.
1203
+ type: string
1204
+ name:
1205
+ description: The name of the did.
1206
+ type: string
1207
+ bytes:
1208
+ description: The size of the did in bytes.
1209
+ type: integer
1210
+ guid:
1211
+ description: The guid of the did.
1212
+ type: string
1213
+ events:
1214
+ description: The number of events of the did.
1215
+ type: integer
1216
+ adler32:
1217
+ description: The adler32 checksum.
1218
+ type: string
1219
+ 401:
1220
+ description: Invalid Auth Token
1221
+ """
1222
+ parameters = json_parameters(parse_response)
1223
+ dids = param_get(parameters, 'dids', default=[])
1224
+ try:
1225
+ def generate(vo):
1226
+ for did in bulk_list_files(dids=dids, vo=vo):
1227
+ yield render_json(**did) + '\n'
1228
+
1229
+ return try_stream(generate(vo=request.environ.get('vo')))
1230
+ except AccessDenied as error:
1231
+ return generate_http_error_flask(401, error)
1232
+ return 'Created', 201
1233
+
1234
+
1114
1235
  class Parents(ErrorHandlingMethodView):
1115
1236
 
1116
1237
  @check_accept_header_wrapper_flask(['application/x-json-stream'])
@@ -1199,8 +1320,10 @@ class Meta(ErrorHandlingMethodView):
1199
1320
  content:
1200
1321
  application/json:
1201
1322
  schema:
1202
- description: A data identifer with all attributes.
1323
+ description: A data identifier with all attributes.
1203
1324
  type: object
1325
+ 400:
1326
+ description: Bad Request - Invalid metadata plugin specified
1204
1327
  401:
1205
1328
  description: Invalid Auth Token
1206
1329
  404:
@@ -1219,6 +1342,8 @@ class Meta(ErrorHandlingMethodView):
1219
1342
  return Response(render_json(**meta), content_type='application/json')
1220
1343
  except DataIdentifierNotFound as error:
1221
1344
  return generate_http_error_flask(404, error)
1345
+ except UnsupportedMetadataPlugin as error:
1346
+ return generate_http_error_flask(400, error)
1222
1347
 
1223
1348
  def post(self, scope_name):
1224
1349
  """
@@ -1243,7 +1368,7 @@ class Meta(ErrorHandlingMethodView):
1243
1368
  - meta
1244
1369
  properties:
1245
1370
  meta:
1246
- description: The metadata to add. A dictionary containg the metadata name as key and the value as value.
1371
+ description: The metadata to add. A dictionary containing the metadata name as key and the value as value.
1247
1372
  type: object
1248
1373
  recursive:
1249
1374
  description: Flag if the metadata should be applied recirsively to children.
@@ -1390,7 +1515,7 @@ class SingleMeta(ErrorHandlingMethodView):
1390
1515
  406:
1391
1516
  description: Not acceptable
1392
1517
  409:
1393
- description: Matadata already exists
1518
+ description: Metadata already exists
1394
1519
  400:
1395
1520
  description: Invalid key or value
1396
1521
  """
@@ -1526,6 +1651,7 @@ class Rules(ErrorHandlingMethodView):
1526
1651
  scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1527
1652
 
1528
1653
  def generate(vo):
1654
+ get_did(scope=scope, name=name, vo=vo)
1529
1655
  for rule in list_replication_rules({'scope': scope, 'name': name}, vo=vo):
1530
1656
  yield dumps(rule, cls=APIEncoder) + '\n'
1531
1657
 
@@ -1534,6 +1660,8 @@ class Rules(ErrorHandlingMethodView):
1534
1660
  return generate_http_error_flask(400, error)
1535
1661
  except RuleNotFound as error:
1536
1662
  return generate_http_error_flask(404, error)
1663
+ except DataIdentifierNotFound as error:
1664
+ return generate_http_error_flask(404, error)
1537
1665
 
1538
1666
 
1539
1667
  class BulkMeta(ErrorHandlingMethodView):
@@ -1577,7 +1705,7 @@ class BulkMeta(ErrorHandlingMethodView):
1577
1705
  content:
1578
1706
  application/json:
1579
1707
  schema:
1580
- description: A list of metadata identifiers for the dids. Seperated by new lines.
1708
+ description: A list of metadata identifiers for the dids. Separated by new lines.
1581
1709
  type: array
1582
1710
  items:
1583
1711
  description: The metadata for one did.
@@ -1613,7 +1741,7 @@ class AssociatedRules(ErrorHandlingMethodView):
1613
1741
  def get(self, scope_name):
1614
1742
  """
1615
1743
  ---
1616
- summary: Get accociated rules
1744
+ summary: Get associated rules
1617
1745
  description: Gets all associated rules for a file.
1618
1746
  tags:
1619
1747
  - Data Identifiers
@@ -1630,7 +1758,7 @@ class AssociatedRules(ErrorHandlingMethodView):
1630
1758
  content:
1631
1759
  application/x-json-stream:
1632
1760
  schema:
1633
- description: All associated rules for a file. Items are seperated by new line character.
1761
+ description: All associated rules for a file. Items are separated by new line character.
1634
1762
  type: array
1635
1763
  items:
1636
1764
  description: A replication rule associated with the file. Has more fields than listed here.
@@ -1701,7 +1829,7 @@ class GUIDLookup(ErrorHandlingMethodView):
1701
1829
  content:
1702
1830
  application/x-json-stream:
1703
1831
  schema:
1704
- description: A list of all datasets associated with the guid. Items are seperated by new line character.
1832
+ description: A list of all datasets associated with the guid. Items are separated by new line character.
1705
1833
  type: array
1706
1834
  items:
1707
1835
  description: A dataset associated with a guid.
@@ -1905,7 +2033,7 @@ class NewDIDs(ErrorHandlingMethodView):
1905
2033
  content:
1906
2034
  application/x-json-stream:
1907
2035
  schema:
1908
- description: A list of the recent dids. Items are seperated by new line characters.
2036
+ description: A list of the recent dids. Items are separated by new line characters.
1909
2037
  type: array
1910
2038
  items:
1911
2039
  description: A did.
@@ -2192,6 +2320,8 @@ def blueprint():
2192
2320
  bp.add_url_rule('/resurrect', view_func=resurrect_view, methods=['post', ])
2193
2321
  bulkmeta_view = BulkMeta.as_view('bulkmeta')
2194
2322
  bp.add_url_rule('/bulkmeta', view_func=bulkmeta_view, methods=['post', ])
2323
+ files_view = BulkFiles.as_view('bulkfiles')
2324
+ bp.add_url_rule('/bulkfiles', view_func=files_view, methods=['post', ])
2195
2325
 
2196
2326
  bp.after_request(response_headers)
2197
2327
  return bp