UncountablePythonSDK 0.0.24__py3-none-any.whl → 0.0.131__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 UncountablePythonSDK might be problematic. Click here for more details.

Files changed (373) hide show
  1. docs/conf.py +60 -8
  2. docs/index.md +107 -4
  3. docs/integration_examples/create_ingredient.md +43 -0
  4. docs/integration_examples/create_output.md +56 -0
  5. docs/integration_examples/index.md +6 -0
  6. docs/justfile +2 -2
  7. docs/requirements.txt +7 -5
  8. examples/async_batch.py +5 -6
  9. examples/basic_auth.py +7 -0
  10. examples/create_entity.py +4 -6
  11. examples/create_ingredient_sdk.py +34 -0
  12. examples/download_files.py +26 -0
  13. examples/edit_recipe_inputs.py +50 -0
  14. examples/integration-server/jobs/materials_auto/concurrent_cron.py +11 -0
  15. examples/integration-server/jobs/materials_auto/example_cron.py +21 -0
  16. examples/integration-server/jobs/materials_auto/example_http.py +47 -0
  17. examples/integration-server/jobs/materials_auto/example_instrument.py +100 -0
  18. examples/integration-server/jobs/materials_auto/example_parse.py +140 -0
  19. examples/integration-server/jobs/materials_auto/example_predictions.py +61 -0
  20. examples/integration-server/jobs/materials_auto/example_runsheet_wh.py +39 -0
  21. examples/integration-server/jobs/materials_auto/example_wh.py +23 -0
  22. examples/integration-server/jobs/materials_auto/profile.yaml +104 -0
  23. examples/integration-server/pyproject.toml +224 -0
  24. examples/invoke_uploader.py +26 -0
  25. examples/oauth.py +7 -0
  26. examples/set_recipe_metadata_file.py +40 -0
  27. examples/set_recipe_output_file_sdk.py +26 -0
  28. examples/upload_files.py +2 -3
  29. pkgs/argument_parser/__init__.py +9 -0
  30. pkgs/argument_parser/_is_namedtuple.py +3 -0
  31. pkgs/argument_parser/argument_parser.py +295 -74
  32. pkgs/argument_parser/case_convert.py +4 -3
  33. pkgs/filesystem_utils/__init__.py +20 -0
  34. pkgs/filesystem_utils/_blob_session.py +144 -0
  35. pkgs/filesystem_utils/_gdrive_session.py +309 -0
  36. pkgs/filesystem_utils/_local_session.py +69 -0
  37. pkgs/filesystem_utils/_s3_session.py +118 -0
  38. pkgs/filesystem_utils/_sftp_session.py +151 -0
  39. pkgs/filesystem_utils/file_type_utils.py +91 -0
  40. pkgs/filesystem_utils/filesystem_session.py +39 -0
  41. pkgs/py.typed +0 -0
  42. pkgs/serialization/__init__.py +8 -1
  43. pkgs/serialization/annotation.py +64 -0
  44. pkgs/serialization/missing_sentry.py +1 -1
  45. pkgs/serialization/opaque_key.py +1 -1
  46. pkgs/serialization/serial_alias.py +47 -0
  47. pkgs/serialization/serial_class.py +69 -54
  48. pkgs/serialization/serial_generic.py +16 -0
  49. pkgs/serialization/serial_union.py +84 -0
  50. pkgs/serialization/yaml.py +57 -0
  51. pkgs/serialization_util/__init__.py +7 -7
  52. pkgs/serialization_util/convert_to_snakecase.py +27 -0
  53. pkgs/serialization_util/dataclasses.py +14 -0
  54. pkgs/serialization_util/serialization_helpers.py +117 -71
  55. pkgs/type_spec/actions_registry/__main__.py +0 -4
  56. pkgs/type_spec/actions_registry/emit_typescript.py +5 -5
  57. pkgs/type_spec/builder.py +438 -109
  58. pkgs/type_spec/builder_types.py +9 -0
  59. pkgs/type_spec/config.py +52 -24
  60. pkgs/type_spec/cross_output_links.py +99 -0
  61. pkgs/type_spec/emit_io_ts.py +1 -1
  62. pkgs/type_spec/emit_open_api.py +160 -41
  63. pkgs/type_spec/emit_open_api_util.py +13 -7
  64. pkgs/type_spec/emit_python.py +450 -136
  65. pkgs/type_spec/emit_typescript.py +117 -250
  66. pkgs/type_spec/emit_typescript_util.py +293 -4
  67. pkgs/type_spec/load_types.py +20 -5
  68. pkgs/type_spec/non_discriminated_union_exceptions.py +14 -0
  69. pkgs/type_spec/open_api_util.py +29 -4
  70. pkgs/type_spec/parts/base.py.prepart +13 -10
  71. pkgs/type_spec/parts/base.ts.prepart +4 -0
  72. pkgs/type_spec/type_info/__main__.py +3 -1
  73. pkgs/type_spec/type_info/emit_type_info.py +161 -32
  74. pkgs/type_spec/ui_entry_actions/__init__.py +4 -0
  75. pkgs/type_spec/ui_entry_actions/generate_ui_entry_actions.py +308 -0
  76. pkgs/type_spec/util.py +4 -4
  77. pkgs/type_spec/value_spec/__main__.py +27 -10
  78. pkgs/type_spec/value_spec/convert_type.py +21 -1
  79. pkgs/type_spec/value_spec/emit_python.py +25 -7
  80. pkgs/type_spec/value_spec/types.py +1 -1
  81. uncountable/__init__.py +1 -2
  82. uncountable/core/__init__.py +11 -3
  83. uncountable/core/async_batch.py +16 -1
  84. uncountable/core/client.py +247 -52
  85. uncountable/core/environment.py +41 -0
  86. uncountable/core/file_upload.py +67 -22
  87. uncountable/core/types.py +8 -13
  88. uncountable/integration/cli.py +142 -0
  89. uncountable/integration/construct_client.py +43 -27
  90. uncountable/integration/cron.py +12 -11
  91. uncountable/integration/db/connect.py +12 -2
  92. uncountable/integration/db/session.py +25 -0
  93. uncountable/integration/entrypoint.py +4 -34
  94. uncountable/integration/executors/executors.py +147 -0
  95. uncountable/integration/executors/generic_upload_executor.py +336 -0
  96. uncountable/integration/executors/script_executor.py +15 -9
  97. uncountable/integration/http_server/__init__.py +5 -0
  98. uncountable/integration/http_server/types.py +69 -0
  99. uncountable/integration/job.py +246 -19
  100. uncountable/integration/queue_runner/__init__.py +0 -0
  101. uncountable/integration/queue_runner/command_server/__init__.py +28 -0
  102. uncountable/integration/queue_runner/command_server/command_client.py +133 -0
  103. uncountable/integration/queue_runner/command_server/command_server.py +142 -0
  104. uncountable/integration/queue_runner/command_server/constants.py +4 -0
  105. uncountable/integration/queue_runner/command_server/protocol/__init__.py +0 -0
  106. uncountable/integration/queue_runner/command_server/protocol/command_server.proto +58 -0
  107. uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.py +57 -0
  108. uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.pyi +114 -0
  109. uncountable/integration/queue_runner/command_server/protocol/command_server_pb2_grpc.py +264 -0
  110. uncountable/integration/queue_runner/command_server/types.py +75 -0
  111. uncountable/integration/queue_runner/datastore/__init__.py +3 -0
  112. uncountable/integration/queue_runner/datastore/datastore_sqlite.py +250 -0
  113. uncountable/integration/queue_runner/datastore/interface.py +29 -0
  114. uncountable/integration/queue_runner/datastore/model.py +24 -0
  115. uncountable/integration/queue_runner/job_scheduler.py +200 -0
  116. uncountable/integration/queue_runner/queue_runner.py +34 -0
  117. uncountable/integration/queue_runner/types.py +7 -0
  118. uncountable/integration/queue_runner/worker.py +116 -0
  119. uncountable/integration/scan_profiles.py +67 -0
  120. uncountable/integration/scheduler.py +199 -0
  121. uncountable/integration/secret_retrieval/__init__.py +3 -0
  122. uncountable/integration/secret_retrieval/retrieve_secret.py +93 -0
  123. uncountable/integration/server.py +103 -54
  124. uncountable/integration/telemetry.py +251 -0
  125. uncountable/integration/webhook_server/entrypoint.py +97 -0
  126. uncountable/types/__init__.py +149 -30
  127. uncountable/types/api/batch/execute_batch.py +16 -9
  128. uncountable/types/api/batch/execute_batch_load_async.py +13 -7
  129. uncountable/types/api/chemical/convert_chemical_formats.py +20 -8
  130. uncountable/types/api/condition_parameters/__init__.py +1 -0
  131. uncountable/types/api/condition_parameters/upsert_condition_match.py +72 -0
  132. uncountable/types/api/entity/create_entities.py +24 -12
  133. uncountable/types/api/entity/create_entity.py +22 -13
  134. uncountable/types/api/entity/create_or_update_entity.py +48 -0
  135. uncountable/types/api/entity/export_entities.py +59 -0
  136. uncountable/types/api/entity/get_entities_data.py +18 -9
  137. uncountable/types/api/entity/grant_entity_permissions.py +48 -0
  138. uncountable/types/api/entity/list_aggregate.py +79 -0
  139. uncountable/types/api/entity/list_entities.py +53 -14
  140. uncountable/types/api/entity/lock_entity.py +45 -0
  141. uncountable/types/api/entity/lookup_entity.py +116 -0
  142. uncountable/types/api/entity/resolve_entity_ids.py +19 -10
  143. uncountable/types/api/entity/set_entity_field_values.py +44 -0
  144. uncountable/types/api/entity/set_values.py +15 -8
  145. uncountable/types/api/entity/transition_entity_phase.py +27 -12
  146. uncountable/types/api/entity/unlock_entity.py +44 -0
  147. uncountable/types/api/equipment/__init__.py +1 -0
  148. uncountable/types/api/equipment/associate_equipment_input.py +43 -0
  149. uncountable/types/api/field_options/__init__.py +1 -0
  150. uncountable/types/api/field_options/upsert_field_options.py +55 -0
  151. uncountable/types/api/files/__init__.py +1 -0
  152. uncountable/types/api/files/download_file.py +77 -0
  153. uncountable/types/api/id_source/list_id_source.py +20 -11
  154. uncountable/types/api/id_source/match_id_source.py +15 -10
  155. uncountable/types/api/input_groups/get_input_group_names.py +16 -7
  156. uncountable/types/api/inputs/create_inputs.py +28 -14
  157. uncountable/types/api/inputs/get_input_data.py +34 -16
  158. uncountable/types/api/inputs/get_input_names.py +19 -10
  159. uncountable/types/api/inputs/get_inputs_data.py +29 -11
  160. uncountable/types/api/inputs/set_input_attribute_values.py +16 -10
  161. uncountable/types/api/inputs/set_input_category.py +44 -0
  162. uncountable/types/api/inputs/set_input_subcategories.py +45 -0
  163. uncountable/types/api/inputs/set_intermediate_type.py +50 -0
  164. uncountable/types/api/integrations/__init__.py +1 -0
  165. uncountable/types/api/integrations/publish_realtime_data.py +41 -0
  166. uncountable/types/api/integrations/push_notification.py +49 -0
  167. uncountable/types/api/integrations/register_sockets_token.py +41 -0
  168. uncountable/types/api/listing/__init__.py +1 -0
  169. uncountable/types/api/listing/fetch_listing.py +58 -0
  170. uncountable/types/api/material_families/__init__.py +1 -0
  171. uncountable/types/api/material_families/update_entity_material_families.py +47 -0
  172. uncountable/types/api/notebooks/__init__.py +1 -0
  173. uncountable/types/api/notebooks/add_notebook_content.py +119 -0
  174. uncountable/types/api/outputs/get_output_data.py +32 -17
  175. uncountable/types/api/outputs/get_output_names.py +18 -9
  176. uncountable/types/api/outputs/get_output_organization.py +173 -0
  177. uncountable/types/api/outputs/resolve_output_conditions.py +23 -11
  178. uncountable/types/api/permissions/set_core_permissions.py +31 -15
  179. uncountable/types/api/project/get_projects.py +20 -11
  180. uncountable/types/api/project/get_projects_data.py +23 -14
  181. uncountable/types/api/recipe_links/create_recipe_link.py +17 -10
  182. uncountable/types/api/recipe_links/remove_recipe_link.py +45 -0
  183. uncountable/types/api/recipe_metadata/get_recipe_metadata_data.py +19 -10
  184. uncountable/types/api/recipes/add_recipe_to_project.py +42 -0
  185. uncountable/types/api/recipes/add_time_series_data.py +64 -0
  186. uncountable/types/api/recipes/archive_recipes.py +14 -7
  187. uncountable/types/api/recipes/associate_recipe_as_input.py +16 -8
  188. uncountable/types/api/recipes/associate_recipe_as_lot.py +14 -7
  189. uncountable/types/api/recipes/clear_recipe_outputs.py +42 -0
  190. uncountable/types/api/recipes/create_mix_order.py +44 -0
  191. uncountable/types/api/recipes/create_recipe.py +21 -14
  192. uncountable/types/api/recipes/create_recipes.py +25 -13
  193. uncountable/types/api/recipes/disassociate_recipe_as_input.py +14 -7
  194. uncountable/types/api/recipes/edit_recipe_inputs.py +208 -19
  195. uncountable/types/api/recipes/get_column_calculation_values.py +57 -0
  196. uncountable/types/api/recipes/get_curve.py +15 -9
  197. uncountable/types/api/recipes/get_recipe_calculations.py +17 -11
  198. uncountable/types/api/recipes/get_recipe_links.py +14 -8
  199. uncountable/types/api/recipes/get_recipe_names.py +16 -7
  200. uncountable/types/api/recipes/get_recipe_output_metadata.py +16 -10
  201. uncountable/types/api/recipes/get_recipes_data.py +96 -45
  202. uncountable/types/api/recipes/lock_recipes.py +64 -0
  203. uncountable/types/api/recipes/remove_recipe_from_project.py +42 -0
  204. uncountable/types/api/recipes/set_recipe_inputs.py +19 -13
  205. uncountable/types/api/recipes/set_recipe_metadata.py +14 -7
  206. uncountable/types/api/recipes/set_recipe_output_annotations.py +114 -0
  207. uncountable/types/api/recipes/set_recipe_output_file.py +55 -0
  208. uncountable/types/api/recipes/set_recipe_outputs.py +40 -15
  209. uncountable/types/api/recipes/set_recipe_tags.py +30 -13
  210. uncountable/types/api/recipes/set_recipe_total.py +59 -0
  211. uncountable/types/api/recipes/unarchive_recipes.py +41 -0
  212. uncountable/types/api/recipes/unlock_recipes.py +51 -0
  213. uncountable/types/api/runsheet/__init__.py +1 -0
  214. uncountable/types/api/runsheet/complete_async_upload.py +41 -0
  215. uncountable/types/api/triggers/run_trigger.py +15 -8
  216. uncountable/types/api/uploader/__init__.py +1 -0
  217. uncountable/types/api/uploader/complete_async_parse.py +46 -0
  218. uncountable/types/api/uploader/invoke_uploader.py +46 -0
  219. uncountable/types/api/user/__init__.py +1 -0
  220. uncountable/types/api/user/get_current_user_info.py +40 -0
  221. uncountable/types/async_batch.py +8 -52
  222. uncountable/types/async_batch_processor.py +694 -18
  223. uncountable/types/async_batch_t.py +108 -0
  224. uncountable/types/async_jobs.py +8 -0
  225. uncountable/types/async_jobs_t.py +52 -0
  226. uncountable/types/auth_retrieval.py +11 -0
  227. uncountable/types/auth_retrieval_t.py +75 -0
  228. uncountable/types/base.py +5 -80
  229. uncountable/types/base_t.py +87 -0
  230. uncountable/types/calculations.py +3 -19
  231. uncountable/types/calculations_t.py +26 -0
  232. uncountable/types/chemical_structure.py +3 -23
  233. uncountable/types/chemical_structure_t.py +28 -0
  234. uncountable/types/client_base.py +1170 -88
  235. uncountable/types/client_config.py +8 -0
  236. uncountable/types/client_config_t.py +36 -0
  237. uncountable/types/curves.py +5 -43
  238. uncountable/types/curves_t.py +50 -0
  239. uncountable/types/data.py +12 -0
  240. uncountable/types/data_t.py +103 -0
  241. uncountable/types/entity.py +8 -270
  242. uncountable/types/entity_t.py +446 -0
  243. uncountable/types/experiment_groups.py +3 -19
  244. uncountable/types/experiment_groups_t.py +26 -0
  245. uncountable/types/exports.py +8 -0
  246. uncountable/types/exports_t.py +34 -0
  247. uncountable/types/field_values.py +25 -61
  248. uncountable/types/field_values_t.py +302 -0
  249. uncountable/types/fields.py +3 -20
  250. uncountable/types/fields_t.py +27 -0
  251. uncountable/types/generic_upload.py +14 -0
  252. uncountable/types/generic_upload_t.py +119 -0
  253. uncountable/types/id_source.py +7 -45
  254. uncountable/types/id_source_t.py +68 -0
  255. uncountable/types/identifier.py +6 -50
  256. uncountable/types/identifier_t.py +62 -0
  257. uncountable/types/input_attributes.py +3 -25
  258. uncountable/types/input_attributes_t.py +29 -0
  259. uncountable/types/inputs.py +6 -57
  260. uncountable/types/inputs_t.py +82 -0
  261. uncountable/types/integration_server.py +8 -0
  262. uncountable/types/integration_server_t.py +46 -0
  263. uncountable/types/integration_session.py +10 -0
  264. uncountable/types/integration_session_t.py +60 -0
  265. uncountable/types/integrations.py +10 -0
  266. uncountable/types/integrations_t.py +62 -0
  267. uncountable/types/job_definition.py +28 -0
  268. uncountable/types/job_definition_t.py +285 -0
  269. uncountable/types/listing.py +9 -0
  270. uncountable/types/listing_t.py +51 -0
  271. uncountable/types/notices.py +8 -0
  272. uncountable/types/notices_t.py +37 -0
  273. uncountable/types/notifications.py +11 -0
  274. uncountable/types/notifications_t.py +74 -0
  275. uncountable/types/outputs.py +3 -22
  276. uncountable/types/outputs_t.py +29 -0
  277. uncountable/types/overrides.py +9 -0
  278. uncountable/types/overrides_t.py +49 -0
  279. uncountable/types/permissions.py +3 -42
  280. uncountable/types/permissions_t.py +45 -0
  281. uncountable/types/phases.py +3 -19
  282. uncountable/types/phases_t.py +26 -0
  283. uncountable/types/post_base.py +3 -26
  284. uncountable/types/post_base_t.py +29 -0
  285. uncountable/types/queued_job.py +17 -0
  286. uncountable/types/queued_job_t.py +140 -0
  287. uncountable/types/recipe_identifiers.py +7 -58
  288. uncountable/types/recipe_identifiers_t.py +75 -0
  289. uncountable/types/recipe_inputs.py +4 -26
  290. uncountable/types/recipe_inputs_t.py +29 -0
  291. uncountable/types/recipe_links.py +4 -46
  292. uncountable/types/recipe_links_t.py +53 -0
  293. uncountable/types/recipe_metadata.py +5 -48
  294. uncountable/types/recipe_metadata_t.py +57 -0
  295. uncountable/types/recipe_output_metadata.py +3 -20
  296. uncountable/types/recipe_output_metadata_t.py +27 -0
  297. uncountable/types/recipe_tags.py +3 -19
  298. uncountable/types/recipe_tags_t.py +26 -0
  299. uncountable/types/recipe_workflow_steps.py +9 -73
  300. uncountable/types/recipe_workflow_steps_t.py +95 -0
  301. uncountable/types/recipes.py +7 -0
  302. uncountable/types/recipes_t.py +25 -0
  303. uncountable/types/response.py +3 -21
  304. uncountable/types/response_t.py +26 -0
  305. uncountable/types/secret_retrieval.py +11 -0
  306. uncountable/types/secret_retrieval_t.py +75 -0
  307. uncountable/types/sockets.py +20 -0
  308. uncountable/types/sockets_t.py +169 -0
  309. uncountable/types/structured_filters.py +25 -0
  310. uncountable/types/structured_filters_t.py +248 -0
  311. uncountable/types/units.py +3 -19
  312. uncountable/types/units_t.py +26 -0
  313. uncountable/types/uploader.py +24 -0
  314. uncountable/types/uploader_t.py +222 -0
  315. uncountable/types/users.py +3 -20
  316. uncountable/types/users_t.py +27 -0
  317. uncountable/types/webhook_job.py +9 -0
  318. uncountable/types/webhook_job_t.py +48 -0
  319. uncountable/types/workflows.py +4 -28
  320. uncountable/types/workflows_t.py +38 -0
  321. uncountablepythonsdk-0.0.131.dist-info/METADATA +64 -0
  322. uncountablepythonsdk-0.0.131.dist-info/RECORD +363 -0
  323. {UncountablePythonSDK-0.0.24.dist-info → uncountablepythonsdk-0.0.131.dist-info}/WHEEL +1 -1
  324. {UncountablePythonSDK-0.0.24.dist-info → uncountablepythonsdk-0.0.131.dist-info}/top_level.txt +0 -1
  325. UncountablePythonSDK-0.0.24.dist-info/METADATA +0 -47
  326. UncountablePythonSDK-0.0.24.dist-info/RECORD +0 -216
  327. docs/quickstart.md +0 -19
  328. examples/recipe-import/importer.py +0 -39
  329. type_spec/external/api/batch/execute_batch.yaml +0 -56
  330. type_spec/external/api/batch/execute_batch_load_async.yaml +0 -18
  331. type_spec/external/api/chemical/convert_chemical_formats.yaml +0 -33
  332. type_spec/external/api/entity/create_entities.yaml +0 -45
  333. type_spec/external/api/entity/create_entity.yaml +0 -51
  334. type_spec/external/api/entity/get_entities_data.yaml +0 -29
  335. type_spec/external/api/entity/list_entities.yaml +0 -52
  336. type_spec/external/api/entity/resolve_entity_ids.yaml +0 -29
  337. type_spec/external/api/entity/set_values.yaml +0 -18
  338. type_spec/external/api/entity/transition_entity_phase.yaml +0 -44
  339. type_spec/external/api/id_source/list_id_source.yaml +0 -35
  340. type_spec/external/api/id_source/match_id_source.yaml +0 -32
  341. type_spec/external/api/input_groups/get_input_group_names.yaml +0 -29
  342. type_spec/external/api/inputs/create_inputs.yaml +0 -48
  343. type_spec/external/api/inputs/get_input_data.yaml +0 -95
  344. type_spec/external/api/inputs/get_input_names.yaml +0 -38
  345. type_spec/external/api/inputs/get_inputs_data.yaml +0 -82
  346. type_spec/external/api/inputs/set_input_attribute_values.yaml +0 -33
  347. type_spec/external/api/outputs/get_output_data.yaml +0 -92
  348. type_spec/external/api/outputs/get_output_names.yaml +0 -35
  349. type_spec/external/api/outputs/resolve_output_conditions.yaml +0 -50
  350. type_spec/external/api/permissions/set_core_permissions.yaml +0 -69
  351. type_spec/external/api/project/get_projects.yaml +0 -42
  352. type_spec/external/api/project/get_projects_data.yaml +0 -50
  353. type_spec/external/api/recipe_links/create_recipe_link.yaml +0 -25
  354. type_spec/external/api/recipe_metadata/get_recipe_metadata_data.yaml +0 -41
  355. type_spec/external/api/recipes/archive_recipes.yaml +0 -20
  356. type_spec/external/api/recipes/associate_recipe_as_input.yaml +0 -19
  357. type_spec/external/api/recipes/associate_recipe_as_lot.yaml +0 -19
  358. type_spec/external/api/recipes/create_recipe.yaml +0 -39
  359. type_spec/external/api/recipes/create_recipes.yaml +0 -47
  360. type_spec/external/api/recipes/disassociate_recipe_as_input.yaml +0 -16
  361. type_spec/external/api/recipes/edit_recipe_inputs.yaml +0 -85
  362. type_spec/external/api/recipes/get_curve.yaml +0 -21
  363. type_spec/external/api/recipes/get_recipe_calculations.yaml +0 -39
  364. type_spec/external/api/recipes/get_recipe_links.yaml +0 -26
  365. type_spec/external/api/recipes/get_recipe_names.yaml +0 -29
  366. type_spec/external/api/recipes/get_recipe_output_metadata.yaml +0 -36
  367. type_spec/external/api/recipes/get_recipes_data.yaml +0 -244
  368. type_spec/external/api/recipes/set_recipe_inputs.yaml +0 -42
  369. type_spec/external/api/recipes/set_recipe_metadata.yaml +0 -20
  370. type_spec/external/api/recipes/set_recipe_outputs.yaml +0 -52
  371. type_spec/external/api/recipes/set_recipe_tags.yaml +0 -62
  372. type_spec/external/api/triggers/run_trigger.yaml +0 -18
  373. uncountable/integration/types.py +0 -89
