Ejemplo n.º 1
0
class NodeRequestSerializer(JSONAPISerializer):
    class Meta:
        type_ = 'node-requests'

    filterable_fields = frozenset(
        ['creator', 'request_type', 'machine_state', 'created', 'id'])
    id = ser.CharField(source='_id', read_only=True)
    request_type = ser.ChoiceField(read_only=True,
                                   required=False,
                                   choices=RequestTypes.choices())
    machine_state = ser.ChoiceField(read_only=True,
                                    required=False,
                                    choices=DefaultStates.choices())
    comment = ser.CharField(required=False, allow_blank=True, max_length=65535)
    created = VersionedDateTimeField(read_only=True)
    modified = VersionedDateTimeField(read_only=True)
    date_last_transitioned = VersionedDateTimeField(read_only=True)

    target = RelationshipField(
        read_only=True,
        related_view='nodes:node-detail',
        related_view_kwargs={'node_id': '<target._id>'},
        filter_key='target___id',
    )

    creator = RelationshipField(
        read_only=True,
        related_view='users:user-detail',
        related_view_kwargs={'user_id': '<creator._id>'},
        filter_key='creator___id',
    )

    links = LinksField({
        'self': 'get_absolute_url',
        'target': 'get_target_url'
    })

    def get_absolute_url(self, obj):
        return absolute_reverse(
            'requests:node-request-detail',
            kwargs={
                'request_id':
                obj._id,
                'version':
                self.context['request'].parser_context['kwargs']['version']
            })

    def get_target_url(self, obj):
        return absolute_reverse(
            'nodes:node-detail',
            kwargs={
                'node_id':
                obj.target._id,
                'version':
                self.context['request'].parser_context['kwargs']['version']
            })

    def create(self, validated_data):
        raise NotImplementedError()
Ejemplo n.º 2
0
class MachineableMixin(models.Model):
    class Meta:
        abstract = True

    # NOTE: machine_state should rarely/never be modified directly -- use the state transition methods below
    machine_state = models.CharField(max_length=15, db_index=True, choices=DefaultStates.choices(), default=DefaultStates.INITIAL.value)

    date_last_transitioned = models.DateTimeField(null=True, blank=True, db_index=True)

    @property
    def MachineClass(self):
        raise NotImplementedError()

    def run_submit(self, user):
        """Run the 'submit' state transition and create a corresponding Action.

        Params:
            user: The user triggering this transition.
        """
        return self.__run_transition(DefaultTriggers.SUBMIT.value, user=user)

    def run_accept(self, user, comment):
        """Run the 'accept' state transition and create a corresponding Action.

        Params:
            user: The user triggering this transition.
            comment: Text describing why.
        """
        return self.__run_transition(DefaultTriggers.ACCEPT.value, user=user, comment=comment)

    def run_reject(self, user, comment):
        """Run the 'reject' state transition and create a corresponding Action.

        Params:
            user: The user triggering this transition.
            comment: Text describing why.
        """
        return self.__run_transition(DefaultTriggers.REJECT.value, user=user, comment=comment)

    def run_edit_comment(self, user, comment):
        """Run the 'edit_comment' state transition and create a corresponding Action.

        Params:
            user: The user triggering this transition.
            comment: New comment text.
        """
        return self.__run_transition(DefaultTriggers.EDIT_COMMENT.value, user=user, comment=comment)

    def __run_transition(self, trigger, **kwargs):
        machine = self.MachineClass(self, 'machine_state')
        trigger_fn = getattr(machine, trigger)
        with transaction.atomic():
            result = trigger_fn(**kwargs)
            action = machine.action
            if not result or action is None:
                valid_triggers = machine.get_triggers(self.machine_state)
                raise InvalidTriggerError(trigger, self.machine_state, valid_triggers)
            return action
Ejemplo n.º 3
0
class BaseAction(ObjectIDMixin, BaseModel):
    class Meta:
        abstract = True

    objects = IncludeManager()

    creator = models.ForeignKey('OSFUser', related_name='+', on_delete=models.CASCADE)

    trigger = models.CharField(max_length=31, choices=DefaultTriggers.choices())
    from_state = models.CharField(max_length=31, choices=DefaultStates.choices())
    to_state = models.CharField(max_length=31, choices=DefaultStates.choices())

    comment = models.TextField(blank=True)

    is_deleted = models.BooleanField(default=False)

    @property
    def target(self):
        raise NotImplementedError()
