rucio 32.8.6__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 (481) hide show
  1. rucio/__init__.py +18 -0
  2. rucio/alembicrevision.py +16 -0
  3. rucio/api/__init__.py +14 -0
  4. rucio/api/account.py +266 -0
  5. rucio/api/account_limit.py +287 -0
  6. rucio/api/authentication.py +302 -0
  7. rucio/api/config.py +218 -0
  8. rucio/api/credential.py +60 -0
  9. rucio/api/did.py +726 -0
  10. rucio/api/dirac.py +71 -0
  11. rucio/api/exporter.py +60 -0
  12. rucio/api/heartbeat.py +62 -0
  13. rucio/api/identity.py +160 -0
  14. rucio/api/importer.py +46 -0
  15. rucio/api/lifetime_exception.py +95 -0
  16. rucio/api/lock.py +131 -0
  17. rucio/api/meta.py +85 -0
  18. rucio/api/permission.py +72 -0
  19. rucio/api/quarantined_replica.py +69 -0
  20. rucio/api/replica.py +528 -0
  21. rucio/api/request.py +220 -0
  22. rucio/api/rse.py +601 -0
  23. rucio/api/rule.py +335 -0
  24. rucio/api/scope.py +89 -0
  25. rucio/api/subscription.py +255 -0
  26. rucio/api/temporary_did.py +49 -0
  27. rucio/api/vo.py +112 -0
  28. rucio/client/__init__.py +16 -0
  29. rucio/client/accountclient.py +413 -0
  30. rucio/client/accountlimitclient.py +155 -0
  31. rucio/client/baseclient.py +929 -0
  32. rucio/client/client.py +77 -0
  33. rucio/client/configclient.py +113 -0
  34. rucio/client/credentialclient.py +54 -0
  35. rucio/client/didclient.py +691 -0
  36. rucio/client/diracclient.py +48 -0
  37. rucio/client/downloadclient.py +1674 -0
  38. rucio/client/exportclient.py +44 -0
  39. rucio/client/fileclient.py +51 -0
  40. rucio/client/importclient.py +42 -0
  41. rucio/client/lifetimeclient.py +74 -0
  42. rucio/client/lockclient.py +99 -0
  43. rucio/client/metaclient.py +137 -0
  44. rucio/client/pingclient.py +45 -0
  45. rucio/client/replicaclient.py +444 -0
  46. rucio/client/requestclient.py +109 -0
  47. rucio/client/rseclient.py +664 -0
  48. rucio/client/ruleclient.py +287 -0
  49. rucio/client/scopeclient.py +88 -0
  50. rucio/client/subscriptionclient.py +161 -0
  51. rucio/client/touchclient.py +78 -0
  52. rucio/client/uploadclient.py +871 -0
  53. rucio/common/__init__.py +14 -0
  54. rucio/common/cache.py +74 -0
  55. rucio/common/config.py +796 -0
  56. rucio/common/constants.py +92 -0
  57. rucio/common/constraints.py +18 -0
  58. rucio/common/didtype.py +187 -0
  59. rucio/common/dumper/__init__.py +306 -0
  60. rucio/common/dumper/consistency.py +449 -0
  61. rucio/common/dumper/data_models.py +325 -0
  62. rucio/common/dumper/path_parsing.py +65 -0
  63. rucio/common/exception.py +1092 -0
  64. rucio/common/extra.py +37 -0
  65. rucio/common/logging.py +404 -0
  66. rucio/common/pcache.py +1387 -0
  67. rucio/common/policy.py +84 -0
  68. rucio/common/schema/__init__.py +143 -0
  69. rucio/common/schema/atlas.py +411 -0
  70. rucio/common/schema/belleii.py +406 -0
  71. rucio/common/schema/cms.py +478 -0
  72. rucio/common/schema/domatpc.py +399 -0
  73. rucio/common/schema/escape.py +424 -0
  74. rucio/common/schema/generic.py +431 -0
  75. rucio/common/schema/generic_multi_vo.py +410 -0
  76. rucio/common/schema/icecube.py +404 -0
  77. rucio/common/schema/lsst.py +423 -0
  78. rucio/common/stomp_utils.py +160 -0
  79. rucio/common/stopwatch.py +56 -0
  80. rucio/common/test_rucio_server.py +148 -0
  81. rucio/common/types.py +158 -0
  82. rucio/common/utils.py +1946 -0
  83. rucio/core/__init__.py +14 -0
  84. rucio/core/account.py +426 -0
  85. rucio/core/account_counter.py +171 -0
  86. rucio/core/account_limit.py +357 -0
  87. rucio/core/authentication.py +563 -0
  88. rucio/core/config.py +386 -0
  89. rucio/core/credential.py +218 -0
  90. rucio/core/did.py +3102 -0
  91. rucio/core/did_meta_plugins/__init__.py +250 -0
  92. rucio/core/did_meta_plugins/did_column_meta.py +326 -0
  93. rucio/core/did_meta_plugins/did_meta_plugin_interface.py +116 -0
  94. rucio/core/did_meta_plugins/filter_engine.py +573 -0
  95. rucio/core/did_meta_plugins/json_meta.py +215 -0
  96. rucio/core/did_meta_plugins/mongo_meta.py +199 -0
  97. rucio/core/did_meta_plugins/postgres_meta.py +317 -0
  98. rucio/core/dirac.py +208 -0
  99. rucio/core/distance.py +164 -0
  100. rucio/core/exporter.py +59 -0
  101. rucio/core/heartbeat.py +263 -0
  102. rucio/core/identity.py +290 -0
  103. rucio/core/importer.py +248 -0
  104. rucio/core/lifetime_exception.py +377 -0
  105. rucio/core/lock.py +474 -0
  106. rucio/core/message.py +241 -0
  107. rucio/core/meta.py +190 -0
  108. rucio/core/monitor.py +441 -0
  109. rucio/core/naming_convention.py +154 -0
  110. rucio/core/nongrid_trace.py +124 -0
  111. rucio/core/oidc.py +1339 -0
  112. rucio/core/permission/__init__.py +107 -0
  113. rucio/core/permission/atlas.py +1333 -0
  114. rucio/core/permission/belleii.py +1076 -0
  115. rucio/core/permission/cms.py +1166 -0
  116. rucio/core/permission/escape.py +1076 -0
  117. rucio/core/permission/generic.py +1128 -0
  118. rucio/core/permission/generic_multi_vo.py +1148 -0
  119. rucio/core/quarantined_replica.py +190 -0
  120. rucio/core/replica.py +3627 -0
  121. rucio/core/replica_sorter.py +368 -0
  122. rucio/core/request.py +2241 -0
  123. rucio/core/rse.py +1835 -0
  124. rucio/core/rse_counter.py +155 -0
  125. rucio/core/rse_expression_parser.py +460 -0
  126. rucio/core/rse_selector.py +277 -0
  127. rucio/core/rule.py +3419 -0
  128. rucio/core/rule_grouping.py +1473 -0
  129. rucio/core/scope.py +152 -0
  130. rucio/core/subscription.py +316 -0
  131. rucio/core/temporary_did.py +188 -0
  132. rucio/core/topology.py +448 -0
  133. rucio/core/trace.py +361 -0
  134. rucio/core/transfer.py +1233 -0
  135. rucio/core/vo.py +151 -0
  136. rucio/core/volatile_replica.py +123 -0
  137. rucio/daemons/__init__.py +14 -0
  138. rucio/daemons/abacus/__init__.py +14 -0
  139. rucio/daemons/abacus/account.py +106 -0
  140. rucio/daemons/abacus/collection_replica.py +113 -0
  141. rucio/daemons/abacus/rse.py +107 -0
  142. rucio/daemons/atropos/__init__.py +14 -0
  143. rucio/daemons/atropos/atropos.py +243 -0
  144. rucio/daemons/auditor/__init__.py +261 -0
  145. rucio/daemons/auditor/hdfs.py +86 -0
  146. rucio/daemons/auditor/srmdumps.py +284 -0
  147. rucio/daemons/automatix/__init__.py +14 -0
  148. rucio/daemons/automatix/automatix.py +281 -0
  149. rucio/daemons/badreplicas/__init__.py +14 -0
  150. rucio/daemons/badreplicas/minos.py +311 -0
  151. rucio/daemons/badreplicas/minos_temporary_expiration.py +173 -0
  152. rucio/daemons/badreplicas/necromancer.py +200 -0
  153. rucio/daemons/bb8/__init__.py +14 -0
  154. rucio/daemons/bb8/bb8.py +356 -0
  155. rucio/daemons/bb8/common.py +762 -0
  156. rucio/daemons/bb8/nuclei_background_rebalance.py +147 -0
  157. rucio/daemons/bb8/t2_background_rebalance.py +146 -0
  158. rucio/daemons/c3po/__init__.py +14 -0
  159. rucio/daemons/c3po/algorithms/__init__.py +14 -0
  160. rucio/daemons/c3po/algorithms/simple.py +131 -0
  161. rucio/daemons/c3po/algorithms/t2_free_space.py +125 -0
  162. rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +127 -0
  163. rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +279 -0
  164. rucio/daemons/c3po/c3po.py +342 -0
  165. rucio/daemons/c3po/collectors/__init__.py +14 -0
  166. rucio/daemons/c3po/collectors/agis.py +108 -0
  167. rucio/daemons/c3po/collectors/free_space.py +62 -0
  168. rucio/daemons/c3po/collectors/jedi_did.py +48 -0
  169. rucio/daemons/c3po/collectors/mock_did.py +46 -0
  170. rucio/daemons/c3po/collectors/network_metrics.py +63 -0
  171. rucio/daemons/c3po/collectors/workload.py +110 -0
  172. rucio/daemons/c3po/utils/__init__.py +14 -0
  173. rucio/daemons/c3po/utils/dataset_cache.py +40 -0
  174. rucio/daemons/c3po/utils/expiring_dataset_cache.py +45 -0
  175. rucio/daemons/c3po/utils/expiring_list.py +63 -0
  176. rucio/daemons/c3po/utils/popularity.py +82 -0
  177. rucio/daemons/c3po/utils/timeseries.py +76 -0
  178. rucio/daemons/cache/__init__.py +14 -0
  179. rucio/daemons/cache/consumer.py +191 -0
  180. rucio/daemons/common.py +391 -0
  181. rucio/daemons/conveyor/__init__.py +14 -0
  182. rucio/daemons/conveyor/common.py +530 -0
  183. rucio/daemons/conveyor/finisher.py +492 -0
  184. rucio/daemons/conveyor/poller.py +372 -0
  185. rucio/daemons/conveyor/preparer.py +198 -0
  186. rucio/daemons/conveyor/receiver.py +206 -0
  187. rucio/daemons/conveyor/stager.py +127 -0
  188. rucio/daemons/conveyor/submitter.py +379 -0
  189. rucio/daemons/conveyor/throttler.py +468 -0
  190. rucio/daemons/follower/__init__.py +14 -0
  191. rucio/daemons/follower/follower.py +97 -0
  192. rucio/daemons/hermes/__init__.py +14 -0
  193. rucio/daemons/hermes/hermes.py +738 -0
  194. rucio/daemons/judge/__init__.py +14 -0
  195. rucio/daemons/judge/cleaner.py +149 -0
  196. rucio/daemons/judge/evaluator.py +172 -0
  197. rucio/daemons/judge/injector.py +154 -0
  198. rucio/daemons/judge/repairer.py +144 -0
  199. rucio/daemons/oauthmanager/__init__.py +14 -0
  200. rucio/daemons/oauthmanager/oauthmanager.py +199 -0
  201. rucio/daemons/reaper/__init__.py +14 -0
  202. rucio/daemons/reaper/dark_reaper.py +272 -0
  203. rucio/daemons/reaper/light_reaper.py +255 -0
  204. rucio/daemons/reaper/reaper.py +701 -0
  205. rucio/daemons/replicarecoverer/__init__.py +14 -0
  206. rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +487 -0
  207. rucio/daemons/storage/__init__.py +14 -0
  208. rucio/daemons/storage/consistency/__init__.py +14 -0
  209. rucio/daemons/storage/consistency/actions.py +753 -0
  210. rucio/daemons/tracer/__init__.py +14 -0
  211. rucio/daemons/tracer/kronos.py +513 -0
  212. rucio/daemons/transmogrifier/__init__.py +14 -0
  213. rucio/daemons/transmogrifier/transmogrifier.py +753 -0
  214. rucio/daemons/undertaker/__init__.py +14 -0
  215. rucio/daemons/undertaker/undertaker.py +137 -0
  216. rucio/db/__init__.py +14 -0
  217. rucio/db/sqla/__init__.py +38 -0
  218. rucio/db/sqla/constants.py +192 -0
  219. rucio/db/sqla/migrate_repo/__init__.py +14 -0
  220. rucio/db/sqla/migrate_repo/env.py +111 -0
  221. rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +71 -0
  222. rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +50 -0
  223. rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +61 -0
  224. rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +46 -0
  225. rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +93 -0
  226. rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +78 -0
  227. rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +46 -0
  228. rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +53 -0
  229. rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +69 -0
  230. rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +42 -0
  231. rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +46 -0
  232. rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +61 -0
  233. rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +42 -0
  234. rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +141 -0
  235. rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +75 -0
  236. rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +75 -0
  237. rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +46 -0
  238. rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +51 -0
  239. rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +135 -0
  240. rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +65 -0
  241. rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +42 -0
  242. rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +66 -0
  243. rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +54 -0
  244. rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +43 -0
  245. rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +46 -0
  246. rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +47 -0
  247. rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +54 -0
  248. rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +39 -0
  249. rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +48 -0
  250. rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +47 -0
  251. rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +48 -0
  252. rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +59 -0
  253. rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +47 -0
  254. rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +72 -0
  255. rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +46 -0
  256. rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +45 -0
  257. rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +48 -0
  258. rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +48 -0
  259. rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +42 -0
  260. rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +69 -0
  261. rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +46 -0
  262. rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +78 -0
  263. rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +62 -0
  264. rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +74 -0
  265. rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +44 -0
  266. rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +67 -0
  267. rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +134 -0
  268. rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +58 -0
  269. rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +79 -0
  270. rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +61 -0
  271. rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +45 -0
  272. rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +46 -0
  273. rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +65 -0
  274. rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +42 -0
  275. rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +46 -0
  276. rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +46 -0
  277. rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +80 -0
  278. rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +43 -0
  279. rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +61 -0
  280. rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +47 -0
  281. rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +46 -0
  282. rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +52 -0
  283. rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +42 -0
  284. rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +65 -0
  285. rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +46 -0
  286. rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +47 -0
  287. rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +45 -0
  288. rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +46 -0
  289. rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +48 -0
  290. rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +50 -0
  291. rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +59 -0
  292. rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +48 -0
  293. rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +108 -0
  294. rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +57 -0
  295. rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +51 -0
  296. rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +50 -0
  297. rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +46 -0
  298. rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +42 -0
  299. rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +93 -0
  300. rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +73 -0
  301. rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +52 -0
  302. rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +45 -0
  303. rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +46 -0
  304. rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +54 -0
  305. rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +48 -0
  306. rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +70 -0
  307. rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +48 -0
  308. rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +95 -0
  309. rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +55 -0
  310. rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +74 -0
  311. rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +78 -0
  312. rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +49 -0
  313. rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +124 -0
  314. rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +60 -0
  315. rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +53 -0
  316. rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +56 -0
  317. rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +67 -0
  318. rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +50 -0
  319. rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +46 -0
  320. rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +92 -0
  321. rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +42 -0
  322. rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +46 -0
  323. rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +147 -0
  324. rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +78 -0
  325. rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +53 -0
  326. rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +74 -0
  327. rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +56 -0
  328. rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +46 -0
  329. rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +68 -0
  330. rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +48 -0
  331. rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +149 -0
  332. rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +106 -0
  333. rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +47 -0
  334. rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +45 -0
  335. rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +105 -0
  336. rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +52 -0
  337. rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +106 -0
  338. rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +30 -0
  339. rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +75 -0
  340. rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +49 -0
  341. rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +45 -0
  342. rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +38 -0
  343. rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +44 -0
  344. rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +46 -0
  345. rucio/db/sqla/models.py +1834 -0
  346. rucio/db/sqla/sautils.py +48 -0
  347. rucio/db/sqla/session.py +470 -0
  348. rucio/db/sqla/types.py +207 -0
  349. rucio/db/sqla/util.py +521 -0
  350. rucio/rse/__init__.py +97 -0
  351. rucio/rse/protocols/__init__.py +14 -0
  352. rucio/rse/protocols/cache.py +123 -0
  353. rucio/rse/protocols/dummy.py +112 -0
  354. rucio/rse/protocols/gfal.py +701 -0
  355. rucio/rse/protocols/globus.py +243 -0
  356. rucio/rse/protocols/gsiftp.py +93 -0
  357. rucio/rse/protocols/http_cache.py +83 -0
  358. rucio/rse/protocols/mock.py +124 -0
  359. rucio/rse/protocols/ngarc.py +210 -0
  360. rucio/rse/protocols/posix.py +251 -0
  361. rucio/rse/protocols/protocol.py +530 -0
  362. rucio/rse/protocols/rclone.py +365 -0
  363. rucio/rse/protocols/rfio.py +137 -0
  364. rucio/rse/protocols/srm.py +339 -0
  365. rucio/rse/protocols/ssh.py +414 -0
  366. rucio/rse/protocols/storm.py +207 -0
  367. rucio/rse/protocols/webdav.py +547 -0
  368. rucio/rse/protocols/xrootd.py +295 -0
  369. rucio/rse/rsemanager.py +752 -0
  370. rucio/tests/__init__.py +14 -0
  371. rucio/tests/common.py +244 -0
  372. rucio/tests/common_server.py +132 -0
  373. rucio/transfertool/__init__.py +14 -0
  374. rucio/transfertool/fts3.py +1484 -0
  375. rucio/transfertool/globus.py +200 -0
  376. rucio/transfertool/globus_library.py +182 -0
  377. rucio/transfertool/mock.py +81 -0
  378. rucio/transfertool/transfertool.py +212 -0
  379. rucio/vcsversion.py +11 -0
  380. rucio/version.py +46 -0
  381. rucio/web/__init__.py +14 -0
  382. rucio/web/rest/__init__.py +14 -0
  383. rucio/web/rest/flaskapi/__init__.py +14 -0
  384. rucio/web/rest/flaskapi/authenticated_bp.py +28 -0
  385. rucio/web/rest/flaskapi/v1/__init__.py +14 -0
  386. rucio/web/rest/flaskapi/v1/accountlimits.py +234 -0
  387. rucio/web/rest/flaskapi/v1/accounts.py +1088 -0
  388. rucio/web/rest/flaskapi/v1/archives.py +100 -0
  389. rucio/web/rest/flaskapi/v1/auth.py +1642 -0
  390. rucio/web/rest/flaskapi/v1/common.py +385 -0
  391. rucio/web/rest/flaskapi/v1/config.py +305 -0
  392. rucio/web/rest/flaskapi/v1/credentials.py +213 -0
  393. rucio/web/rest/flaskapi/v1/dids.py +2204 -0
  394. rucio/web/rest/flaskapi/v1/dirac.py +116 -0
  395. rucio/web/rest/flaskapi/v1/export.py +77 -0
  396. rucio/web/rest/flaskapi/v1/heartbeats.py +129 -0
  397. rucio/web/rest/flaskapi/v1/identities.py +263 -0
  398. rucio/web/rest/flaskapi/v1/import.py +133 -0
  399. rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +315 -0
  400. rucio/web/rest/flaskapi/v1/locks.py +360 -0
  401. rucio/web/rest/flaskapi/v1/main.py +83 -0
  402. rucio/web/rest/flaskapi/v1/meta.py +226 -0
  403. rucio/web/rest/flaskapi/v1/metrics.py +37 -0
  404. rucio/web/rest/flaskapi/v1/nongrid_traces.py +97 -0
  405. rucio/web/rest/flaskapi/v1/ping.py +89 -0
  406. rucio/web/rest/flaskapi/v1/redirect.py +366 -0
  407. rucio/web/rest/flaskapi/v1/replicas.py +1866 -0
  408. rucio/web/rest/flaskapi/v1/requests.py +841 -0
  409. rucio/web/rest/flaskapi/v1/rses.py +2204 -0
  410. rucio/web/rest/flaskapi/v1/rules.py +824 -0
  411. rucio/web/rest/flaskapi/v1/scopes.py +161 -0
  412. rucio/web/rest/flaskapi/v1/subscriptions.py +646 -0
  413. rucio/web/rest/flaskapi/v1/templates/auth_crash.html +80 -0
  414. rucio/web/rest/flaskapi/v1/templates/auth_granted.html +82 -0
  415. rucio/web/rest/flaskapi/v1/tmp_dids.py +115 -0
  416. rucio/web/rest/flaskapi/v1/traces.py +100 -0
  417. rucio/web/rest/flaskapi/v1/vos.py +280 -0
  418. rucio/web/rest/main.py +19 -0
  419. rucio/web/rest/metrics.py +28 -0
  420. rucio-32.8.6.data/data/rucio/etc/alembic.ini.template +71 -0
  421. rucio-32.8.6.data/data/rucio/etc/alembic_offline.ini.template +74 -0
  422. rucio-32.8.6.data/data/rucio/etc/globus-config.yml.template +5 -0
  423. rucio-32.8.6.data/data/rucio/etc/ldap.cfg.template +30 -0
  424. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_approval_request.tmpl +38 -0
  425. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +4 -0
  426. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_approved_user.tmpl +17 -0
  427. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +6 -0
  428. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_denied_user.tmpl +17 -0
  429. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +19 -0
  430. rucio-32.8.6.data/data/rucio/etc/rse-accounts.cfg.template +25 -0
  431. rucio-32.8.6.data/data/rucio/etc/rucio.cfg.atlas.client.template +42 -0
  432. rucio-32.8.6.data/data/rucio/etc/rucio.cfg.template +257 -0
  433. rucio-32.8.6.data/data/rucio/etc/rucio_multi_vo.cfg.template +234 -0
  434. rucio-32.8.6.data/data/rucio/requirements.txt +55 -0
  435. rucio-32.8.6.data/data/rucio/tools/bootstrap.py +34 -0
  436. rucio-32.8.6.data/data/rucio/tools/merge_rucio_configs.py +147 -0
  437. rucio-32.8.6.data/data/rucio/tools/reset_database.py +40 -0
  438. rucio-32.8.6.data/scripts/rucio +2540 -0
  439. rucio-32.8.6.data/scripts/rucio-abacus-account +75 -0
  440. rucio-32.8.6.data/scripts/rucio-abacus-collection-replica +47 -0
  441. rucio-32.8.6.data/scripts/rucio-abacus-rse +79 -0
  442. rucio-32.8.6.data/scripts/rucio-admin +2434 -0
  443. rucio-32.8.6.data/scripts/rucio-atropos +61 -0
  444. rucio-32.8.6.data/scripts/rucio-auditor +199 -0
  445. rucio-32.8.6.data/scripts/rucio-automatix +51 -0
  446. rucio-32.8.6.data/scripts/rucio-bb8 +58 -0
  447. rucio-32.8.6.data/scripts/rucio-c3po +86 -0
  448. rucio-32.8.6.data/scripts/rucio-cache-client +135 -0
  449. rucio-32.8.6.data/scripts/rucio-cache-consumer +43 -0
  450. rucio-32.8.6.data/scripts/rucio-conveyor-finisher +59 -0
  451. rucio-32.8.6.data/scripts/rucio-conveyor-poller +67 -0
  452. rucio-32.8.6.data/scripts/rucio-conveyor-preparer +38 -0
  453. rucio-32.8.6.data/scripts/rucio-conveyor-receiver +44 -0
  454. rucio-32.8.6.data/scripts/rucio-conveyor-stager +77 -0
  455. rucio-32.8.6.data/scripts/rucio-conveyor-submitter +140 -0
  456. rucio-32.8.6.data/scripts/rucio-conveyor-throttler +105 -0
  457. rucio-32.8.6.data/scripts/rucio-dark-reaper +54 -0
  458. rucio-32.8.6.data/scripts/rucio-dumper +159 -0
  459. rucio-32.8.6.data/scripts/rucio-follower +45 -0
  460. rucio-32.8.6.data/scripts/rucio-hermes +55 -0
  461. rucio-32.8.6.data/scripts/rucio-judge-cleaner +90 -0
  462. rucio-32.8.6.data/scripts/rucio-judge-evaluator +138 -0
  463. rucio-32.8.6.data/scripts/rucio-judge-injector +45 -0
  464. rucio-32.8.6.data/scripts/rucio-judge-repairer +45 -0
  465. rucio-32.8.6.data/scripts/rucio-kronos +45 -0
  466. rucio-32.8.6.data/scripts/rucio-light-reaper +53 -0
  467. rucio-32.8.6.data/scripts/rucio-minos +54 -0
  468. rucio-32.8.6.data/scripts/rucio-minos-temporary-expiration +51 -0
  469. rucio-32.8.6.data/scripts/rucio-necromancer +121 -0
  470. rucio-32.8.6.data/scripts/rucio-oauth-manager +64 -0
  471. rucio-32.8.6.data/scripts/rucio-reaper +84 -0
  472. rucio-32.8.6.data/scripts/rucio-replica-recoverer +249 -0
  473. rucio-32.8.6.data/scripts/rucio-storage-consistency-actions +75 -0
  474. rucio-32.8.6.data/scripts/rucio-transmogrifier +78 -0
  475. rucio-32.8.6.data/scripts/rucio-undertaker +77 -0
  476. rucio-32.8.6.dist-info/METADATA +83 -0
  477. rucio-32.8.6.dist-info/RECORD +481 -0
  478. rucio-32.8.6.dist-info/WHEEL +5 -0
  479. rucio-32.8.6.dist-info/licenses/AUTHORS.rst +94 -0
  480. rucio-32.8.6.dist-info/licenses/LICENSE +201 -0
  481. rucio-32.8.6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2204 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import ast