@@ -1,45 +1,272 @@
1
+ import functools
2
+ import hmac
3
+ import typing
4
+ from abc import ABC, abstractmethod
1
5
  from dataclasses import dataclass
2
- from uncountable.core.client import Client
3
- from uncountable.integration.types import JobDefinition
4
6
 
5
- from abc import ABC, abstractmethod
7
+ import simplejson
8
+
9
+ from pkgs.argument_parser import CachedParser
10
+ from pkgs.serialization_util import serialize_for_api
11
+ from uncountable.core.async_batch import AsyncBatchProcessor
12
+ from uncountable.core.client import Client
13
+ from uncountable.core.environment import get_local_admin_server_port
14
+ from uncountable.core.file_upload import FileUpload
15
+ from uncountable.core.types import AuthDetailsOAuth
16
+ from uncountable.integration.http_server import (
17
+ GenericHttpRequest,
18
+ GenericHttpResponse,
19
+ HttpException,
20
+ )
21
+ from uncountable.integration.queue_runner.command_server.command_client import (
22
+ send_job_queue_message,
23
+ )
24
+ from uncountable.integration.queue_runner.command_server.types import (
25
+ CommandServerException,
26
+ )
27
+ from uncountable.integration.secret_retrieval.retrieve_secret import retrieve_secret
28
+ from uncountable.integration.telemetry import JobLogger
29
+ from uncountable.types import (
30
+ base_t,
31
+ job_definition_t,
32
+ queued_job_t,
33
+ webhook_job_t,
34
+ )
35
+ from uncountable.types.job_definition_t import (
36
+ HttpJobDefinitionBase,
37
+ JobDefinition,
38
+ JobResult,
39
+ ProfileMetadata,
40
+ )
6
41
 
