예제 #1
0
파일: tasks.py 프로젝트: takano32/airone
def edit_entry_attrs(self, job_id):
    job = Job.objects.get(id=job_id)

    if job.proceed_if_ready():
        # At the first time, update job status to prevent executing this job duplicately
        job.update(Job.STATUS["PROCESSING"])

        user = User.objects.get(id=job.user.id)
        entry = Entry.objects.get(id=job.target.id)

        recv_data = json.loads(job.params)

        for info in recv_data["attrs"]:
            if info["id"]:
                attr = Attribute.objects.get(id=info["id"])
            else:
                entity_attr = EntityAttr.objects.get(id=info["entity_attr_id"])
                attr = entry.attrs.filter(schema=entity_attr,
                                          is_active=True).first()
                if not attr:
                    attr = entry.add_attribute_from_base(entity_attr, user)

            try:
                converted_value = _convert_data_value(attr, info)
            except ValueError as e:
                Logger.warning("(%s) attr_data: %s" % (e, str(info)))
                continue

            # Check a new update value is specified, or not
            if not attr.is_updated(converted_value):
                continue

            # Add new AttributeValue instance to Attribute instnace
            attr.add_value(user, converted_value)

        if custom_view.is_custom("after_edit_entry", entry.schema.name):
            custom_view.call_custom("after_edit_entry", entry.schema.name,
                                    recv_data, user, entry)

        # update entry information to Elasticsearch
        entry.register_es()

        # clear flag to specify this entry has been completed to edit
        entry.del_status(Entry.STATUS_EDITING)

        # update job status and save it
        job.update(Job.STATUS["DONE"])

        # running job to notify changing entry event
        job_notify_event = Job.new_notify_update_entry(user, entry)
        job_notify_event.run()
예제 #2
0
    def post(self, request, format=None):
        user = User.objects.get(id=request.user.id)
        sel = PostEntrySerializer(data=request.data)

        # This is necessary because request.data might be changed by the processing of serializer
        raw_request_data = deepcopy(request.data)

        if not sel.is_valid():
            ret = {
                'result':
                'Validation Error',
                'details':
                ['(%s) %s' % (k, ','.join(e)) for k, e in sel._errors.items()],
            }
            return Response(ret, status=status.HTTP_400_BAD_REQUEST)

        # checking that target user has permission to create an entry
        if not user.has_permission(sel.validated_data['entity'],
                                   ACLType.Writable):
            return Response(
                {'result': 'Permission denied to create(or update) entry'},
                status=status.HTTP_400_BAD_REQUEST)

        # set target entry information to response data
        resp_data = {
            'updated_attrs': {},  # This describes updated attribute values
            'is_created':
            False,  # This sets true when target entry will be created in this
            # processing
        }

        entry_condition = {
            'schema': sel.validated_data['entity'],
            'name': sel.validated_data['name'],
            'is_active': True,
        }
        if 'id' in sel.validated_data:
            # prevent to register duplicate entry-name with other entry
            if Entry.objects.filter(
                    Q(**entry_condition)
                    & ~Q(id=sel.validated_data['id'])).exists():
                return Response(
                    {
                        'result':
                        '"%s" is duplicate name with other Entry' %
                        entry_condition['name']
                    },
                    status=status.HTTP_400_BAD_REQUEST)

            entry = Entry.objects.get(id=sel.validated_data['id'])
            entry.name = sel.validated_data['name']
            entry.save(update_fields=['name'])
            entry.set_status(Entry.STATUS_EDITING)

            # create job to notify entry event to the registered WebHook
            job_notify = Job.new_notify_update_entry(user, entry)

        elif Entry.objects.filter(**entry_condition).exists():
            entry = Entry.objects.get(**entry_condition)
            entry.set_status(Entry.STATUS_EDITING)

            # create job to notify entry event to the registered WebHook
            job_notify = Job.new_notify_update_entry(user, entry)

        else:
            entry = Entry.objects.create(created_user=user,
                                         status=Entry.STATUS_CREATING,
                                         **entry_condition)
            resp_data['is_created'] = True

            # create job to notify entry event to the registered WebHook
            job_notify = Job.new_notify_create_entry(user, entry)

        entry.complement_attrs(user)
        for name, value in sel.validated_data['attrs'].items():
            # If user doesn't have readable permission for target Attribute, it won't be created.
            if not entry.attrs.filter(name=name).exists():
                continue

            attr = entry.attrs.get(schema__name=name, is_active=True)
            if user.has_permission(
                    attr.schema, ACLType.Writable) and attr.is_updated(value):
                attr.add_value(user, value)

                # This enables to let user know what attributes are changed in this request
                resp_data['updated_attrs'][name] = raw_request_data['attrs'][
                    name]

        # register target Entry to the Elasticsearch
        entry.register_es()

        # run notification job
        job_notify.run()

        entry.del_status(Entry.STATUS_CREATING | Entry.STATUS_EDITING)

        return Response(dict({'result': entry.id}, **resp_data))