Ejemplo n.º 4
0
class BaseActionSerializer(JSONAPISerializer):
    filterable_fields = frozenset([
        'id',
        'trigger',
        'from_state',
        'to_state',
        'date_created',
        'date_modified',
        'target',
    ])

    id = ser.CharField(source='_id', read_only=True)

    trigger = ser.ChoiceField(choices=DefaultTriggers.choices())

    comment = ser.CharField(max_length=65535, required=False)

    from_state = ser.ChoiceField(choices=DefaultStates.choices(),
                                 read_only=True)
    to_state = ser.ChoiceField(choices=DefaultStates.choices(), read_only=True)

    date_created = ser.DateTimeField(source='created', read_only=True)
    date_modified = ser.DateTimeField(source='modified', read_only=True)
    auto = ser.BooleanField(read_only=True)

    creator = RelationshipField(
        read_only=True,
        related_view='users:user-detail',
        related_view_kwargs={'user_id': '<creator._id>'},
        filter_key='creator__guids___id',
        always_embed=True,
    )

    links = LinksField({
        'self': 'get_action_url',
    })

    def get_absolute_url(self, obj):
        return self.get_action_url(obj)

    def get_action_url(self, obj):
        return utils.absolute_reverse(
            'actions:action-detail',
            kwargs={
                'action_id':
                obj._id,
                'version':
                self.context['request'].parser_context['kwargs']['version']
            })

    def create(self, validated_data):
        trigger = validated_data.pop('trigger')
        user = validated_data.pop('user')
        target = validated_data.pop('target')
        comment = validated_data.pop('comment', '')
        permissions = validated_data.pop('permissions', '')
        visible = validated_data.pop('visible', '')
        try:
            if trigger == DefaultTriggers.ACCEPT.value:
                return target.run_accept(user=user,
                                         comment=comment,
                                         permissions=permissions,
                                         visible=visible)
            if trigger == DefaultTriggers.REJECT.value:
                return target.run_reject(user, comment)
            if trigger == DefaultTriggers.EDIT_COMMENT.value:
                return target.run_edit_comment(user, comment)
            if trigger == DefaultTriggers.SUBMIT.value:
                return target.run_submit(user)
        except InvalidTriggerError as e:
            # Invalid transition from the current state
            raise Conflict(e.message)
        else:
            raise JSONAPIAttributeException(attribute='trigger',
                                            detail='Invalid trigger.')

    class Meta:
        type_ = 'actions'
        abstract = True
Ejemplo n.º 5
0
class BaseActionSerializer(JSONAPISerializer):
    filterable_fields = frozenset([
        'id',
        'trigger',
        'from_state',
        'to_state',
        'date_created',
        'date_modified',
        'target',
    ])

    id = ser.CharField(source='_id', read_only=True)

    trigger = ser.ChoiceField(choices=DefaultTriggers.choices())

    comment = HideIfProviderCommentsPrivate(ser.CharField(max_length=65535, required=False))

    from_state = ser.ChoiceField(choices=DefaultStates.choices(), read_only=True)
    to_state = ser.ChoiceField(choices=DefaultStates.choices(), read_only=True)

    date_created = ser.DateTimeField(source='created', read_only=True)
    date_modified = ser.DateTimeField(source='modified', read_only=True)

    creator = RelationshipField(
        read_only=True,
        related_view='users:user-detail',
        related_view_kwargs={'user_id': '<creator._id>'},
        filter_key='creator__guids___id',
        always_embed=True,
    )

    links = LinksField(
        {
            'self': 'get_action_url',
        }
    )

    @property
    def get_action_url(self):
        raise NotImplementedError()

    def get_absolute_url(self, obj):
        return self.get_action_url(obj)

    def create(self, validated_data):
        trigger = validated_data.pop('trigger')
        user = validated_data.pop('user')
        target = validated_data.pop('target')
        comment = validated_data.pop('comment', '')
        try:
            if trigger == DefaultTriggers.ACCEPT.value:
                return target.run_accept(user, comment)
            if trigger == DefaultTriggers.REJECT.value:
                return target.run_reject(user, comment)
            if trigger == DefaultTriggers.EDIT_COMMENT.value:
                return target.run_edit_comment(user, comment)
            if trigger == DefaultTriggers.SUBMIT.value:
                return target.run_submit(user)
        except InvalidTriggerError as e:
            # Invalid transition from the current state
            raise Conflict(e.message)
        else:
            raise JSONAPIAttributeException(attribute='trigger', detail='Invalid trigger.')

    class Meta:
        type_ = 'actions'
        abstract = True