7
42
 
8
- @dataclass
9
- class JobArgumentsBase:
43
+ @dataclass(kw_only=True)
44
+ class JobArguments:
10
45
  job_definition: JobDefinition
46
+ profile_metadata: ProfileMetadata
11
47
  client: Client
48
+ batch_processor: AsyncBatchProcessor
49
+ logger: JobLogger
50
+ payload: base_t.JsonValue
51
+ job_uuid: str
52
+
53
+
54
+ # only for compatibility:
55
+ CronJobArguments = JobArguments
56
+
57
+
58
+ class Job[PT](ABC):
59
+ _unc_job_registered: bool = False
60
+
61
+ @property
62
+ @abstractmethod
63
+ def payload_type(self) -> type[PT]: ...
64
+
65
+ @abstractmethod
66
+ def run_outer(self, args: JobArguments) -> JobResult: ...
67
+
68
+ @functools.cached_property
69
+ def _cached_payload_parser(self) -> CachedParser[PT]:
70
+ return CachedParser(self.payload_type)
71
+
72
+ def get_payload(self, payload: base_t.JsonValue) -> PT:
73
+ return self._cached_payload_parser.parse_storage(payload)
74
+
75
+
76
+ class CronJob(Job):
77
+ @property
78
+ def payload_type(self) -> type[None]:
79
+ return type(None)
80
+
81
+ def run_outer(self, args: JobArguments) -> JobResult:
82
+ assert isinstance(args, CronJobArguments)
83
+ return self.run(args)
84
+
85
+ @abstractmethod
86
+ def run(self, args: JobArguments) -> JobResult: ...
87
+
88
+
89
+ WPT = typing.TypeVar("WPT")
12
90
 