예제 #3
0
def revert_attrv(request, recv_data):
    attr = Attribute.objects.filter(id=recv_data["attr_id"]).first()
    if not attr:
        return HttpResponse("Specified Attribute-id is invalid", status=400)

    if not request.user.has_permission(attr, ACLType.Writable):
        return HttpResponse(
            "You don't have permission to update this Attribute", status=400)

    attrv = AttributeValue.objects.filter(id=recv_data["attrv_id"]).first()
    if not attrv or attrv.parent_attr.id != attr.id:
        return HttpResponse("Specified AttributeValue-id is invalid",
                            status=400)

    # When the AttributeType was changed after settting value, this operation is aborted
    if attrv.data_type != attr.schema.type:
        return HttpResponse(
            "Attribute-type was changed after this value was registered.",
            status=400)

    latest_value = attr.get_latest_value()
    if latest_value.get_value() != attrv.get_value():
        # copy specified AttributeValue
        new_attrv = AttributeValue.objects.create(
            **{
                "value": attrv.value,
                "referral": attrv.referral,
                "status": attrv.status,
                "boolean": attrv.boolean,
                "date": attrv.date,
                "data_type": attrv.data_type,
                "created_user": request.user,
                "parent_attr": attr,
                "is_latest": True,
            })

        # This also copies child attribute values and append new one
        new_attrv.data_array.add(*[
            AttributeValue.objects.create(
                **{
                    "value": v.value,
                    "referral": v.referral,
                    "created_user": request.user,
                    "parent_attr": attr,
                    "status": v.status,
                    "boolean": v.boolean,
                    "date": v.date,
                    "data_type": v.data_type,
                    "is_latest": False,
                    "parent_attrv": new_attrv,
                }) for v in attrv.data_array.all()
        ])

        # append cloned value to Attribute
        attr.values.add(new_attrv)

        # clear all exsts latest flag
        attr.unset_latest_flag(exclude_id=new_attrv.id)

        # register update to the Elasticsearch
        attr.parent_entry.register_es()

        # Send notification to the webhook URL
        job_notify = Job.new_notify_update_entry(request.user,
                                                 attr.parent_entry)
        job_notify.run()

        # call custom-view if it exists
        if custom_view.is_custom("revert_attrv",
                                 attr.parent_entry.schema.name):
            return custom_view.call_custom(*[
                "revert_attrv",
                attr.parent_entry.schema.name,
                request,
                request.user,
                attr,
                latest_value,
                new_attrv,
            ])

    return HttpResponse('Succeed in updating Attribute "%s"' %
                        attr.schema.name)
