def _load_tab_flows(workflow: Workflow, delta_id: int) -> List[TabFlow]: """ Query `workflow` for each tab's `TabFlow` (ordered by tab position). Raise `ModuleError` or `ValueError` if migrate_params() fails. Failed migration means the whole execute can't happen. """ ret = [] with workflow.cooperative_lock(): # reloads workflow if workflow.last_delta_id != delta_id: raise UnneededExecution for tab_model in workflow.live_tabs.all(): steps = [ ExecuteStep( step, (step.module_version.param_schema if step.module_version is not None else ParamDType.Dict({})), # We need to invoke the kernel and migrate _all_ modules' # params (WfModule.get_params), because we can only check # for tab cycles after migrating (and before calling any # render()). _get_migrated_params(step), ) for step in tab_model.live_wf_modules.all() ] ret.append(TabFlow(Tab(tab_model.slug, tab_model.name), steps)) return ret
def _wf_module_delete_secret_and_build_delta( workflow: Workflow, wf_module: WfModule, param: str ) -> Optional[Dict[str, Any]]: """ Write a new secret (or `None`) to `wf_module`, or raise. Return a "delta" for websockets.ws_client_send_delta_async(), or `None` if the database has not been modified. Raise Workflow.DoesNotExist if the Workflow was deleted. """ with workflow.cooperative_lock(): # raises Workflow.DoesNotExist try: wf_module.refresh_from_db() except WfModule.DoesNotExist: return None # no-op if wf_module.secrets.get(param) is None: return None # no-op wf_module.secrets = dict(wf_module.secrets) # shallow copy del wf_module.secrets[param] wf_module.save(update_fields=["secrets"]) return { "updateWfModules": { str(wf_module.id): {"secrets": wf_module.secret_metadata} } }
def _wf_module_delete_secret_and_build_delta( workflow: Workflow, wf_module: WfModule, param: str) -> Optional[clientside.Update]: """ Write a new secret (or `None`) to `wf_module`, or raise. Return a `clientside.Update`, or `None` if the database is not modified. Raise Workflow.DoesNotExist if the Workflow was deleted. """ with workflow.cooperative_lock(): # raises Workflow.DoesNotExist try: wf_module.refresh_from_db() except WfModule.DoesNotExist: return None # no-op if wf_module.secrets.get(param) is None: return None # no-op wf_module.secrets = dict(wf_module.secrets) # shallow copy del wf_module.secrets[param] wf_module.save(update_fields=["secrets"]) return clientside.Update(steps={ wf_module.id: clientside.StepUpdate(secrets=wf_module.secret_metadata) })
def _do_finish_upload( workflow: Workflow, wf_module: WfModule, uuid: uuidgen.UUID, filename: str ) -> clientside.Update: with workflow.cooperative_lock(): wf_module.refresh_from_db() try: in_progress_upload = wf_module.in_progress_uploads.get( id=uuid, is_completed=False ) except InProgressUpload.DoesNotExist: raise HandlerError( "BadRequest: key is not being uploaded for this WfModule right now. " "(Even a valid key becomes invalid after you create, finish or abort " "an upload on its WfModule.)" ) try: in_progress_upload.convert_to_uploaded_file(filename) except FileNotFoundError: raise HandlerError( "BadRequest: file not found. " "You must upload the file before calling finish_upload." ) return clientside.Update( steps={ wf_module.id: clientside.StepUpdate( files=wf_module.to_clientside().files ) } )
def _write_tab_position(workflow: Workflow, tab_slug: str) -> None: """Write position in DB, or raise (Workflow|Tab).DoesNotExist.""" with workflow.cooperative_lock(): # raises Workflow.DoesNotExist # raises Tab.DoesNotExist, e.g. if tab.is_deleted tab = workflow.live_tabs.get(slug=tab_slug) workflow.selected_tab_position = tab.position workflow.save(update_fields=["selected_tab_position"])
def make_init_state(request, workflow: Workflow, modules: Dict[str, ModuleZipfile]) -> Dict[str, Any]: """Build a dict to embed as JSON in `window.initState` in HTML. Raise Http404 if the workflow disappeared. Side-effect: update workflow.last_viewed_at. """ try: with workflow.cooperative_lock(): # raise DoesNotExist on race if request.user.is_anonymous: user = None else: lock_user_by_id(request.user.id, for_write=False) user = query_clientside_user(request.user.id) workflow.last_viewed_at = datetime.datetime.now() workflow.save(update_fields=["last_viewed_at"]) state = clientside.Init( user=user, workflow=workflow.to_clientside(), tabs={ tab.slug: tab.to_clientside() for tab in workflow.live_tabs }, steps={ step.id: step.to_clientside( force_module_zipfile=modules.get(step.module_id_name)) for step in Step.live_in_workflow( workflow).prefetch_related("tab") }, modules={ module_id: clientside.Module( spec=module.get_spec(), js_module=module.get_optional_js_module(), ) for module_id, module in modules.items() }, blocks={ block.slug: block.to_clientside() for block in workflow.blocks.all() }, settings={ "bigTableRowsPerTile": settings.BIG_TABLE_ROWS_PER_TILE, "bigTableColumnsPerTile": settings.BIG_TABLE_COLUMNS_PER_TILE, }, ) except Workflow.DoesNotExist: raise Http404("Workflow was recently deleted") ctx = JsonizeContext(request.locale_id, modules) return jsonize_clientside_init(state, ctx)
def _do_abort_upload(workflow: Workflow, wf_module: WfModule, uuid: uuidgen.UUID) -> None: with workflow.cooperative_lock(): try: in_progress_upload = wf_module.in_progress_uploads.get(id=uuid) except InProgressUpload.DoesNotExist: return # no-op in_progress_upload.delete_s3_data() # Aborted upload should disappear, as far as the user is concerned in_progress_upload.is_completed = True in_progress_upload.save(update_fields=["is_completed"])
def _write_step_position(workflow: Workflow, step_id: int) -> None: """Write position in DB, or raise (Workflow|Tab|Step).DoesNotExist.""" with workflow.cooperative_lock(): # raises Workflow.DoesNotExist # Raises Step.DoesNotExist, e.g. if tab.is_deleted step = Step.live_in_workflow(workflow).get(pk=step_id) tab = step.tab tab.selected_step_position = step.order tab.save(update_fields=["selected_step_position"]) workflow.selected_tab_position = tab.position workflow.save(update_fields=["selected_tab_position"])
def _write_wf_module_position(workflow: Workflow, wf_module_id: int) -> None: """Write position in DB, or raise (Workflow|Tab|WfModule).DoesNotExist.""" with workflow.cooperative_lock(): # raises Workflow.DoesNotExist # Raises WfModule.DoesNotExist, e.g. if tab.is_deleted wf_module = WfModule.live_in_workflow(workflow).get(pk=wf_module_id) tab = wf_module.tab tab.selected_wf_module_position = wf_module.order tab.save(update_fields=["selected_wf_module_position"]) workflow.selected_tab_position = tab.position workflow.save(update_fields=["selected_tab_position"])
def _wf_module_set_secret_and_build_delta( workflow: Workflow, wf_module: WfModule, param: str, secret: str) -> Optional[clientside.Update]: """ Write a new secret to `wf_module`, or raise. Return a `clientside.Update`, or `None` if the database is not modified. Raise Workflow.DoesNotExist if the Workflow was deleted. """ with workflow.cooperative_lock(): # raises Workflow.DoesNotExist try: wf_module.refresh_from_db() except WfModule.DoesNotExist: return None # no-op if wf_module.secrets.get(param, {}).get("secret") == secret: return None # no-op try: module_zipfile = MODULE_REGISTRY.latest(wf_module.module_id_name) except KeyError: raise HandlerError( f"BadRequest: ModuleZipfile {wf_module.module_id_name} does not exist" ) module_spec = module_zipfile.get_spec() if not any(p.type == "secret" and p.secret_logic.provider == "string" for p in module_spec.param_fields): raise HandlerError( f"BadRequest: param is not a secret string parameter") created_at = timezone.now() created_at_str = ( created_at.strftime("%Y-%m-%dT%H:%M:%S") + "." + created_at.strftime("%f")[0:3] # milliseconds + "Z") wf_module.secrets = { **wf_module.secrets, param: { "name": created_at_str, "secret": secret }, } wf_module.save(update_fields=["secrets"]) return clientside.Update(steps={ wf_module.id: clientside.StepUpdate(secrets=wf_module.secret_metadata) })
def _wf_module_set_secret_and_build_delta( workflow: Workflow, wf_module: WfModule, param: str, secret: str ) -> Optional[Dict[str, Any]]: """ Write a new secret to `wf_module`, or raise. Return a "delta" for websockets.ws_client_send_delta_async(), or `None` if the database is not modified. Raise Workflow.DoesNotExist if the Workflow was deleted. """ with workflow.cooperative_lock(): # raises Workflow.DoesNotExist try: wf_module.refresh_from_db() except WfModule.DoesNotExist: return None # no-op if wf_module.secrets.get(param, {}).get("secret") == secret: return None # no-op module_version = wf_module.module_version if module_version is None: raise HandlerError(f"BadRequest: ModuleVersion does not exist") if not any( p.type == "secret" and p.secret_logic.provider == "string" for p in module_version.param_fields ): raise HandlerError(f"BadRequest: param is not a secret string parameter") created_at = timezone.now() created_at_str = ( created_at.strftime("%Y-%m-%dT%H:%M:%S") + "." + created_at.strftime("%f")[0:3] # milliseconds + "Z" ) wf_module.secrets = { **wf_module.secrets, param: {"name": created_at_str, "secret": secret}, } wf_module.save(update_fields=["secrets"]) return { "updateWfModules": { str(wf_module.id): {"secrets": wf_module.secret_metadata} } }
def _load_tab_flows(workflow: Workflow, delta_id: int) -> List[TabFlow]: """Query `workflow` for each tab's `TabFlow` (ordered by tab position). Raise `ModuleError` or `ValueError` if migrate_params() fails. Failed migration means the whole execute can't happen. """ ret = [] with workflow.cooperative_lock(): # reloads workflow if workflow.last_delta_id != delta_id: raise UnneededExecution module_zipfiles = MODULE_REGISTRY.all_latest() for tab_model in workflow.live_tabs.all(): steps = [ _build_execute_step(step, module_zipfiles=module_zipfiles) for step in tab_model.live_steps.all() ] ret.append(TabFlow(Tab(tab_model.slug, tab_model.name), steps)) return ret
def make_init_state(request, workflow: Workflow, modules: Dict[str, ModuleZipfile]) -> Dict[str, Any]: """ Build a dict to embed as JSON in `window.initState` in HTML. Raise Http404 if the workflow disappeared. Side-effect: update workflow.last_viewed_at. """ try: with workflow.cooperative_lock(): # raise DoesNotExist on race workflow.last_viewed_at = timezone.now() workflow.save(update_fields=["last_viewed_at"]) state = clientside.Init( workflow=workflow.to_clientside(), tabs={ tab.slug: tab.to_clientside() for tab in workflow.live_tabs }, steps={ step.id: step.to_clientside() for step in WfModule.live_in_workflow(workflow) }, modules={ module_id: clientside.Module( spec=module.get_spec(), js_module=module.get_optional_js_module(), ) for module_id, module in modules.items() }, ) except Workflow.DoesNotExist: raise Http404("Workflow was recently deleted") ctx = JsonizeContext(request.user, request.session, request.locale_id, modules) return jsonize_clientside_init(state, ctx)
def _do_create_upload(workflow: Workflow, wf_module: WfModule) -> Dict[str, Any]: with workflow.cooperative_lock(): wf_module.refresh_from_db() in_progress_upload = wf_module.in_progress_uploads.create() return in_progress_upload.generate_upload_parameters()