13
91
 
14
- @dataclass
15
- class CronJobArguments(JobArgumentsBase):
16
- # can imagine passing additional data such as in the sftp or webhook cases
92
+ @dataclass(kw_only=True)
93
+ class WebhookResponse:
17
94
  pass
18
95
 
19
96
 
20
- JobArguments = CronJobArguments
97
+ class _RequestValidatorClient(Client):
98
+ def __init__(self, *, base_url: str, oauth_bearer_token: str):
99
+ super().__init__(
100
+ base_url=base_url,
101
+ auth_details=AuthDetailsOAuth(refresh_token=""),
102
+ config=None,
103
+ )
104
+ self._oauth_bearer_token = oauth_bearer_token
21
105
 
106
+ def _get_oauth_bearer_token(self, *, oauth_details: AuthDetailsOAuth) -> str:
107
+ return self._oauth_bearer_token
22
108
 
23
- @dataclass
24
- class JobResult:
25
- success: bool
26
109
 
110
+ class CustomHttpJob(Job[GenericHttpRequest]):
111
+ @property
112
+ def payload_type(self) -> type[GenericHttpRequest]:
113
+ return GenericHttpRequest
27
114
 
28
- class Job(ABC):
29
- _unc_job_registered: bool = False
115
+ @staticmethod
116
+ @abstractmethod
117
+ def validate_request(
118
+ *,
119
+ request: GenericHttpRequest,
120
+ job_definition: HttpJobDefinitionBase,
121
+ profile_meta: ProfileMetadata,
122
+ ) -> None:
123
+ """
124
+ Validate that the request is valid. If the request is invalid, raise an
125
+ exception.
126
+ """
127
+ ...
30
128
 