예제 #4
0
파일: tasks.py 프로젝트: hinashi/airone
def _do_import_entries(job):
    user = job.user

    entity = Entity.objects.get(id=job.target.id)
    if not user.has_permission(entity, ACLType.Writable):
        job.update(
            **{
                'status':
                Job.STATUS['ERROR'],
                'text':
                'Permission denied to import. '
                'You need Writable permission for "%s"' % entity.name
            })
        return

    whole_data = json.loads(job.params).get(entity.name)
    if not whole_data:
        job.update(
            **{
                'status': Job.STATUS['ERROR'],
                'text': 'Uploaded file has no entry data of %s' % entity.name
            })
        return

    # get custom_view method to prevent executing check method in every loop processing
    custom_view_handler = None
    if custom_view.is_custom("after_import_entry", entity.name):
        custom_view_handler = 'after_import_entry'

    job.update(Job.STATUS['PROCESSING'])

    total_count = len(whole_data)
    # create or update entry
    for (index, entry_data) in enumerate(whole_data):
        job.text = 'Now importing... (progress: [%5d/%5d])' % (index + 1,
                                                               total_count)
        job.save(update_fields=['text'])

        # abort processing when job is canceled
        if job.is_canceled():
            return

        entry = Entry.objects.filter(name=entry_data['name'],
                                     schema=entity).first()
        if not entry:
            entry = Entry.objects.create(name=entry_data['name'],
                                         schema=entity,
                                         created_user=user)

            # create job to notify create event to the WebHook URL
            job_notify = Job.new_notify_create_entry(user, entry)

        elif not user.has_permission(entry, ACLType.Writable):
            continue

        else:
            # create job to notify edit event to the WebHook URL
            job_notify = Job.new_notify_update_entry(user, entry)

        entry.complement_attrs(user)
        for attr_name, value in entry_data['attrs'].items():
            # If user doesn't have readable permission for target Attribute,
            # it won't be created.
            if not entry.attrs.filter(schema__name=attr_name).exists():
                continue

            # There should be only one EntityAttr that is specified by name and Entity.
            # Once there are multiple EntityAttrs, it must be an abnormal situation.
            # In that case, this aborts import processing for this entry and reports it
            # as an error.
            attr_query = entry.attrs.filter(schema__name=attr_name,
                                            is_active=True,
                                            schema__parent_entity=entry.schema)
            if attr_query.count() > 1:
                Logger.error(
                    '[task.import_entry] Abnormal entry was detected(%s:%d)' %
                    (entry.name, entry.id))
                break

            attr = attr_query.last()
            if (not user.has_permission(attr.schema, ACLType.Writable)
                    or not user.has_permission(attr, ACLType.Writable)):
                continue

            input_value = attr.convert_value_to_register(value)
            if user.has_permission(
                    attr.schema,
                    ACLType.Writable) and attr.is_updated(input_value):
                attr.add_value(user, input_value)

            # call custom-view processing corresponding to import entry
            if custom_view_handler:
                custom_view.call_custom(custom_view_handler, entity.name, user,
                                        entry, attr, value)

        # register entry to the Elasticsearch
        entry.register_es()

        # run notification job
        job_notify.run()

    if not job.is_canceled():
        job.update(status=Job.STATUS['DONE'], text='')
예제 #5
0
    def update(self, entry: Entry, validated_data):
        entry.set_status(Entry.STATUS_EDITING)
        user: User = self.context["request"].user

        entity_name = entry.schema.name
        if custom_view.is_custom("before_update_entry", entity_name):
            custom_view.call_custom("before_update_entry", entity_name, user,
                                    validated_data, entry)

        attrs_data = validated_data.pop("attrs", [])

        # update name of Entry object. If name would be updated, the elasticsearch data of entries
        # that refers this entry also be updated by creating REGISTERED_REFERRALS task.
        job_register_referrals: Optional[Job] = None
        if "name" in validated_data and entry.name != validated_data["name"]:
            entry.name = validated_data["name"]
            entry.save(update_fields=["name"])
            job_register_referrals = Job.new_register_referrals(user, entry)

        for entity_attr in entry.schema.attrs.filter(is_active=True):
            attr: Attribute = entry.attrs.filter(schema=entity_attr,
                                                 is_active=True).first()
            if not attr:
                attr = entry.add_attribute_from_base(entity_attr, user)

            # skip for unpermitted attributes
            if not user.has_permission(attr, ACLType.Writable):
                continue

            # make AttributeValue object if the value is specified
            attr_data = [
                x for x in attrs_data if int(x["id"]) == entity_attr.id
            ]
            if not attr_data:
                continue

            # Check a new update value is specified, or not
            if not attr.is_updated(attr_data[0]["value"]):
                continue

            attr.add_value(user, attr_data[0]["value"])

        if custom_view.is_custom("after_update_entry", entity_name):
            custom_view.call_custom("after_update_entry", entity_name, user,
                                    attrs_data, entry)

        # update entry information to Elasticsearch
        entry.register_es()

        # clear flag to specify this entry has been completed to edit
        entry.del_status(Entry.STATUS_EDITING)

        # running job of re-register referrals because of chaning entry's name
        if job_register_referrals:
            job_register_referrals.run()

        # running job to notify changing entry event
        job_notify_event: Job = Job.new_notify_update_entry(user, entry)
        job_notify_event.run()

        return entry