17
+ from json import dumps
18
+
19
+ from flask import Flask, Response, request
20
+
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
31
+ from rucio.db.sqla.constants import DIDType
32
+ 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
35
+
36
+
37
+ class Scope(ErrorHandlingMethodView):
38
+
39
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
40
+ def get(self, scope):
41
+ """
42
+ ---
43
+ summary: Get Data Identifier
44
+ description: Return all data identifiers in the given scope.
45
+ tags:
46
+ - Data Identifiers
47
+ parameters:
48
+ - name: scope
49
+ in: path
50
+ description: The scope.
51
+ schema:
52
+ type: string
53
+ style: simple
54
+ requestBody:
55
+ content:
56
+ application/json:
57
+ schema:
58
+ type: object
59
+ properties:
60
+ name:
61
+ description: The name of the did.
62
+ type: string
63
+ recursive:
64
+ description: If specified, also returns the child ids recursively.
65
+ type: boolean
66
+ responses:
67
+ 200:
68
+ description: OK
69
+ content:
70
+ application/x-json-stream:
71
+ schema:
72
+ description: Line seperated dictionary of dids.
73
+ type: array
74
+ items:
75
+ type: object
76
+ description: Data identifier
77
+ properties:
78
+ scope:
79
+ type: string
80
+ description: The scope of the did.
81
+ name:
82
+ type: string
83
+ description: The name of the did.
84
+ type:
85
+ type: string
86
+ description: The type of the did.
87
+ enum: ['F', 'D', 'C', 'A', 'X', 'Y', 'Z']
88
+ parent:
89
+ type: string
90
+ description: The parent of the did.
91
+ level:
92
+ type: integer
93
+ description: The level of the did.
94
+ 401:
95
+ description: Invalid Auth Token
96
+ 404:
97
+ description: No Dids found
98
+ 406:
99
+ description: Not acceptable
100
+ """
101
+ try:
102
+ def generate(name, recursive, vo):
103
+ for did in scope_list(scope=scope, name=name, recursive=recursive, vo=vo):
104
+ yield render_json(**did) + '\n'
105
+
106
+ recursive = 'recursive' in request.args
107
+
108
+ return try_stream(
109
+ generate(
110
+ name=request.args.get('name', default=None),
111
+ recursive=recursive,
112
+ vo=request.environ.get('vo')
113
+ )
114
+ )
115
+ except DataIdentifierNotFound as error:
116
+ return generate_http_error_flask(404, error)
117
+
118
+
119
+ class Search(ErrorHandlingMethodView):
120
+
121
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
122
+ def get(self, scope):
123
+ """
124
+ ---
125
+ summary: List Data identifier
126
+ description: List all data identifiers in a scope which match a given metadata.
127
+ tags:
128
+ - Data Identifiers
129
+ parameters:
130
+ - name: scope
131
+ in: path
132
+ description: The scope of the data identifiers.
133
+ schema:
134
+ type: string
135
+ style: simple
136
+ - name: type
137
+ in: query
138
+ description: The did type to search for.
139
+ schema:
140
+ type: string
141
+ enum: ['all', 'collection', 'container', 'dataset', 'file']
142
+ default: 'collection'
143
+ - name: limit
144
+ in: query
145
+ description: The maximum number od dids returned.
146
+ schema:
147
+ type: integer
148
+ - name: long
149
+ in: query
150
+ description: Provides a longer output, otherwise just prints names.
151
+ schema:
152
+ type: boolean
153
+ default: false
154
+ - name: recursive
155
+ in: query
156
+ description: Recursively list chilred.
157
+ schema:
158
+ type: boolean
159
+ - name: created_before
160
+ in: query
161
+ description: Date string in RFC-1123 format where the creation date was earlier.
162
+ schema:
163
+ type: string
164
+ - name: created_after
165
+ in: query
166
+ description: Date string in RFC-1123 format where the creation date was later.
167
+ schema:
168
+ type: string
169
+ - name: length
170
+ in: query
171
+ description: Exact number of attached DIDs.
172
+ schema:
173
+ type: integer
174
+ - name: length.gt
175
+ in: query
176
+ description: Number of attached DIDs greater than.
177
+ schema:
178
+ type: integer
179
+ - name: length.lt
180
+ in: query
181
+ description: Number of attached DIDs less than.
182
+ schema:
183
+ type: integer
184
+ - name: length.gte
185
+ in: query
186
+ description: Number of attached DIDs greater than or equal to
187
+ schema:
188
+ type: integer
189
+ - name: length.lte
190
+ in: query
191
+ description: Number of attached DIDs less than or equal to.
192
+ schema:
193
+ type: integer
194
+ - name: name
195
+ in: query
196
+ description: Name or pattern of a did.
197
+ schema:
198
+ type: string
199
+ responses:
200
+ 200:
201
+ description: OK
202
+ content:
203
+ application/x-json-stream:
204
+ schema:
205
+ description: Line separated name of DIDs or dictionaries of DIDs for long option.
206
+ type: array
207
+ items:
208
+ type: object
209
+ description: the name of a DID or a dictionarie of a DID for long option.
210
+ 401:
211
+ description: Invalid Auth Token
212
+ 404:
213
+ description: Invalid key in filter.
214
+ 406:
215
+ description: Not acceptable
216
+ 409:
217
+ description: Wrong did type
218
+ """
219
+ filters = request.args.get('filters', default=None)
220
+ if filters is not None:
221
+ filters = ast.literal_eval(filters)
222
+ else:
223
+ # backwards compatability for created*, length* and name filters passed through as request args
224
+ filters = {}
225
+ for arg, value in request.args.copy().items():
226
+ if arg not in ['type', 'limit', 'long', 'recursive']:
227
+ filters[arg] = value
228
+ filters = [filters]
229
+
230
+ did_type = request.args.get('type', default=None)
231
+ limit = request.args.get('limit', default=None)
232
+ long = request.args.get('long', type=['True', '1'].__contains__, default=False)
233
+ recursive = request.args.get('recursive', type='True'.__eq__, default=False)
234
+ try:
235
+ def generate(vo):
236
+ for did in list_dids(scope=scope, filters=filters, did_type=did_type, limit=limit, long=long, recursive=recursive, vo=vo):
237
+ yield dumps(did) + '\n'
238
+ return try_stream(generate(vo=request.environ.get('vo')))
239
+ except UnsupportedOperation as error:
240
+ return generate_http_error_flask(409, error)
241
+ except KeyNotFound as error:
242
+ return generate_http_error_flask(404, error)
243
+
244
+
245
+ class BulkDIDS(ErrorHandlingMethodView):
246
+
247
+ @check_accept_header_wrapper_flask(['application/json'])
248
+ def post(self):
249
+ """
250
+ ---
251
+ summary: Add Dids bulk
252
+ description: Add new Dids in bulk.
253
+ tags:
254
+ - Data Identifiers
255
+ requestBody:
256
+ content:
257
+ application/json:
258
+ schema:
259
+ type: array
260
+ items:
261
+ description: One did to add.
262
+ type: object
263
+ required:
264
+ - scope
265
+ - name
266
+ - type
267
+ properties:
268
+ scope:
269
+ description: The did scope.
270
+ type: string
271
+ name:
272
+ description: The did name.
273
+ type: string
274
+ type:
275
+ description: The type of the did.
276
+ type: string
277
+ enum: ["F", "D", "C", "A", "X", "Y", "Z"]
278
+ account:
279
+ description: The account associated with the did.
280
+ type: string
281
+ statuses:
282
+ description: The monotonic status
283
+ type: string
284
+ responses:
285
+ 201:
286
+ description: OK
287
+ content:
288
+ application/json:
289
+ schema:
290
+ type: string
291
+ enum: ["Created"]
292
+ 401:
293
+ description: Invalid Auth Token
294
+ 406:
295
+ description: Not acceptable
296
+ 409:
297
+ description: Did already exists
298
+ """
299
+ dids = json_list()
300
+ try:
301
+ add_dids(dids=dids, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
302
+ except DataIdentifierNotFound as error:
303
+ return generate_http_error_flask(404, error)
304
+ except (DuplicateContent, DataIdentifierAlreadyExists, UnsupportedOperation) as error:
305
+ return generate_http_error_flask(409, error)
306
+ except AccessDenied as error:
307
+ return generate_http_error_flask(401, error)
308
+ return 'Created', 201
309
+
310
+
311
+ class Attachments(ErrorHandlingMethodView):
312
+
313
+ def post(self):
314
+ """
315
+ ---
316
+ summary: Attach did to did
317
+ description: Attaches a did to another did
318
+ tags:
319
+ - Data Identifiers
320
+ requestBody:
321
+ content:
322
+ 'application/json':
323
+ schema:
324
+ oneOf:
325
+ - description: An array containing all dids. Duplicates are not ignored.
326
+ type: array
327
+ required:
328
+ - scope
329
+ - name
330
+ - dids
331
+ properties:
332
+ scope:
333
+ description: The scope of the did.
334
+ type: string
335
+ name:
336
+ description: The name of the did.
337
+ type: string
338
+ dids:
339
+ description: The dids associated to the did.
340
+ type: array
341
+ items:
342
+ type: object
343
+ description: A did.
344
+ required:
345
+ - scope
346
+ - name
347
+ properties:
348
+ scope:
349
+ description: The scope of the did.
350
+ type: string
351
+ name:
352
+ description: The name of the did.
353
+ type: string
354
+ rse_id:
355
+ description: The rse id of the did.
356
+ type: string
357
+ - type: object
358
+ required:
359
+ - attachments
360
+ properties:
361
+ ignore_duplicates:
362
+ description: If duplicates should be ignored.
363
+ type: boolean
364
+ default: false
365
+ attachments:
366
+ description: An array containing all dids. Duplicates are not ignored.
367
+ type: array
368
+ required:
369
+ - scope
370
+ - name
371
+ - dids
372
+ properties:
373
+ scope:
374
+ description: The scope of the did.
375
+ type: string
376
+ name:
377
+ description: The name of the did.
378
+ type: string
379
+ dids:
380
+ description: The dids associated to the did.
381
+ type: array
382
+ items:
383
+ type: object
384
+ description: A did.
385
+ required:
386
+ - scope
387
+ - name
388
+ properties:
389
+ scope:
390
+ description: The scope of the did.
391
+ type: string
392
+ name:
393
+ description: The name of the did.
394
+ type: string
395
+ rse_id:
396
+ description: The rse id of the did.
397
+ type: string
398
+ responses:
399
+ 200:
400
+ description: OK
401
+ content:
402
+ application/json:
403
+ schema:
404
+ type: string
405
+ enum: ["Created"]
406
+ 401:
407
+ description: Invalid Auth Token
408
+ 404:
409
+ description: Not found
410
+ 406:
411
+ description: Not acceptable
412
+ """
413
+ parameters = json_parse((dict, list))
414
+ if isinstance(parameters, list):
415
+ attachments = parameters
416
+ ignore_duplicate = False
417
+ else:
418
+ assert isinstance(parameters, dict)
419
+ attachments = param_get(parameters, 'attachments')
420
+ ignore_duplicate = param_get(parameters, 'ignore_duplicate', default=False)
421
+
422
+ try:
423
+ attach_dids_to_dids(attachments=attachments, ignore_duplicate=ignore_duplicate, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
424
+ except DataIdentifierNotFound as error:
425
+ return generate_http_error_flask(404, error)
426
+ except (DuplicateContent, DataIdentifierAlreadyExists, UnsupportedOperation, FileAlreadyExists) as error:
427
+ return generate_http_error_flask(409, error)
428
+ except AccessDenied as error:
429
+ return generate_http_error_flask(401, error)
430
+ except FileConsistencyMismatch as error:
431
+ return generate_http_error_flask(412, error)
432
+
433
+ return 'Created', 201
434
+
435
+
436
+ class DIDs(ErrorHandlingMethodView):
437
+
438
+ @check_accept_header_wrapper_flask(['application/json'])
439
+ def get(self, scope_name):
440
+ """
441
+ ---
442
+ summary: Get did
443
+ description: Get a single data identifier.
444
+ tags:
445
+ - Data identifiers
446
+ parameters:
447
+ - name: scope_name
448
+ in: path
449
+ description: The scope and the name of the did.
450
+ schema:
451
+ type: string
452
+ style: simple
453
+ - name: dynamic_depth
454
+ in: query
455
+ description: The DID type at which to stop the dynamic length/size estimation
456
+ schema:
457
+ type: string
458
+ enum: ["FILE", "DATASET"]
459
+ - name: dynamic
460
+ in: query
461
+ description: Same as dynamic_depth = "FILE"
462
+ deprecated: true
463
+ schema:
464
+ type: string
465
+ responses:
466
+ 200:
467
+ description: OK
468
+ content:
469
+ application/json:
470
+ schema:
471
+ oneOf:
472
+ - description: A single file did.
473
+ type: object
474
+ properties:
475
+ scope:
476
+ description: The scope of the did.
477
+ type: string
478
+ name:
479
+ description: The name of the did.
480
+ type: string
481
+ type:
482
+ description: The type of the string.
483
+ type: string
484
+ account:
485
+ description: The associated account.
486
+ type: string
487
+ bytes:
488
+ description: The size in bytes.
489
+ type: integer
490
+ length:
491
+ description: The number of files. Corresponses to 1.
492
+ type: integer
493
+ enum: [1]
494
+ md5:
495
+ description: md5 checksum.
496
+ type: string
497
+ adler32:
498
+ description: adler32 checksum.
499
+ type: string
500
+ - description: A single file did.
501
+ type: object
502
+ properties:
503
+ scope:
504
+ description: The scope of the did.
505
+ type: string
506
+ name:
507
+ description: The name of the did.
508
+ type: string
509
+ type:
510
+ description: The type of the string.
511
+ type: string
512
+ account:
513
+ description: The associated account.
514
+ type: string
515
+ open:
516
+ description: If the did is write open.
517
+ type: boolean
518
+ monotonic:
519
+ description: If the did is monotonic.
520
+ type: boolean
521
+ expired_at:
522
+ description: When the did expired.
523
+ type: string
524
+ length:
525
+ description: The number of associated dids.
526
+ type: number
527
+ bytes:
528
+ description: The size in bytes.
529
+ type: number
530
+ 401:
531
+ description: Invalid Auth Token
532
+ 404:
533
+ description: Scope not found
534
+ 406:
535
+ description: Not acceptable
536
+ """
537
+ try:
538
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
539
+ dynamic_depth = None
540
+ if 'dynamic_depth' in request.args:
541
+ orig = request.args['dynamic_depth'].upper()
542
+ if orig == 'DATASET':
543
+ dynamic_depth = DIDType.DATASET
544
+ elif orig == 'FILE':
545
+ dynamic_depth = DIDType.FILE
546
+ else:
547
+ dynamic_depth = None
548
+ elif 'dynamic' in request.args:
549
+ dynamic_depth = DIDType.FILE
550
+ did = get_did(scope=scope, name=name, dynamic_depth=dynamic_depth, vo=request.environ.get('vo'))
551
+ return Response(render_json(**did), content_type='application/json')
552
+ except ValueError as error:
553
+ return generate_http_error_flask(400, error)
554
+ except (ScopeNotFound, DataIdentifierNotFound) as error:
555
+ return generate_http_error_flask(404, error)
556
+
557
+ def post(self, scope_name):
558
+ """
559
+ ---
560
+ summary: Create did
561
+ description: Create a new data identifier.
562
+ tags:
563
+ - Data Identifiers
564
+ parameters:
565
+ - name: scope_name
566
+ in: path
567
+ description: The scope and the name of the did.
568
+ schema:
569
+ type: string
570
+ style: simple
571
+ requestBody:
572
+ content:
573
+ 'application/json':
574
+ schema:
575
+ type: object
576
+ required:
577
+ - type
578
+ properties:
579
+ type:
580
+ description: The type of the did.
581
+ type: string
582
+ statuses:
583
+ description: The statuses of the did.
584
+ type: string
585
+ meta:
586
+ description: The meta of the did.
587
+ type: string
588
+ rules:
589
+ description: The rules associated with the did.
590
+ type: array
591
+ items:
592
+ type: object
593
+ description: A rule.
594
+ lifetime:
595
+ description: The lifetime of the did.
596
+ type: string
597
+ dids:
598
+ description: The dids associated with the did.
599
+ type: array
600
+ items:
601
+ type: object
602
+ description: The did associated with a did.
603
+ properties:
604
+ scope:
605
+ description: The scope of the did.
606
+ type: string
607
+ name:
608
+ description: The name of the did.
609
+ type: string
610
+ rse:
611
+ description: The rse associated with the did.
612
+ type: string
613
+ responses:
614
+ 201:
615
+ description: OK
616
+ content:
617
+ application/json:
618
+ schema:
619
+ type: string
620
+ enum: ['Created']
621
+ 401:
622
+ description: Invalid Auth Token
623
+ 404:
624
+ description: Did or scope not found
625
+ 409:
626
+ description: Did already exists
627
+ """
628
+ try:
629
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
630
+ except ValueError as error:
631
+ return generate_http_error_flask(400, error)
632
+
633
+ parameters = json_parameters()
634
+ type_param = param_get(parameters, 'type')
635
+
636
+ try:
637
+ add_did(
638
+ scope=scope,
639
+ name=name,
640
+ did_type=type_param,
641
+ statuses=param_get(parameters, 'statuses', default={}),
642
+ meta=param_get(parameters, 'meta', default={}),
643
+ rules=param_get(parameters, 'rules', default=[]),
644
+ lifetime=param_get(parameters, 'lifetime', default=None),
645
+ dids=param_get(parameters, 'dids', default=[]),
646
+ rse=param_get(parameters, 'rse', default=None),
647
+ issuer=request.environ.get('issuer'),
648
+ vo=request.environ.get('vo'),
649
+ )
650
+ except (InvalidObject, InvalidPath) as error:
651
+ return generate_http_error_flask(400, error)
652
+ except (DataIdentifierNotFound, ScopeNotFound) as error:
653
+ return generate_http_error_flask(404, error)
654
+ except (DuplicateContent, DataIdentifierAlreadyExists, UnsupportedOperation) as error:
655
+ return generate_http_error_flask(409, error)
656
+ except AccessDenied as error:
657
+ return generate_http_error_flask(401, error)
658
+ except DatabaseException as error:
659
+ if 'DELETED_DIDS_PK violated' in str(error):
660
+ return generate_http_error_flask(
661
+ status_code=406,
662
+ exc=error.__class__.__name__,
663
+ exc_msg=str('A deleted DID {} with scope {} is reused'.format(name, scope))
664
+ )
665
+ else:
666
+ return generate_http_error_flask(406, error)
667
+
668
+ return 'Created', 201
669
+
670
+ def put(self, scope_name):
671
+ """
672
+ ---
673
+ summary: Update did
674
+ description: Update a did.
675
+ tags:
676
+ - Data Identifiers
677
+ parameters:
678
+ - name: scope_name
679
+ in: path
680
+ description: The scope and the name of the did.
681
+ schema:
682
+ type: string
683
+ style: simple
684
+ requestBody:
685
+ content:
686
+ 'application/json':
687
+ schema:
688
+ type: object
689
+ properties:
690
+ open:
691
+ description: The open status
692
+ type: boolean
693
+ responses:
694
+ 200:
695
+ description: OK
696
+ 401:
697
+ description: Invalid Auth Token
698
+ 404:
699
+ description: Did not found
700
+ 409:
701
+ description: Wrong status
702
+ """
703
+ try:
704
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
705
+ except ValueError as error:
706
+ return generate_http_error_flask(400, error)
707
+
708
+ parameters = json_parameters()
709
+
710
+ try:
711
+ set_status(scope=scope, name=name, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'), **parameters)
712
+ except DataIdentifierNotFound as error:
713
+ return generate_http_error_flask(404, error)
714
+ except (UnsupportedStatus, UnsupportedOperation) as error:
715
+ return generate_http_error_flask(409, error)
716
+ except AccessDenied as error:
717
+ return generate_http_error_flask(401, error)
718
+
719
+ return '', 200
720
+
721
+
722
+ class Attachment(ErrorHandlingMethodView):
723
+
724
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
725
+ def get(self, scope_name):
726
+ """
727
+ ---
728
+ summary: Get did
729
+ description: Returns the contents of a data identifier.
730
+ tags:
731
+ - Data Identifiers
732
+ parameters:
733
+ - name: scope_name
734
+ in: path
735
+ description: The scope and the name of the did.
736
+ schema:
737
+ type: string
738
+ style: simple
739
+ responses:
740
+ 200:
741
+ description: Did found
742
+ content:
743
+ application/x-json-stream:
744
+ schema:
745
+ description: The contents of a did. Items are line seperated.
746
+ type: array
747
+ items:
748
+ type: object
749
+ required:
750
+ - scope
751
+ - name
752
+ - type
753
+ - bytes
754
+ - adler32
755
+ - md5
756
+ properties:
757
+ scope:
758
+ description: The scope of the did.
759
+ type: string
760
+ name:
761
+ description: The name of the did.
762
+ type: string
763
+ type:
764
+ description: The type of the did.
765
+ type: string
766
+ bytes:
767
+ description: The size of the did.
768
+ type: number
769
+ adler32:
770
+ description: The adler32 checksum of the did.
771
+ type: string
772
+ md5:
773
+ description: The md5 checksum of the did.
774
+ type: string
775
+ 401:
776
+ description: Invalid Auth Token
777
+ 404:
778
+ description: Scope not found
779
+ 406:
780
+ description: Not acceptable
781
+ """
782
+ try:
783
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
784
+
785
+ def generate(vo):
786
+ for did in list_content(scope=scope, name=name, vo=vo):
787
+ yield render_json(**did) + '\n'
788
+
789
+ return try_stream(generate(vo=request.environ.get('vo')))
790
+ except ValueError as error:
791
+ return generate_http_error_flask(400, error)
792
+ except DataIdentifierNotFound as error:
793
+ return generate_http_error_flask(404, error)
794
+
795
+ def post(self, scope_name):
796
+ """
797
+ ---
798
+ summary: Add dids to did
799
+ description: Append data identifiers to data identifiers.
800
+ tags:
801
+ - Data Identifiers
802
+ parameters:
803
+ - name: scope_name
804
+ in: path
805
+ description: The scope and the name of the did.
806
+ schema:
807
+ type: string
808
+ style: simple
809
+ requestBody:
810
+ content:
811
+ 'application/json':
812
+ schema:
813
+ type: object
814
+ required:
815
+ - dids
816
+ properties:
817
+ rse:
818
+ description: The name of the rse.
819
+ type: string
820
+ account:
821
+ description: The account which attaches the dids.
822
+ type: string
823
+ dids:
824
+ description: The dids to attach.
825
+ type: object
826
+ properties:
827
+ account:
828
+ description: The account attaching the did.
829
+ type: string
830
+ scope:
831
+ description: The scope of the did.
832
+ type: string
833
+ name:
834
+ description: The name of the did.
835
+ type: string
836
+ responses:
837
+ 201:
838
+ description: OK
839
+ content:
840
+ application/json:
841
+ schema:
842
+ type: string
843
+ enum: ["Created"]
844
+ 401:
845
+ description: Invalid Auth Token
846
+ 404:
847
+ description: Did not found
848
+ 406:
849
+ description: Not acceptable
850
+ 409:
851
+ description: Already attached
852
+ """
853
+ try:
854
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
855
+ except ValueError as error:
856
+ return generate_http_error_flask(400, error)
857
+
858
+ attachments = json_parameters()
859
+
860
+ try:
861
+ attach_dids(scope=scope, name=name, attachment=attachments, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
862
+ except InvalidPath as error:
863
+ return generate_http_error_flask(400, error)
864
+ except (DataIdentifierNotFound, RSENotFound) as error:
865
+ return generate_http_error_flask(404, error)
866
+ except (DuplicateContent, UnsupportedOperation, FileAlreadyExists) as error:
867
+ return generate_http_error_flask(409, error)
868
+ except AccessDenied as error:
869
+ return generate_http_error_flask(401, error)
870
+
871
+ return 'Created', 201
872
+
873
+ def delete(self, scope_name):
874
+ """
875
+ ---
876
+ summary: Detach dids from did
877
+ description: Detach data identifiers from data identifiers.
878
+ tags:
879
+ - Data Identifiers
880
+ parameters:
881
+ - name: scope_name
882
+ in: path
883
+ description: The scope and the name of the did.
884
+ schema:
885
+ type: string
886
+ style: simple
887
+ requestBody:
888
+ content:
889
+ 'application/json':
890
+ schema:
891
+ type: object
892
+ required:
893
+ - dids
894
+ properties:
895
+ dids:
896
+ description: The dids to detach.
897
+ type: array
898
+ items:
899
+ type: object
900
+ properties:
901
+ scope:
902
+ description: The scope of the did.
903
+ type: string
904
+ name:
905
+ description: The name of the did.
906
+ type: string
907
+ responses:
908
+ 200:
909
+ description: OK
910
+ 401:
911
+ description: Invalid Auth Token
912
+ 404:
913
+ description: Did not found
914
+ """
915
+ try:
916
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
917
+ except ValueError as error:
918
+ return generate_http_error_flask(400, error)
919
+
920
+ parameters = json_parameters()
921
+ dids = param_get(parameters, 'dids')
922
+
923
+ try:
924
+ detach_dids(scope=scope, name=name, dids=dids, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
925
+ except UnsupportedOperation as error:
926
+ return generate_http_error_flask(409, error)
927
+ except DataIdentifierNotFound as error:
928
+ return generate_http_error_flask(404, error)
929
+ except AccessDenied as error:
930
+ return generate_http_error_flask(401, error)
931
+
932
+ return '', 200
933
+
934
+
935
+ class AttachmentHistory(ErrorHandlingMethodView):
936
+
937
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
938
+ def get(self, scope_name):
939
+ """
940
+ ---
941
+ summary: Get history
942
+ description: Returns the content history of a data identifier.
943
+ tags:
944
+ - Data Identifiers
945
+ parameters:
946
+ - name: scope_name
947
+ in: path
948
+ description: The scope and the name of the did.
949
+ schema:
950
+ type: string
951
+ style: simple
952
+ responses:
953
+ 200:
954
+ description: Did found
955
+ content:
956
+ application/x-json-stream:
957
+ schema:
958
+ description: The dids with their information and history. Elements are seperated by new line characters.
959
+ type: array
960
+ items:
961
+ type: object
962
+ description: A single did with history data.
963
+ properties:
964
+ scope:
965
+ description: The scope of the did.
966
+ type: string
967
+ name:
968
+ description: The name of the did.
969
+ type: string
970
+ type:
971
+ description: The type of the did.
972
+ type: string
973
+ bytes:
974
+ description: The size of the did in bytes.
975
+ type: integer
976
+ adler32:
977
+ description: The abler32 sha checksum.
978
+ type: string
979
+ md5:
980
+ description: The md5 checksum.
981
+ type: string
982
+ deleted_at:
983
+ description: The deleted_at date time.
984
+ type: string
985
+ created_at:
986
+ description: The created_at date time.
987
+ type: string
988
+ updated_at:
989
+ description: The last time the did was updated.
990
+ type: string
991
+ 401:
992
+ description: Invalid Auth Token
993
+ 404:
994
+ description: Did not found
995
+ 406:
996
+ description: Not acceptable
997
+ """
998
+ try:
999
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1000
+
1001
+ def generate(vo):
1002
+ for did in list_content_history(scope=scope, name=name, vo=vo):
1003
+ yield render_json(**did) + '\n'
1004
+
1005
+ return try_stream(generate(vo=request.environ.get('vo')))
1006
+ except ValueError as error:
1007
+ return generate_http_error_flask(400, error)
1008
+ except DataIdentifierNotFound as error:
1009
+ return generate_http_error_flask(404, error)
1010
+
1011
+
1012
+ class Files(ErrorHandlingMethodView):
1013
+
1014
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
1015
+ def get(self, scope_name):
1016
+ """
1017
+ ---
1018
+ summary: Get replicas
1019
+ description: List all replicas for a did.
1020
+ tags:
1021
+ - Data Identifiers
1022
+ parameters:
1023
+ - name: scope_name
1024
+ in: path
1025
+ description: The scope and the name of the did.
1026
+ schema:
1027
+ type: string
1028
+ style: simple
1029
+ - name: long
1030
+ in: query
1031
+ description: Flag to trigger long output.
1032
+ schema:
1033
+ type: object
1034
+ required: false
1035
+ responses:
1036
+ 200:
1037
+ description: OK
1038
+ content:
1039
+ application/x-json-stream:
1040
+ schema:
1041
+ oneOf:
1042
+ - description: All replica information if `long` is defined.
1043
+ type: array
1044
+ items:
1045
+ type: object
1046
+ properties:
1047
+ scope:
1048
+ description: The scope of the did.
1049
+ type: string
1050
+ name:
1051
+ description: The name of the did.
1052
+ type: string
1053
+ bytes:
1054
+ description: The size of the did in bytes.
1055
+ type: integer
1056
+ guid:
1057
+ description: The guid of the did.
1058
+ type: string
1059
+ events:
1060
+ description: The number of events of the did.
1061
+ type: integer
1062
+ adler32:
1063
+ description: The adler32 checksum.
1064
+ type: string
1065
+ lumiblocknr:
1066
+ description: The lumi block nr. Only availabe if `long` is defined in the query.
1067
+ type: integer
1068
+ - description: All replica information.
1069
+ type: array
1070
+ items:
1071
+ type: object
1072
+ properties:
1073
+ scope:
1074
+ description: The scope of the did.
1075
+ type: string
1076
+ name:
1077
+ description: The name of the did.
1078
+ type: string
1079
+ bytes:
1080
+ description: The size of the did in bytes.
1081
+ type: integer
1082
+ guid:
1083
+ description: The guid of the did.
1084
+ type: string
1085
+ events:
1086
+ description: The number of events of the did.
1087
+ type: integer
1088
+ adler32:
1089
+ description: The adler32 checksum.
1090
+ type: string
1091
+ 401:
1092
+ description: Invalid Auth Token
1093
+ 404:
1094
+ description: Did not found
1095
+ 406:
1096
+ description: Not acceptable
1097
+ """
1098
+ long = 'long' in request.args
1099
+
1100
+ try:
1101
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1102
+
1103
+ def generate(vo):
1104
+ for file in list_files(scope=scope, name=name, long=long, vo=vo):
1105
+ yield dumps(file) + '\n'
1106
+
1107
+ return try_stream(generate(vo=request.environ.get('vo')))
1108
+ except ValueError as error:
1109
+ return generate_http_error_flask(400, error)
1110
+ except DataIdentifierNotFound as error:
1111
+ return generate_http_error_flask(404, error)
1112
+
1113
+
1114
+ class Parents(ErrorHandlingMethodView):
1115
+
1116
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
1117
+ def get(self, scope_name):
1118
+ """
1119
+ ---
1120
+ summary: Get Parents
1121
+ description: Lists all parents of the did.
1122
+ tags:
1123
+ - Data Identifiers
1124
+ parameters:
1125
+ - name: scope_name
1126
+ in: path
1127
+ description: The scope and the name of the did.
1128
+ schema:
1129
+ type: string
1130
+ style: simple
1131
+ responses:
1132
+ 200:
1133
+ description: OK
1134
+ content:
1135
+ application/x-json-stream:
1136
+ schema:
1137
+ description: The parents of the did.
1138
+ type: array
1139
+ items:
1140
+ type: object
1141
+ description: A parent of the did.
1142
+ properties:
1143
+ scope:
1144
+ description: The scope of the did.
1145
+ type: string
1146
+ name:
1147
+ description: The name of the did.
1148
+ type: string
1149
+ type:
1150
+ description: The type of the did.
1151
+ type: string
1152
+ 401:
1153
+ description: Invalid Auth Token
1154
+ 404:
1155
+ description: Did not found
1156
+ 406:
1157
+ description: Not acceptable
1158
+ """
1159
+ try:
1160
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1161
+
1162
+ def generate(vo):
1163
+ for dataset in list_parent_dids(scope=scope, name=name, vo=vo):
1164
+ yield render_json(**dataset) + "\n"
1165
+
1166
+ return try_stream(generate(vo=request.environ.get('vo')))
1167
+ except ValueError as error:
1168
+ return generate_http_error_flask(400, error)
1169
+ except DataIdentifierNotFound as error:
1170
+ return generate_http_error_flask(404, error)
1171
+
1172
+
1173
+ class Meta(ErrorHandlingMethodView):
1174
+
1175
+ @check_accept_header_wrapper_flask(['application/json'])
1176
+ def get(self, scope_name):
1177
+ """
1178
+ ---
1179
+ summary: Get metadata
1180
+ description: Get the metadata of a did.
1181
+ tags:
1182
+ - Data Identifiers
1183
+ parameters:
1184
+ - name: scope_name
1185
+ in: path
1186
+ description: The scope and the name of the did.
1187
+ schema:
1188
+ type: string
1189
+ style: simple
1190
+ - name: plugin
1191
+ in: query
1192
+ description: The plugin to use.
1193
+ schema:
1194
+ type: string
1195
+ default: DID_COLUMN
1196
+ responses:
1197
+ 200:
1198
+ description: OK
1199
+ content:
1200
+ application/json:
1201
+ schema:
1202
+ description: A data identifer with all attributes.
1203
+ type: object
1204
+ 401:
1205
+ description: Invalid Auth Token
1206
+ 404:
1207
+ description: Did not found
1208
+ 406:
1209
+ description: Not acceptable
1210
+ """
1211
+ try:
1212
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1213
+ except ValueError as error:
1214
+ return generate_http_error_flask(400, error)
1215
+
1216
+ try:
1217
+ plugin = request.args.get('plugin', default='DID_COLUMN')
1218
+ meta = get_metadata(scope=scope, name=name, plugin=plugin, vo=request.environ.get('vo'))
1219
+ return Response(render_json(**meta), content_type='application/json')
1220
+ except DataIdentifierNotFound as error:
1221
+ return generate_http_error_flask(404, error)
1222
+
1223
+ def post(self, scope_name):
1224
+ """
1225
+ ---
1226
+ summary: Add metadata
1227
+ description: Add metadata to a did.
1228
+ tags:
1229
+ - Data Identifiers
1230
+ parameters:
1231
+ - name: scope_name
1232
+ in: path
1233
+ description: The scope and the name of the did.
1234
+ schema:
1235
+ type: string
1236
+ style: simple
1237
+ requestBody:
1238
+ content:
1239
+ 'application/json':
1240
+ schema:
1241
+ type: object
1242
+ required:
1243
+ - meta
1244
+ properties:
1245
+ meta:
1246
+ description: The metadata to add. A dictionary containg the metadata name as key and the value as value.
1247
+ type: object
1248
+ recursive:
1249
+ description: Flag if the metadata should be applied recirsively to children.
1250
+ type: boolean
1251
+ default: false
1252
+ responses:
1253
+ 201:
1254
+ description: Created
1255
+ content:
1256
+ application/json:
1257
+ schema:
1258
+ type: string
1259
+ enum: ["Created"]
1260
+ 401:
1261
+ description: Invalid Auth Token
1262
+ 404:
1263
+ description: Not found
1264
+ 406:
1265
+ description: Not acceptable
1266
+ """
1267
+ try:
1268
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1269
+ except ValueError as error:
1270
+ return generate_http_error_flask(400, error)
1271
+
1272
+ parameters = json_parameters()
1273
+ meta = param_get(parameters, 'meta')
1274
+
1275
+ try:
1276
+ set_metadata_bulk(
1277
+ scope=scope,
1278
+ name=name,
1279
+ meta=meta,
1280
+ issuer=request.environ.get('issuer'),
1281
+ recursive=param_get(parameters, 'recursive', default=False),
1282
+ vo=request.environ.get('vo'),
1283
+ )
1284
+ except DataIdentifierNotFound as error:
1285
+ return generate_http_error_flask(404, error)
1286
+ except Duplicate as error:
1287
+ return generate_http_error_flask(409, error)
1288
+ except (KeyNotFound, InvalidMetadata, InvalidValueForKey) as error:
1289
+ return generate_http_error_flask(400, error)
1290
+
1291
+ return "Created", 201
1292
+
1293
+ def delete(self, scope_name):
1294
+ """
1295
+ ---
1296
+ summary: Delete metadata
1297
+ description: Deletes the specified metadata from the did.
1298
+ tags:
1299
+ - Data Identifiers
1300
+ parameters:
1301
+ - name: scope_name
1302
+ in: path
1303
+ description: The scope and the name of the did.
1304
+ schema:
1305
+ type: string
1306
+ style: simple
1307
+ - name: key
1308
+ in: query
1309
+ description: The key to delete.
1310
+ schema:
1311
+ type: string
1312
+ responses:
1313
+ 200:
1314
+ description: OK
1315
+ 400:
1316
+ description: scope_name could not be parsed.
1317
+ 401:
1318
+ description: Invalid Auth Token
1319
+ 404:
1320
+ description: Did or key not found
1321
+ 406:
1322
+ description: Not acceptable
1323
+ 409:
1324
+ description: Feature is not in current database.
1325
+ """
1326
+ try:
1327
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1328
+ except ValueError as error:
1329
+ return generate_http_error_flask(400, error)
1330
+
1331
+ if 'key' in request.args:
1332
+ key = request.args['key']
1333
+ else:
1334
+ return generate_http_error_flask(404, KeyNotFound.__name__, 'No key provided to remove')
1335
+
1336
+ try:
1337
+ delete_metadata(scope=scope, name=name, key=key, vo=request.environ.get('vo'))
1338
+ except (KeyNotFound, DataIdentifierNotFound) as error:
1339
+ return generate_http_error_flask(404, error)
1340
+ except NotImplementedError as error:
1341
+ return generate_http_error_flask(409, error, 'Feature not in current database')
1342
+
1343
+ return '', 200
1344
+
1345
+
1346
+ class SingleMeta(ErrorHandlingMethodView):
1347
+ def post(self, scope_name, key):
1348
+ """
1349
+ ---
1350
+ summary: Add metadata
1351
+ description: Add metadata to a did.
1352
+ tags:
1353
+ - Data Identifiers
1354
+ parameters:
1355
+ - name: scope_name
1356
+ in: path
1357
+ description: The scope and the name of the did.
1358
+ schema:
1359
+ type: string
1360
+ style: simple
1361
+ - name: key
1362
+ in: path
1363
+ description: The key for the metadata.
1364
+ schema:
1365
+ type: string
1366
+ style: simple
1367
+ requestBody:
1368
+ content:
1369
+ 'application/json':
1370
+ schema:
1371
+ type: object
1372
+ required:
1373
+ - value
1374
+ properties:
1375
+ value:
1376
+ description: The value to set.
1377
+ type: object
1378
+ responses:
1379
+ 201:
1380
+ description: Created
1381
+ content:
1382
+ application/json:
1383
+ schema:
1384
+ type: string
1385
+ enum: ["Created"]
1386
+ 401:
1387
+ description: Invalid Auth Token
1388
+ 404:
1389
+ description: Did not found
1390
+ 406:
1391
+ description: Not acceptable
1392
+ 409:
1393
+ description: Matadata already exists
1394
+ 400:
1395
+ description: Invalid key or value
1396
+ """
1397
+ try:
1398
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1399
+ except ValueError as error:
1400
+ return generate_http_error_flask(400, error)
1401
+
1402
+ parameters = json_parameters()
1403
+ value = param_get(parameters, 'value')
1404
+
1405
+ try:
1406
+ set_metadata(
1407
+ scope=scope,
1408
+ name=name,
1409
+ key=key,
1410
+ value=value,
1411
+ issuer=request.environ.get('issuer'),
1412
+ recursive=param_get(parameters, 'recursive', default=False),
1413
+ vo=request.environ.get('vo'),
1414
+ )
1415
+ except DataIdentifierNotFound as error:
1416
+ return generate_http_error_flask(404, error)
1417
+ except Duplicate as error:
1418
+ return generate_http_error_flask(409, error)
1419
+ except (KeyNotFound, InvalidMetadata, InvalidValueForKey) as error:
1420
+ return generate_http_error_flask(400, error)
1421
+
1422
+ return 'Created', 201
1423
+
1424
+
1425
+ class BulkDIDsMeta(ErrorHandlingMethodView):
1426
+
1427
+ def post(self):
1428
+ """
1429
+ ---
1430
+ summary: Add metadata bulk
1431
+ description: Adds metadata in a bulk.
1432
+ tags:
1433
+ - Data Identifiers
1434
+ requestBody:
1435
+ content:
1436
+ 'application/json':
1437
+ schema:
1438
+ type: object
1439
+ required:
1440
+ - dids
1441
+ properties:
1442
+ dids:
1443
+ description: A list with all the dids and the metadata.
1444
+ type: array
1445
+ items:
1446
+ description: The did and associated metadata.
1447
+ type: object
1448
+ properties:
1449
+ scope:
1450
+ description: The scope of the did.
1451
+ type: string
1452
+ name:
1453
+ description: The name of the did.
1454
+ type: string
1455
+ meta:
1456
+ description: The metadata to add. A dictionary with the meta key as key and the value as value.
1457
+ type: object
1458
+ responses:
1459
+ 200:
1460
+ description: Created
1461
+ content:
1462
+ application/json:
1463
+ schema:
1464
+ type: string
1465
+ enum: ["Created"]
1466
+ 401:
1467
+ description: Invalid Auth Token
1468
+ 404:
1469
+ description: Did not found
1470
+ 406:
1471
+ description: Not acceptable
1472
+ 409:
1473
+ description: Unsupported Operation
1474
+ """
1475
+ parameters = json_parameters()
1476
+ dids = param_get(parameters, 'dids')
1477
+
1478
+ try:
1479
+ set_dids_metadata_bulk(dids=dids, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
1480
+ except DataIdentifierNotFound as error:
1481
+ return generate_http_error_flask(404, error)
1482
+ except UnsupportedOperation as error:
1483
+ return generate_http_error_flask(409, error)
1484
+ except AccessDenied as error:
1485
+ return generate_http_error_flask(401, error)
1486
+
1487
+ return 'Created', 201
1488
+
1489
+
1490
+ class Rules(ErrorHandlingMethodView):
1491
+
1492
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
1493
+ def get(self, scope_name):
1494
+ """
1495
+ ---
1496
+ summary: Get rules
1497
+ description: Lists all rules of a given did.
1498
+ tags:
1499
+ - Data Identifiers
1500
+ parameters:
1501
+ - name: scope_name
1502
+ in: path
1503
+ description: The scope and the name of the did.
1504
+ schema:
1505
+ type: string
1506
+ style: simple
1507
+ responses:
1508
+ 200:
1509
+ description: The rules associated with a did.
1510
+ content:
1511
+ application/x-json-stream:
1512
+ schema:
1513
+ description: The rules associated with a did.
1514
+ type: array
1515
+ items:
1516
+ description: A rule.
1517
+ type: object
1518
+ 401:
1519
+ description: Invalid Auth Token
1520
+ 404:
1521
+ description: Did or rule not found
1522
+ 406:
1523
+ description: Not acceptable
1524
+ """
1525
+ try:
1526
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1527
+
1528
+ def generate(vo):
1529
+ for rule in list_replication_rules({'scope': scope, 'name': name}, vo=vo):
1530
+ yield dumps(rule, cls=APIEncoder) + '\n'
1531
+
1532
+ return try_stream(generate(vo=request.environ.get('vo')))
1533
+ except ValueError as error:
1534
+ return generate_http_error_flask(400, error)
1535
+ except RuleNotFound as error:
1536
+ return generate_http_error_flask(404, error)
1537
+
1538
+
1539
+ class BulkMeta(ErrorHandlingMethodView):
1540
+
1541
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
1542
+ def post(self):
1543
+ """
1544
+ ---
1545
+ summary: Get metadata bulk
1546
+ description: List all metadata of a list of data identifiers.
1547
+ tags:
1548
+ - Data Identifiers
1549
+ requestBody:
1550
+ content:
1551
+ 'application/x-json-stream':
1552
+ schema:
1553
+ type: object
1554
+ required:
1555
+ - dids
1556
+ properties:
1557
+ dids:
1558
+ description: The dids.
1559
+ type: array
1560
+ items:
1561
+ description: A did.
1562
+ type: object
1563
+ properties:
1564
+ name:
1565
+ description: The name of the did.
1566
+ type: string
1567
+ scope:
1568
+ description: The scope of the did.
1569
+ type: string
1570
+ inherit:
1571
+ description: Concatenated the metadata of the parent if set to true.
1572
+ type: boolean
1573
+ default: false
1574
+ responses:
1575
+ 200:
1576
+ description: OK
1577
+ content:
1578
+ application/json:
1579
+ schema:
1580
+ description: A list of metadata identifiers for the dids. Seperated by new lines.
1581
+ type: array
1582
+ items:
1583
+ description: The metadata for one did.
1584
+ type: object
1585
+ 400:
1586
+ description: Cannot decode json parameter list
1587
+ 401:
1588
+ description: Invalid Auth Token
1589
+ 404:
1590
+ description: Did not found
1591
+ 406:
1592
+ description: Not acceptable
1593
+ """
1594
+ parameters = json_parameters()
1595
+ dids = param_get(parameters, 'dids')
1596
+ inherit = param_get(parameters, 'inherit', default=False)
1597
+
1598
+ try:
1599
+ def generate(vo):
1600
+ for meta in get_metadata_bulk(dids, inherit=inherit, vo=vo):
1601
+ yield render_json(**meta) + '\n'
1602
+
1603
+ return try_stream(generate(vo=request.environ.get('vo')))
1604
+ except ValueError as error:
1605
+ return generate_http_error_flask(400, error, 'Cannot decode json parameter list')
1606
+ except DataIdentifierNotFound as error:
1607
+ return generate_http_error_flask(404, error)
1608
+
1609
+
1610
+ class AssociatedRules(ErrorHandlingMethodView):
1611
+
1612
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
1613
+ def get(self, scope_name):
1614
+ """
1615
+ ---
1616
+ summary: Get accociated rules
1617
+ description: Gets all associated rules for a file.
1618
+ tags:
1619
+ - Data Identifiers
1620
+ parameters:
1621
+ - name: scope_name
1622
+ in: path
1623
+ description: The scope and the name of the did.
1624
+ schema:
1625
+ type: string
1626
+ style: simple
1627
+ responses:
1628
+ 200:
1629
+ description: OK
1630
+ content:
1631
+ application/x-json-stream:
1632
+ schema:
1633
+ description: All associated rules for a file. Items are seperated by new line character.
1634
+ type: array
1635
+ items:
1636
+ description: A replication rule associated with the file. Has more fields than listed here.
1637
+ type: object
1638
+ properties:
1639
+ id:
1640
+ description: The id of the rule.
1641
+ type: string
1642
+ subscription_id:
1643
+ description: The subscription id of the rule.
1644
+ type: string
1645
+ account:
1646
+ description: The account associated with the rule.
1647
+ type: string
1648
+ scope:
1649
+ description: The scope associated with the rule.
1650
+ type: string
1651
+ name:
1652
+ description: The name of the rule.
1653
+ type: string
1654
+ state:
1655
+ description: The state of the rule.
1656
+ type: string
1657
+ rse_expression:
1658
+ description: The rse expression of the rule.
1659
+ type: string
1660
+ 401:
1661
+ description: Invalid Auth Token
1662
+ 404:
1663
+ description: Did not found
1664
+ 406:
1665
+ description: Not acceptable
1666
+ """
1667
+ try:
1668
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1669
+
1670
+ def generate(vo):
1671
+ for rule in list_associated_replication_rules_for_file(scope=scope, name=name, vo=vo):
1672
+ yield dumps(rule, cls=APIEncoder) + '\n'
1673
+
1674
+ return try_stream(generate(vo=request.environ.get('vo')))
1675
+ except ValueError as error:
1676
+ return generate_http_error_flask(400, error)
1677
+ except DataIdentifierNotFound as error:
1678
+ return generate_http_error_flask(404, error)
1679
+
1680
+
1681
+ class GUIDLookup(ErrorHandlingMethodView):
1682
+
1683
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
1684
+ def get(self, guid):
1685
+ """
1686
+ ---
1687
+ summary: Get dataset
1688
+ description: Returns the dataset associated with a GUID.
1689
+ tags:
1690
+ - Data Identifiers
1691
+ parameters:
1692
+ - name: guid
1693
+ in: path
1694
+ description: The GUID to query buy.
1695
+ schema:
1696
+ type: string
1697
+ style: simple
1698
+ responses:
1699
+ 200:
1700
+ description: OK
1701
+ content:
1702
+ application/x-json-stream:
1703
+ schema:
1704
+ description: A list of all datasets associated with the guid. Items are seperated by new line character.
1705
+ type: array
1706
+ items:
1707
+ description: A dataset associated with a guid.
1708
+ type: object
1709
+ properties:
1710
+ scope:
1711
+ description: The scope of the dataset.
1712
+ type: string
1713
+ name:
1714
+ description: The name of the dataset.
1715
+ type: string
1716
+ 401:
1717
+ description: Invalid Auth Token
1718
+ 404:
1719
+ description: Did not found
1720
+ 406:
1721
+ description: Not acceptable
1722
+ """
1723
+ try:
1724
+ def generate(vo):
1725
+ for dataset in get_dataset_by_guid(guid, vo=vo):
1726
+ yield dumps(dataset, cls=APIEncoder) + '\n'
1727
+
1728
+ return try_stream(generate(vo=request.environ.get('vo')))
1729
+ except DataIdentifierNotFound as error:
1730
+ return generate_http_error_flask(404, error)
1731
+
1732
+
1733
+ class SampleLegacy(ErrorHandlingMethodView):
1734
+
1735
+ def post(self, input_scope, input_name, output_scope, output_name, nbfiles):
1736
+ """
1737
+ ---
1738
+ summary: Create sample
1739
+ description: Creates a sample from an input collection.
1740
+ tags:
1741
+ - Data Identifiers
1742
+ parameters:
1743
+ - name: input_scope
1744
+ in: path
1745
+ description: The input scope.
1746
+ schema:
1747
+ type: string
1748
+ style: simple
1749
+ - name: input_name
1750
+ in: path
1751
+ description: The input name.
1752
+ schema:
1753
+ type: string
1754
+ style: simple
1755
+ - name: output_scope
1756
+ in: path
1757
+ description: The output scope.
1758
+ schema:
1759
+ type: string
1760
+ style: simple
1761
+ - name: output_name
1762
+ in: path
1763
+ description: The output name.
1764
+ schema:
1765
+ type: string
1766
+ style: simple
1767
+ - name: nbfiles
1768
+ in: path
1769
+ description: The number of files to register in the output dataset.
1770
+ schema:
1771
+ type: string
1772
+ style: simple
1773
+ responses:
1774
+ 201:
1775
+ description: OK
1776
+ content:
1777
+ application/json:
1778
+ schema:
1779
+ type: string
1780
+ enum: ["Created"]
1781
+ 401:
1782
+ description: Invalid Auth Token
1783
+ 404:
1784
+ description: Not found
1785
+ 406:
1786
+ description: Not acceptable
1787
+ 409:
1788
+ description: Duplication
1789
+ """
1790
+ try:
1791
+ create_did_sample(
1792
+ input_scope=input_scope,
1793
+ input_name=input_name,
1794
+ output_scope=output_scope,
1795
+ output_name=output_name,
1796
+ issuer=request.environ.get('issuer'),
1797
+ nbfiles=nbfiles,
1798
+ vo=request.environ.get('vo'),
1799
+ )
1800
+ except DataIdentifierNotFound as error:
1801
+ return generate_http_error_flask(404, error)
1802
+ except (DuplicateContent, DataIdentifierAlreadyExists, UnsupportedOperation) as error:
1803
+ return generate_http_error_flask(409, error)
1804
+ except AccessDenied as error:
1805
+ return generate_http_error_flask(401, error)
1806
+
1807
+ return 'Created', 201
1808
+
1809
+
1810
+ class Sample(ErrorHandlingMethodView):
1811
+
1812
+ def post(self):
1813
+ """
1814
+ ---
1815
+ summary: Create sample
1816
+ description: Creates a sample from an input collection.
1817
+ tags:
1818
+ - Data Identifiers
1819
+ requestBody:
1820
+ description: Parameters (source and destination) for the files in the sample to be created
1821
+ content:
1822
+ 'application/json':
1823
+ schema:
1824
+ type: object
1825
+ required:
1826
+ - input_scope
1827
+ - input_name
1828
+ - output_scope
1829
+ - output_name
1830
+ - nbfiles
1831
+ properties:
1832
+ input_scope:
1833
+ description: The input scope.
1834
+ type: string
1835
+ input_name:
1836
+ description: The input name.
1837
+ type: string
1838
+ output_scope:
1839
+ description: The output scope.
1840
+ type: string
1841
+ output_name:
1842
+ description: The output name.
1843
+ type: string
1844
+ nbfiles:
1845
+ description: The number of files to register in the output dataset.
1846
+ type: string
1847
+ responses:
1848
+ 201:
1849
+ description: OK
1850
+ content:
1851
+ application/json:
1852
+ schema:
1853
+ type: string
1854
+ enum: ["Created"]
1855
+ 401:
1856
+ description: Invalid Auth Token
1857
+ 404:
1858
+ description: Not found
1859
+ 406:
1860
+ description: Not acceptable
1861
+ 409:
1862
+ description: Duplication
1863
+ """
1864
+ parameters = json_parameters()
1865
+ try:
1866
+ create_did_sample(
1867
+ input_scope=parameters['input_scope'],
1868
+ input_name=parameters['input_name'],
1869
+ output_scope=parameters['output_scope'],
1870
+ output_name=parameters['output_name'],
1871
+ issuer=request.environ.get('issuer'),
1872
+ nbfiles=parameters['nbfiles'],
1873
+ vo=request.environ.get('vo'),
1874
+ )
1875
+ except DataIdentifierNotFound as error:
1876
+ return generate_http_error_flask(404, error)
1877
+ except (DuplicateContent, DataIdentifierAlreadyExists, UnsupportedOperation) as error:
1878
+ return generate_http_error_flask(409, error)
1879
+ except AccessDenied as error:
1880
+ return generate_http_error_flask(401, error)
1881
+
1882
+ return 'Created', 201
1883
+
1884
+
1885
+ class NewDIDs(ErrorHandlingMethodView):
1886
+
1887
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
1888
+ def get(self):
1889
+ """
1890
+ ---
1891
+ summary: Get recent identifiers
1892
+ description: Returns a list of recent identifiers.
1893
+ tags:
1894
+ - Data Identifiers
1895
+ parameters:
1896
+ - name: type
1897
+ in: query
1898
+ description: The type of the did.
1899
+ schema:
1900
+ type: string
1901
+ required: false
1902
+ responses:
1903
+ 200:
1904
+ description: OK
1905
+ content:
1906
+ application/x-json-stream:
1907
+ schema:
1908
+ description: A list of the recent dids. Items are seperated by new line characters.
1909
+ type: array
1910
+ items:
1911
+ description: A did.
1912
+ type: object
1913
+ properties:
1914
+ scope:
1915
+ description: The scope of the did.
1916
+ type: string
1917
+ name:
1918
+ description: The name of the did.
1919
+ type: string
1920
+ did_type:
1921
+ description: The type of the did.
1922
+ type: string
1923
+ 401:
1924
+ description: Invalid Auth Token
1925
+ 406:
1926
+ description: Not acceptable
1927
+ """
1928
+ def generate(_type, vo):
1929
+ for did in list_new_dids(did_type=_type, vo=vo):
1930
+ yield dumps(did, cls=APIEncoder) + '\n'
1931
+
1932
+ type_param = request.args.get('type', default=None)
1933
+
1934
+ return try_stream(generate(_type=type_param, vo=request.environ.get('vo')))
1935
+
1936
+
1937
+ class Resurrect(ErrorHandlingMethodView):
1938
+
1939
+ def post(self):
1940
+ """
1941
+ ---
1942
+ summary: Resurrect dids
1943
+ description: Resurrect all given dids.
1944
+ tags:
1945
+ - Data Identifiers
1946
+ requestBody:
1947
+ content:
1948
+ 'application/json':
1949
+ schema:
1950
+ description: List of did to resurrect.
1951
+ type: array
1952
+ items:
1953
+ description: A did to resurrect.
1954
+ type: object
1955
+ properties:
1956
+ scope:
1957
+ description: The scope of the did.
1958
+ type: string
1959
+ name:
1960
+ description: The name of the did
1961
+ type: string
1962
+ responses:
1963
+ 201:
1964
+ description: OK
1965
+ content:
1966
+ application/json:
1967
+ schema:
1968
+ type: string
1969
+ enum: ["Created"]
1970
+ 401:
1971
+ description: Invalid Auth Token
1972
+ 404:
1973
+ description: Not found
1974
+ 409:
1975
+ description: Conflict
1976
+ 500:
1977
+ description: Internal error
1978
+ """
1979
+ dids = json_list()
1980
+
1981
+ try:
1982
+ resurrect(dids=dids, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
1983
+ except DataIdentifierNotFound as error:
1984
+ return generate_http_error_flask(404, error)
1985
+ except (DuplicateContent, DataIdentifierAlreadyExists, UnsupportedOperation) as error:
1986
+ return generate_http_error_flask(409, error)
1987
+ except AccessDenied as error:
1988
+ return generate_http_error_flask(401, error)
1989
+ return 'Created', 201
1990
+
1991
+
1992
+ class Follow(ErrorHandlingMethodView):
1993
+
1994
+ @check_accept_header_wrapper_flask(['application/json'])
1995
+ def get(self, scope_name):
1996
+ """
1997
+ ---
1998
+ summary: Get followers
1999
+ description: Get all followers for a specific did.
2000
+ tags:
2001
+ - Data Identifiers
2002
+ parameters:
2003
+ - name: scope_name
2004
+ in: path
2005
+ description: The scope and the name of the did.
2006
+ schema:
2007
+ type: string
2008
+ style: simple
2009
+ responses:
2010
+ 200:
2011
+ description: OK
2012
+ content:
2013
+ application/json:
2014
+ schema:
2015
+ description: A list of all followers of a did.
2016
+ type: array
2017
+ items:
2018
+ description: A follower of a did.
2019
+ type: object
2020
+ properties:
2021
+ user:
2022
+ description: The user which follows the did.
2023
+ type: string
2024
+ 400:
2025
+ description: Value error
2026
+ 401:
2027
+ description: Invalid Auth Token
2028
+ 404:
2029
+ description: Did not found
2030
+ 406:
2031
+ description: Not acceptable
2032
+ """
2033
+ try:
2034
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
2035
+
2036
+ def generate(vo):
2037
+ for user in get_users_following_did(scope=scope, name=name, vo=vo):
2038
+ yield render_json(**user) + '\n'
2039
+
2040
+ return try_stream(generate(vo=request.environ.get('vo')), content_type='application/json')
2041
+ except ValueError as error:
2042
+ return generate_http_error_flask(400, error)
2043
+ except DataIdentifierNotFound as error:
2044
+ return generate_http_error_flask(404, error)
2045
+
2046
+ def post(self, scope_name):
2047
+ """
2048
+ ---
2049
+ summary: Post follow
2050
+ description: Mark the input DID as being followed by the given account.
2051
+ tags:
2052
+ - Data Identifiers
2053
+ parameters:
2054
+ - name: scope_name
2055
+ in: path
2056
+ description: The scope and the name of the did.
2057
+ schema:
2058
+ type: string
2059
+ style: simple
2060
+ requestBody:
2061
+ content:
2062
+ application/json:
2063
+ schema:
2064
+ type: object
2065
+ required:
2066
+ - account
2067
+ properties:
2068
+ account:
2069
+ description: The account to follow the did.
2070
+ type: string
2071
+ responses:
2072
+ 201:
2073
+ description: OK
2074
+ 400:
2075
+ description: Scope or name could not be interpreted
2076
+ 401:
2077
+ description: Invalid Auth Token
2078
+ 404:
2079
+ description: Not found
2080
+ 500:
2081
+ description: Internal error
2082
+ """
2083
+ try:
2084
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
2085
+ except ValueError as error:
2086
+ return generate_http_error_flask(400, error)
2087
+
2088
+ parameters = json_parameters()
2089
+ account = param_get(parameters, 'account')
2090
+
2091
+ try:
2092
+ add_did_to_followed(scope=scope, name=name, account=account, vo=request.environ.get('vo'))
2093
+ except DataIdentifierNotFound as error:
2094
+ return generate_http_error_flask(404, error)
2095
+ except AccessDenied as error:
2096
+ return generate_http_error_flask(401, error)
2097
+
2098
+ def delete(self, scope_name):
2099
+ """
2100
+ ---
2101
+ summary: Delete follow
2102
+ description: Mark the input DID as not followed
2103
+ tags:
2104
+ - Data Identifiers
2105
+ parameters:
2106
+ - name: scope_name
2107
+ in: path
2108
+ description: The scope and the name of the did.
2109
+ schema:
2110
+ type: string
2111
+ style: simple
2112
+ requestBody:
2113
+ content:
2114
+ 'application/json':
2115
+ schema:
2116
+ type: object
2117
+ required:
2118
+ - account
2119
+ properties:
2120
+ account:
2121
+ description: The account to unfollow the did.
2122
+ type: string
2123
+ responses:
2124
+ 200:
2125
+ description: OK
2126
+ 401:
2127
+ description: Invalid Auth Token
2128
+ 404:
2129
+ description: Not found
2130
+ 500:
2131
+ description: Internal error
2132
+ """
2133
+ try:
2134
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
2135
+ except ValueError as error:
2136
+ return generate_http_error_flask(400, error)
2137
+
2138
+ parameters = json_parameters()
2139
+ account = param_get(parameters, 'account')
2140
+
2141
+ try:
2142
+ remove_did_from_followed(scope=scope, name=name, account=account, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
2143
+ except DataIdentifierNotFound as error:
2144
+ return generate_http_error_flask(404, error)
2145
+
2146
+ return '', 200
2147
+
2148
+
2149
+ def blueprint():
2150
+ bp = AuthenticatedBlueprint('dids', __name__, url_prefix='/dids')
2151
+
2152
+ scope_view = Scope.as_view('scope')
2153
+ bp.add_url_rule('/<scope>/', view_func=scope_view, methods=['get', ])
2154
+ guid_lookup_view = GUIDLookup.as_view('guid_lookup')
2155
+ bp.add_url_rule('/<guid>/guid', view_func=guid_lookup_view, methods=['get', ])
2156
+ search_view = Search.as_view('search')
2157
+ bp.add_url_rule('/<scope>/dids/search', view_func=search_view, methods=['get', ])
2158
+ dids_view = DIDs.as_view('dids')
2159
+ bp.add_url_rule('/<path:scope_name>/status', view_func=dids_view, methods=['put', 'get'])
2160
+ files_view = Files.as_view('files')
2161
+ bp.add_url_rule('/<path:scope_name>/files', view_func=files_view, methods=['get', ])
2162
+ attachment_history_view = AttachmentHistory.as_view('attachment_history')
2163
+ bp.add_url_rule('/<path:scope_name>/dids/history', view_func=attachment_history_view, methods=['get', ])
2164
+ attachment_view = Attachment.as_view('attachment')
2165
+ bp.add_url_rule('/<path:scope_name>/dids', view_func=attachment_view, methods=['get', 'post', 'delete'])
2166
+ meta_view = Meta.as_view('meta')
2167
+ bp.add_url_rule('/<path:scope_name>/meta', view_func=meta_view, methods=['get', 'post', 'delete'])
2168
+ singlemeta_view = SingleMeta.as_view('singlemeta')
2169
+ bp.add_url_rule('/<path:scope_name>/meta/<key>', view_func=singlemeta_view, methods=['post', ])
2170
+ bulkdidsmeta_view = BulkDIDsMeta.as_view('bulkdidsmeta')
2171
+ bp.add_url_rule('/bulkdidsmeta', view_func=bulkdidsmeta_view, methods=['post', ])
2172
+ rules_view = Rules.as_view('rules')
2173
+ bp.add_url_rule('/<path:scope_name>/rules', view_func=rules_view, methods=['get', ])
2174
+ parents_view = Parents.as_view('parents')
2175
+ bp.add_url_rule('/<path:scope_name>/parents', view_func=parents_view, methods=['get', ])
2176
+ associated_rules_view = AssociatedRules.as_view('associated_rules')
2177
+ bp.add_url_rule('/<path:scope_name>/associated_rules', view_func=associated_rules_view, methods=['get', ])
2178
+ follow_view = Follow.as_view('follow')
2179
+ bp.add_url_rule('/<path:scope_name>/follow', view_func=follow_view, methods=['get', 'post', 'delete'])
2180
+ bp.add_url_rule('/<path:scope_name>', view_func=dids_view, methods=['get', 'post'])
2181
+ bulkdids_view = BulkDIDS.as_view('bulkdids')
2182
+ bp.add_url_rule('', view_func=bulkdids_view, methods=['post', ])
2183
+ sample_view_legacy = SampleLegacy.as_view('sample')
2184
+ bp.add_url_rule('/<input_scope>/<input_name>/<output_scope>/<output_name>/<nbfiles>/sample', view_func=sample_view_legacy, methods=['post', ])
2185
+ sample_view = Sample.as_view('sample_new')
2186
+ bp.add_url_rule('/sample', view_func=sample_view, methods=['post', ])
2187
+ attachements_view = Attachments.as_view('attachments')
2188
+ bp.add_url_rule('/attachments', view_func=attachements_view, methods=['post', ])
2189
+ new_dids_view = NewDIDs.as_view('new_dids')
2190
+ bp.add_url_rule('/new', view_func=new_dids_view, methods=['get', ])
2191
+ resurrect_view = Resurrect.as_view('resurrect')
2192
+ bp.add_url_rule('/resurrect', view_func=resurrect_view, methods=['post', ])
2193
+ bulkmeta_view = BulkMeta.as_view('bulkmeta')
2194
+ bp.add_url_rule('/bulkmeta', view_func=bulkmeta_view, methods=['post', ])
2195
+
2196
+ bp.after_request(response_headers)
2197
+ return bp
2198
+
2199
+
2200
+ def make_doc():
2201
+ """ Only used for sphinx documentation """
2202
+ doc_app = Flask(__name__)
2203
+ doc_app.register_blueprint(blueprint())
2204
+ return doc_app