129
+ @staticmethod
130
+ def get_validated_oauth_request_user_id(
131
+ *, profile_metadata: ProfileMetadata, request: GenericHttpRequest
132
+ ) -> base_t.ObjectId:
133
+ token = request.headers.get("Authorization", "").replace("Bearer ", "")
134
+ if token == "":
135
+ raise HttpException(
136
+ message="unauthorized; no bearer token in request", error_code=401
137
+ )
138
+ return (
139
+ _RequestValidatorClient(
140
+ base_url=profile_metadata.base_url,
141
+ oauth_bearer_token=token,
142
+ )
143
+ .get_current_user_info()
144
+ .user_id
145
+ )
146
+
147
+ @staticmethod
31
148
  @abstractmethod
32
- def run(self, args: JobArguments) -> JobResult:
149
+ def handle_request(
150
+ *,
151
+ request: GenericHttpRequest,
152
+ job_definition: HttpJobDefinitionBase,
153
+ profile_meta: ProfileMetadata,
154
+ ) -> GenericHttpResponse:
155
+ """
156
+ Handle the request synchronously. Normally this should just enqueue a job
157
+ and return immediately (see WebhookJob as an example).
158
+ """
33
159
  ...
34
160
 
161
+ def run_outer(self, args: JobArguments) -> JobResult:
162
+ args.logger.log_warning(
163
+ message=f"Unexpected call to run_outer for CustomHttpJob: {args.job_definition.id}"
164
+ )
165
+ return JobResult(success=False)
35
166
 