예제 #6
0
 def test_may_schedule_with_parallelizable_operation(self):
     [job1, job2] = [Job.new_notify_update_entry(self.guest, self.entry) for _ in range(2)]
     self.assertEqual(job2.dependent_job, job1)
     self.assertEqual(job1.status, Job.STATUS["PREPARING"])
     self.assertTrue(job2.proceed_if_ready())
예제 #7
0
    def post(self, request, format=None):
        sel = PostEntrySerializer(data=request.data)

        # This is necessary because request.data might be changed by the processing of serializer
        raw_request_data = deepcopy(request.data)

        if not sel.is_valid():
            ret = {
                "result":
                "Validation Error",
                "details":
                ["(%s) %s" % (k, ",".join(e)) for k, e in sel._errors.items()],
            }
            return Response(ret, status=status.HTTP_400_BAD_REQUEST)

        # checking that target user has permission to create an entry
        if not request.user.has_permission(sel.validated_data["entity"],
                                           ACLType.Writable):
            return Response(
                {"result": "Permission denied to create(or update) entry"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # set target entry information to response data
        resp_data = {
            "updated_attrs": {},  # This describes updated attribute values
            "is_created":
            False,  # This sets true when target entry will be created in this
            # processing
        }

        # This variable indicates whether NOTIFY UPDATE ENTRY Job will be created.
        # This is necessary to create minimum necessary NOTIFY_UPDATE_ENTRY Job.
        will_notify_update_entry = False

        # Common processing to update Entry's name and set will_notify_update_entry variable
        def _update_entry_name(entry):
            # Set Entry status that indicates target Entry is under editing processing
            # to prevent to updating this entry from others.
            entry.set_status(Entry.STATUS_EDITING)

            # Set will_notify_update_entry when name parameter is different with target Entry's name
            _will_notify_update_entry = False
            if entry.name != sel.validated_data["name"]:
                entry.name = sel.validated_data["name"]
                entry.save(update_fields=["name"])
                _will_notify_update_entry = True

            return _will_notify_update_entry

        entry_condition = {
            "schema": sel.validated_data["entity"],
            "name": sel.validated_data["name"],
            "is_active": True,
        }
        if "id" in sel.validated_data:
            # prevent to register duplicate entry-name with other entry
            if Entry.objects.filter(
                    Q(**entry_condition)
                    & ~Q(id=sel.validated_data["id"])).exists():
                return Response(
                    {
                        "result":
                        '"%s" is duplicate name with other Entry' %
                        entry_condition["name"]
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )

            entry = Entry.objects.get(id=sel.validated_data["id"])
            will_notify_update_entry = _update_entry_name(entry)

        elif Entry.objects.filter(**entry_condition).exists():
            entry = Entry.objects.get(**entry_condition)
            will_notify_update_entry = _update_entry_name(entry)

        else:
            entry = Entry.objects.create(created_user=request.user,
                                         status=Entry.STATUS_CREATING,
                                         **entry_condition)
            resp_data["is_created"] = True

            # create job to notify entry event to the registered WebHook
            Job.new_notify_create_entry(request.user, entry).run()

        entry.complement_attrs(request.user)
        for name, value in sel.validated_data["attrs"].items():
            # If user doesn't have readable permission for target Attribute, it won't be created.
            if not entry.attrs.filter(name=name).exists():
                continue

            attr = entry.attrs.get(schema__name=name, is_active=True)
            if request.user.has_permission(
                    attr.schema, ACLType.Writable) and attr.is_updated(value):
                attr.add_value(request.user, value)
                will_notify_update_entry = True

                # This enables to let user know what attributes are changed in this request
                resp_data["updated_attrs"][name] = raw_request_data["attrs"][
                    name]

        if will_notify_update_entry:
            # Create job to notify event, which indicates target entry is updated,
            # to the registered WebHook.
            Job.new_notify_update_entry(request.user, entry).run()

        # register target Entry to the Elasticsearch
        entry.register_es()

        entry.del_status(Entry.STATUS_CREATING | Entry.STATUS_EDITING)

        return Response(dict({"result": entry.id}, **resp_data))