Beispiel #1
0
 def _objects_are_valid(self):
     try:
         _check_instances_for_transition(instances=self.objects,
                                         transition=self.transition,
                                         requester=self.request.user)
     except TransitionNotAllowedError as e:
         return False, e
     return True, None
Beispiel #2
0
 def test_return_report_when_requester_is_not_assets_owner(self):
     _, transition, _ = self._create_transition(
         model=self.bo_asset,
         name='test',
         source=[BackOfficeAssetStatus.new.id],
         target=BackOfficeAssetStatus.used.id,
         actions=['must_be_owner_of_asset'])
     with self.assertRaises(TransitionNotAllowedError):
         _check_instances_for_transition(instances=[self.bo_asset],
                                         transition=transition,
                                         requester=self.user_pl)
Beispiel #3
0
 def _check_instances(self):
     try:
         _check_instances_for_transition(
             instances=self.objects,
             transition=self.transition,
             requester=self.request.user,
         )
     except TransitionNotAllowedError as e:
         raise DRFValidationError({
             api_settings.NON_FIELD_ERRORS_KEY:
             list(itertools.chain(*e.errors.values()))
         })
Beispiel #4
0
def _perform_async_transition(transition_job):
    transition = transition_job.transition
    obj = transition_job.obj
    requester = transition_job.user
    _check_instances_for_transition(
        instances=[obj],
        transition=transition,
        check_async_job=False,
        requester=requester
    )
    _check_action_with_instances([obj], transition)
    # check if this job isn't already finished
    if not transition_job.is_running:
        logger.warning(
            'Running previously ended transition job: %s', transition_job
        )
        return
    # make sure that none of previous actions has failed
    executed_actions = list(transition_job.transition_job_actions.all())
    try:
        _check_previous_actions(transition_job, executed_actions)
    except AsyncTransitionError:
        return

    completed_actions_names = set([
        tja.action_name for tja in executed_actions
        if tja.status != TransitionJobActionStatus.STARTED
    ])
    attachments = []
    # TODO: move this to transition (sth like
    # `for action in transition.get_actions(obj)`)
    for action in _order_actions_by_requirements(transition.actions.all(), obj):
        transition_job.refresh_from_db()
        if transition_job.is_killed:
            logger.info('Transition job: {} is killed'.format(transition_job))
            return

        if action.name in completed_actions_names:
            logger.debug('Action {} already performed - skipping'.format(
                action.name
            ))
            continue
        logger.info('Performing action {} in transition {} (job: {})'.format(
            action, transition, transition_job
        ))
        func = getattr(obj, action.name)
        # TODO: disable save object ?
        # data should be in transition_job.params dict
        defaults = _prepare_action_data(
            action=action,
            **transition_job.params
        )
        tja = TransitionJobAction.objects.get_or_create(
            transition_job=transition_job,
            action_name=action.name,
            defaults=dict(
                status=TransitionJobActionStatus.STARTED,
            )
        )[0]
        freeze = False
        try:
            # we shouldn't run whole transition atomically since it could be
            # spreaded to multiple processes (multiple tasks) - run single
            # action in transaction instead
            with transaction.atomic():
                try:
                    result = func(
                        instances=[obj],
                        requester=requester,
                        tja=tja,
                        **defaults
                    )
                except RescheduleAsyncTransitionActionLater as e:
                    # action is not ready - reschedule this job later and
                    # continue when you left off
                    transition_job.reschedule()
                    return
                except FreezeAsyncTransition:
                    # if action raise this exception, we're assumming that it's
                    # finished
                    freeze = True
                else:
                    if isinstance(result, Attachment):
                        attachments.append(result)
        except Exception as e:
            logger.exception(e)
            tja.status = TransitionJobActionStatus.FAILED
            raise FailedActionError('Action {} has failed'.format(action.name)) from e  # noqa
        else:
            tja.status = TransitionJobActionStatus.FINISHED
        finally:
            tja.save()
        completed_actions_names.add(action.name)
        # freeze whole transition if demanded by some action
        if freeze:
            transition_job.freeze()
            return

    # save obj and history
    _post_transition_instance_processing(
        obj, transition, transition_job.params['data'],
        history_kwargs=transition_job.params['history_kwargs'],
        requester=requester, attachments=attachments,
    )
    transition_job.success()