36
- class CronJob(Job):
37
167
 
168
+ class WebhookJob[WPT](Job[webhook_job_t.WebhookEventPayload]):
169
+ @property
170
+ def payload_type(self) -> type[webhook_job_t.WebhookEventPayload]:
171
+ return webhook_job_t.WebhookEventPayload
172
+
173
+ @property
38
174
  @abstractmethod
39
- def run(self, args: CronJobArguments) -> JobResult:
40
- ...
175
+ def webhook_payload_type(self) -> type[WPT]: ...
176
+
177
+ @staticmethod
178
+ def validate_request(
179
+ *,
180
+ request: GenericHttpRequest,
181
+ job_definition: job_definition_t.HttpJobDefinitionBase,
182
+ profile_meta: ProfileMetadata,
183
+ ) -> None:
184
+ assert isinstance(job_definition, job_definition_t.WebhookJobDefinition)
185
+ signature_key = retrieve_secret(
186
+ profile_metadata=profile_meta,
187
+ secret_retrieval=job_definition.signature_key_secret,
188
+ )
189
+ passed_signature = request.headers.get("Uncountable-Webhook-Signature")
190
+ if passed_signature is None:
191
+ raise HttpException.no_signature_passed()
192
+
193
+ request_body_signature = hmac.new(
194
+ signature_key.encode("utf-8"), msg=request.body_bytes, digestmod="sha256"
195
+ ).hexdigest()
196
+
197
+ if request_body_signature != passed_signature:
198
+ raise HttpException.payload_failed_signature()
199
+
200
+ @staticmethod
201
+ def handle_request(
202
+ *,
203
+ request: GenericHttpRequest,
204
+ job_definition: job_definition_t.HttpJobDefinitionBase,
205
+ profile_meta: ProfileMetadata, # noqa: ARG004
206
+ ) -> GenericHttpResponse:
207
+ try:
208
+ request_body = simplejson.loads(request.body_text)
209
+ webhook_payload = typing.cast(base_t.JsonValue, request_body)
210
+ except (simplejson.JSONDecodeError, ValueError) as e:
211
+ raise HttpException.body_parse_error() from e
212
+
213
+ try:
214
+ send_job_queue_message(
215
+ job_ref_name=job_definition.id,
216
+ payload=queued_job_t.QueuedJobPayload(
217
+ invocation_context=queued_job_t.InvocationContextWebhook(
218
+ webhook_payload=webhook_payload
219
+ )
220
+ ),
221
+ port=get_local_admin_server_port(),
222
+ )
223
+ except CommandServerException as e:
224
+ raise HttpException.unknown_error() from e
41
225
 
226
+ return GenericHttpResponse(
227
+ response=simplejson.dumps(serialize_for_api(WebhookResponse())),
228
+ status_code=200,
229
+ )
42
230
 
43
- def register_job(cls: Job):
231
+ def run_outer(self, args: JobArguments) -> JobResult:
232
+ webhook_body = self.get_payload(args.payload)
233
+ inner_payload = CachedParser(self.webhook_payload_type).parse_api(
234
+ webhook_body.data
235
+ )
236
+ return self.run(args, inner_payload)
237
+
238
+ @abstractmethod
239
+ def run(self, args: JobArguments, payload: WPT) -> JobResult: ...
240
+
241
+
242
+ def register_job(cls: type[Job]) -> type[Job]:
44
243
  cls._unc_job_registered = True
45
244
  return cls
245
+
246
+
247
+ class RunsheetWebhookJob(WebhookJob[webhook_job_t.RunsheetWebhookPayload]):
248
+ @property
249
+ def webhook_payload_type(self) -> type:
250
+ return webhook_job_t.RunsheetWebhookPayload
251
+
252
+ @abstractmethod
253
+ def build_runsheet(
254
+ self,
255
+ *,
256
+ args: JobArguments,
257
+ payload: webhook_job_t.RunsheetWebhookPayload,
258
+ ) -> FileUpload: ...
259
+
260
+ def run(
261
+ self, args: JobArguments, payload: webhook_job_t.RunsheetWebhookPayload
262
+ ) -> JobResult:
263
+ runsheet = self.build_runsheet(args=args, payload=payload)
264
+
265
+ files = args.client.upload_files(file_uploads=[runsheet])
266
+ args.client.complete_async_upload(
267
+ async_job_id=payload.async_job_id, file_id=files[0].file_id
268
+ )
269
+
270
+ return JobResult(
271
+ success=True,
272
+ )
File without changes
@@ -0,0 +1,28 @@
1
+ from .command_client import check_health, send_job_queue_message
2
+ from .command_server import serve
3
+ from .types import (
4
+ CommandEnqueueJob,
5
+ CommandEnqueueJobResponse,
6
+ CommandQueue,
7
+ CommandRetryJob,
8
+ CommandRetryJobResponse,
9
+ CommandServerBadResponse,
10
+ CommandServerException,
11
+ CommandServerTimeout,
12
+ CommandTask,
13
+ )
14
+
15
+ __all__: list[str] = [
16
+ "serve",
17
+ "check_health",
18
+ "send_job_queue_message",
19
+ "CommandEnqueueJob",
20
+ "CommandEnqueueJobResponse",
21
+ "CommandRetryJob",
22
+ "CommandRetryJobResponse",
23
+ "CommandTask",
24
+ "CommandQueue",
25
+ "CommandServerTimeout",
26
+ "CommandServerException",
27
+ "CommandServerBadResponse",
28
+ ]
@@ -0,0 +1,133 @@
1
+ from contextlib import contextmanager
2
+ from typing import Generator
3
+
4
+ import grpc
5
+ import simplejson as json
6
+
7
+ from pkgs.serialization_util import serialize_for_api
8
+ from uncountable.integration.queue_runner.command_server.protocol.command_server_pb2 import (
9
+ CheckHealthRequest,
10
+ CheckHealthResult,
11
+ EnqueueJobRequest,
12
+ EnqueueJobResult,
13
+ ListQueuedJobsRequest,
14
+ ListQueuedJobsResult,
15
+ RetryJobRequest,
16
+ RetryJobResult,
17
+ VaccuumQueuedJobsRequest,
18
+ VaccuumQueuedJobsResult,
19
+ )
20
+ from uncountable.integration.queue_runner.command_server.types import (
21
+ CommandServerBadResponse,
22
+ CommandServerTimeout,
23
+ )
24
+ from uncountable.types import queued_job_t
25
+
26
+ from .protocol.command_server_pb2_grpc import CommandServerStub
27
+
28
+ _LOCAL_RPC_HOST = "localhost"
29
+ _DEFAULT_MESSAGE_TIMEOUT_SECS = 2
30
+
31
+
32
+ @contextmanager
33
+ def command_server_connection(
34
+ host: str, port: int
35
+ ) -> Generator[CommandServerStub, None, None]:
36
+ try:
37
+ with grpc.insecure_channel(f"{host}:{port}") as channel:
38
+ stub = CommandServerStub(channel)
39
+ yield stub
40
+ except grpc._channel._InactiveRpcError as e:
41
+ raise CommandServerTimeout() from e
42
+
43
+
44
+ def send_job_queue_message(
45
+ *,
46
+ job_ref_name: str,
47
+ payload: queued_job_t.QueuedJobPayload,
48
+ host: str = "localhost",
49
+ port: int,
50
+ ) -> str:
51
+ with command_server_connection(host=host, port=port) as stub:
52
+ request = EnqueueJobRequest(
53
+ job_ref_name=job_ref_name,
54
+ serialized_payload=json.dumps(serialize_for_api(payload)),
55
+ )
56
+
57
+ response = stub.EnqueueJob(request, timeout=_DEFAULT_MESSAGE_TIMEOUT_SECS)
58
+
59
+ assert isinstance(response, EnqueueJobResult)
60
+ if not response.successfully_queued:
61
+ raise CommandServerBadResponse("queue operation was not successful")
62
+
63
+ return response.queued_job_uuid
64
+
65
+
66
+ def send_retry_job_message(
67
+ *,
68
+ job_uuid: str,
69
+ host: str = "localhost",
70
+ port: int,
71
+ ) -> str:
72
+ with command_server_connection(host=host, port=port) as stub:
73
+ request = RetryJobRequest(uuid=job_uuid)
74
+
75
+ try:
76
+ response = stub.RetryJob(request, timeout=_DEFAULT_MESSAGE_TIMEOUT_SECS)
77
+ assert isinstance(response, RetryJobResult)
78
+ if not response.successfully_queued:
79
+ raise CommandServerBadResponse("queue operation was not successful")
80
+
81
+ return response.queued_job_uuid
82
+ except grpc.RpcError as e:
83
+ raise ValueError(e.details()) # type: ignore
84
+
85
+
86
+ def check_health(*, host: str = _LOCAL_RPC_HOST, port: int) -> bool:
87
+ with command_server_connection(host=host, port=port) as stub:
88
+ request = CheckHealthRequest()
89
+
90
+ response = stub.CheckHealth(request, timeout=_DEFAULT_MESSAGE_TIMEOUT_SECS)
91
+
92
+ assert isinstance(response, CheckHealthResult)
93
+
94
+ return response.success
95
+
96
+
97
+ def send_list_queued_jobs_message(
98
+ *,
99
+ offset: int,
100
+ limit: int,
101
+ host: str = "localhost",
102
+ port: int,
103
+ ) -> list[ListQueuedJobsResult.ListQueuedJobsResultItem]:
104
+ with command_server_connection(host=host, port=port) as stub:
105
+ request = ListQueuedJobsRequest(
106
+ offset=offset,
107
+ limit=limit,
108
+ )
109
+
110
+ try:
111
+ response = stub.ListQueuedJobs(
112
+ request, timeout=_DEFAULT_MESSAGE_TIMEOUT_SECS
113
+ )
114
+ except grpc.RpcError as e:
115
+ raise ValueError(e.details()) # type: ignore
116
+
117
+ assert isinstance(response, ListQueuedJobsResult)
118
+ return list(response.queued_jobs)
119
+
120
+
121
+ def send_vaccuum_queued_jobs_message(*, host: str = "localhost", port: int) -> None:
122
+ with command_server_connection(host=host, port=port) as stub:
123
+ request = VaccuumQueuedJobsRequest()
124
+
125
+ try:
126
+ response = stub.VaccuumQueuedJobs(
127
+ request, timeout=_DEFAULT_MESSAGE_TIMEOUT_SECS
128
+ )
129
+ except grpc.RpcError as e:
130
+ raise ValueError(e.details()) # type: ignore
131
+
132
+ assert isinstance(response, VaccuumQueuedJobsResult)
133
+ return None
@@ -0,0 +1,142 @@
1
+ import asyncio
2
+
3
+ import grpc.aio as grpc_aio
4
+ import simplejson as json
5
+ from google.protobuf.timestamp_pb2 import Timestamp
6
+ from grpc import StatusCode
7
+
8
+ from pkgs.argument_parser import CachedParser
9
+ from uncountable.core.environment import get_local_admin_server_port
10
+ from uncountable.integration.queue_runner.command_server.protocol.command_server_pb2 import (
11
+ CheckHealthRequest,
12
+ CheckHealthResult,
13
+ EnqueueJobRequest,
14
+ EnqueueJobResult,
15
+ ListQueuedJobsRequest,
16
+ ListQueuedJobsResult,
17
+ RetryJobRequest,
18
+ RetryJobResult,
19
+ VaccuumQueuedJobsRequest,
20
+ VaccuumQueuedJobsResult,
21
+ )
22
+ from uncountable.integration.queue_runner.command_server.types import (
23
+ CommandEnqueueJob,
24
+ CommandEnqueueJobResponse,
25
+ CommandQueue,
26
+ CommandRetryJob,
27
+ CommandRetryJobResponse,
28
+ CommandVaccuumQueuedJobs,
29
+ CommandVaccuumQueuedJobsResponse,
30
+ )
31
+ from uncountable.integration.queue_runner.datastore import DatastoreSqlite
32
+ from uncountable.types import queued_job_t
33
+
34
+ from .constants import ListQueuedJobsConstants
35
+ from .protocol.command_server_pb2_grpc import (
36
+ CommandServerServicer,
37
+ add_CommandServerServicer_to_server,
38
+ )
39
+
40
+ queued_job_payload_parser = CachedParser(queued_job_t.QueuedJobPayload)
41
+
42
+
43
+ async def serve(command_queue: CommandQueue, datastore: DatastoreSqlite) -> None:
44
+ server = grpc_aio.server()
45
+
46
+ class CommandServerHandler(CommandServerServicer):
47
+ async def EnqueueJob(
48
+ self, request: EnqueueJobRequest, context: grpc_aio.ServicerContext
49
+ ) -> EnqueueJobResult:
50
+ payload_json = json.loads(request.serialized_payload)
51
+ payload = queued_job_payload_parser.parse_api(payload_json)
52
+ response_queue: asyncio.Queue[CommandEnqueueJobResponse] = asyncio.Queue()
53
+ await command_queue.put(
54
+ CommandEnqueueJob(
55
+ job_ref_name=request.job_ref_name,
56
+ payload=payload,
57
+ response_queue=response_queue,
58
+ )
59
+ )
60
+ response = await response_queue.get()
61
+ result = EnqueueJobResult(
62
+ successfully_queued=True, queued_job_uuid=response.queued_job_uuid
63
+ )
64
+ return result
65
+
66
+ async def RetryJob(
67
+ self, request: RetryJobRequest, context: grpc_aio.ServicerContext
68
+ ) -> RetryJobResult:
69
+ response_queue: asyncio.Queue[CommandRetryJobResponse] = asyncio.Queue()
70
+ await command_queue.put(
71
+ CommandRetryJob(
72
+ queued_job_uuid=request.uuid, response_queue=response_queue
73
+ )
74
+ )
75
+ response = await response_queue.get()
76
+ if response.queued_job_uuid is not None:
77
+ return RetryJobResult(
78
+ successfully_queued=True, queued_job_uuid=response.queued_job_uuid
79
+ )
80
+ else:
81
+ return RetryJobResult(successfully_queued=False, queued_job_uuid="")
82
+
83
+ async def CheckHealth(
84
+ self, request: CheckHealthRequest, context: grpc_aio.ServicerContext
85
+ ) -> CheckHealthResult:
86
+ return CheckHealthResult(success=True)
87
+
88
+ async def ListQueuedJobs(
89
+ self, request: ListQueuedJobsRequest, context: grpc_aio.ServicerContext
90
+ ) -> ListQueuedJobsResult:
91
+ if (
92
+ request.limit < ListQueuedJobsConstants.LIMIT_MIN
93
+ or request.limit > ListQueuedJobsConstants.LIMIT_MAX
94
+ ):
95
+ await context.abort(
96
+ StatusCode.INVALID_ARGUMENT, "Limit must be between 1 and 100."
97
+ )
98
+
99
+ if request.offset < ListQueuedJobsConstants.OFFSET_MIN:
100
+ await context.abort(
101
+ StatusCode.INVALID_ARGUMENT, "Offset cannot be negative."
102
+ )
103
+
104
+ queued_job_metadata = datastore.list_queued_job_metadata(
105
+ offset=request.offset, limit=request.limit
106
+ )
107
+
108
+ response_list: list[ListQueuedJobsResult.ListQueuedJobsResultItem] = []
109
+ for item in queued_job_metadata:
110
+ proto_timestamp = Timestamp()
111
+ proto_timestamp.FromDatetime(item.submitted_at)
112
+
113
+ response_list.append(
114
+ ListQueuedJobsResult.ListQueuedJobsResultItem(
115
+ uuid=item.queued_job_uuid,
116
+ job_ref_name=item.job_ref_name,
117
+ num_attempts=item.num_attempts,
118
+ submitted_at=proto_timestamp,
119
+ status=item.status,
120
+ )
121
+ )
122
+ return ListQueuedJobsResult(queued_jobs=response_list)
123
+
124
+ async def VaccuumQueuedJobs(
125
+ self, request: VaccuumQueuedJobsRequest, context: grpc_aio.ServicerContext
126
+ ) -> VaccuumQueuedJobsResult:
127
+ response_queue: asyncio.Queue[CommandVaccuumQueuedJobsResponse] = (
128
+ asyncio.Queue()
129
+ )
130
+ await command_queue.put(
131
+ CommandVaccuumQueuedJobs(response_queue=response_queue)
132
+ )
133
+ return VaccuumQueuedJobsResult()
134
+
135
+ add_CommandServerServicer_to_server(CommandServerHandler(), server)
136
+
137
+ listen_addr = f"[::]:{get_local_admin_server_port()}"
138
+
139
+ server.add_insecure_port(listen_addr)
140
+
141
+ await server.start()
142
+ await server.wait_for_termination()
@@ -0,0 +1,4 @@
1
+ class ListQueuedJobsConstants:
2
+ LIMIT_MIN = 1
3
+ LIMIT_MAX = 100
4
+ OFFSET_MIN = 0
@@ -0,0 +1,58 @@
1
+ syntax = "proto3";
2
+ import "google/protobuf/timestamp.proto";
3
+
4
+ service CommandServer {
5
+ rpc EnqueueJob(EnqueueJobRequest) returns (EnqueueJobResult) {}
6
+ rpc RetryJob(RetryJobRequest) returns (RetryJobResult) {}
7
+ rpc CheckHealth(CheckHealthRequest) returns (CheckHealthResult) {}
8
+ rpc ListQueuedJobs(ListQueuedJobsRequest) returns (ListQueuedJobsResult) {}
9
+ rpc VaccuumQueuedJobs(VaccuumQueuedJobsRequest) returns (VaccuumQueuedJobsResult) {}
10
+ }
11
+
12
+ message EnqueueJobRequest {
13
+ string job_ref_name = 1;
14
+ string serialized_payload = 2;
15
+ }
16
+
17
+ message EnqueueJobResult {
18
+ bool successfully_queued = 1;
19
+ string queued_job_uuid = 2;
20
+ }
21
+
22
+ message RetryJobRequest {
23
+ string uuid = 1;
24
+ }
25
+
26
+ message RetryJobResult {
27
+ bool successfully_queued = 1;
28
+ string queued_job_uuid = 2;
29
+ }
30
+
31
+ message VaccuumQueuedJobsRequest {
32
+ }
33
+
34
+ message VaccuumQueuedJobsResult {
35
+ }
36
+
37
+ message CheckHealthRequest {}
38
+
39
+ message CheckHealthResult {
40
+ bool success = 1;
41
+ }
42
+
43
+ message ListQueuedJobsRequest {
44
+ uint32 offset = 1;
45
+ uint32 limit = 2;
46
+ }
47
+
48
+ message ListQueuedJobsResult {
49
+ message ListQueuedJobsResultItem {
50
+ string uuid = 1;
51
+ string job_ref_name = 2;
52
+ int64 num_attempts = 3;
53
+ google.protobuf.Timestamp submitted_at = 4;
54
+ string status = 5;
55
+ }
56
+
57
+ repeated ListQueuedJobsResultItem queued_jobs = 1;
58
+ }