예제 #1
0
    def iterate(self, environ, start_response):
        stats = ""
        with StatsTimer() as poll_timer:
            try:
                content_length = int(environ.get('CONTENT_LENGTH', '0'))
                request = environ["wsgi.input"].read(content_length).decode()
                headers = self._get_headers(environ)
            except KeyError:
                log("Error in request data", level="ERROR")
                raise Exception("Error in request data")

        stats += "Polling time: {} msecs\n".format(poll_timer.msecs)

        message = SmartAppFromMessage(request,
                                      headers=headers,
                                      headers_required=False)
        if not message.validate():
            start_response("400 BAD REQUEST",
                           self._get_outgoing_headers(headers))
            return [b'{"message": "invalid message"}']

        answer, stats = self.handle_message(message, stats)
        with StatsTimer() as publish_timer:
            if not answer:
                start_response("204 NO CONTENT",
                               self._get_outgoing_headers(headers))
                return [b'{"message": "no answer"}']
            start_response("200 OK",
                           self._get_outgoing_headers(headers, answer))
            answer = SmartAppToMessage(answer, message, request=None)

        stats += "Publishing time: {} msecs".format(publish_timer.msecs)
        log(stats, params={log_const.KEY_NAME: "timings"})
        return [answer.value.encode()]
 def _get_timeout_from_message(self, orig_message_raw, callback_id,
                               headers):
     timeout_from_message = SmartAppFromMessage(
         orig_message_raw,
         headers=headers,
         masking_fields=self.masking_fields)
     timeout_from_message.callback_id = callback_id
     return timeout_from_message
    def test_valid_false(self):
        input_msg = {
            "uuid": {"userChannel": "web", "userId": 99, "chatId": 80},
            "payload": "some payload"
        }
        headers = [('test_header', 'result')]
        json_input_msg = json.dumps(input_msg, ensure_ascii=False)

        message = SmartAppFromMessage(json_input_msg, headers=headers)
        self.assertFalse(message.validate())
 def _get_timeout_from_message(self, orig_message_raw, callback_id,
                               headers):
     orig_message_raw = json.dumps(orig_message_raw)
     timeout_from_message = SmartAppFromMessage(
         orig_message_raw,
         headers=headers,
         masking_fields=self.masking_fields,
         validators=self.from_msg_validators)
     timeout_from_message.callback_id = callback_id
     return timeout_from_message
 def test_valid_true(self):
     input_msg = {
         "messageId": 2,
         "sessionId": 234,
         "uuid": {"userChannel": "web", "userId": 99, "chatId": 80},
         "payload": {"key": "some payload"},
         "messageName": "some_type"
     }
     json_input_msg = json.dumps(input_msg, ensure_ascii=False)
     headers = [('test_header', 'result')]
     message = SmartAppFromMessage(json_input_msg, headers=headers)
     self.assertTrue(message.validate())
    def handle_message(
        self, message: SmartAppFromMessage
    ) -> typing.Tuple[int, str, SmartAppToMessage]:
        if not message.validate():
            result = 400, "BAD REQUEST", SmartAppToMessage(
                self.BAD_REQUEST_COMMAND,
                message=message,
                request=None,
            )
            try:
                result[2].as_dict
            except (json.JSONDecodeError, KeyError):
                result = 400, "BAD REQUEST", SmartAppToMessage(
                    self.BAD_REQUEST_COMMAND,
                    message=basic_error_message,
                    request=None,
                )
            finally:
                return result

        answer, stats = self.process_message(message)
        if not answer:
            return 204, "NO CONTENT", SmartAppToMessage(self.NO_ANSWER_COMMAND,
                                                        message=message,
                                                        request=None)

        answer_message = SmartAppToMessage(answer,
                                           message,
                                           request=None,
                                           validators=self.to_msg_validators)
        if answer_message.validate():
            return 200, "OK", answer_message
        else:
            return 500, "BAD ANSWER", SmartAppToMessage(
                self.BAD_ANSWER_COMMAND, message=message, request=None)
예제 #7
0
 def process_message(self, raw_message: str, headers: tuple = ()) -> typing.Tuple[typing.Any, list]:
     masking_fields = self.settings["template_settings"].get("masking_fields")
     message = SmartAppFromMessage(raw_message, headers=headers, masking_fields=masking_fields)
     user = self.__user_cls(self.environment.user_id, message, self.user_data, self.settings,
                            self.app_model.scenario_descriptions,
                            self.__parametrizer_cls, load_error=False)
     answers = self.app_model.answer(message, user)
     return user, answers or []
예제 #8
0
 def process_message(self, raw_message: str, headers: tuple = ()) -> typing.Tuple[typing.Any, list]:
     from smart_kit.configs import get_app_config
     masking_fields = self.settings["template_settings"].get("masking_fields")
     message = SmartAppFromMessage(raw_message, headers=headers, masking_fields=masking_fields,
                                   validators=get_app_config().FROM_MSG_VALIDATORS)
     user = self.__user_cls(self.environment.user_id, message, self.user_data, self.settings,
                            self.app_model.scenario_descriptions,
                            self.__parametrizer_cls, load_error=False)
     answers = self.app_model.answer(message, user)
     return user, answers or []
    def process_message(self, mq_message, consumer, kafka_key, stats):
        # ну тут чутка копипасты
        mutex = None
        try:
            message_value = mq_message.value()
            message = SmartAppFromMessage(message_value,
                                          headers=mq_message.headers(),
                                          masking_fields=self.masking_fields)
            if message.validate():
                mutex = self._locks.setdefault(message.db_uid,
                                               threading.Lock())
                mutex.acquire()

            super().process_message(mq_message, consumer, kafka_key, stats)
        except Exception:
            if mutex and mutex.locked():
                mutex.release()
            raise
        if mutex and mutex.locked():
            mutex.release()
    async def iterate(self, request: aiohttp.web.Request):
        headers = self._get_headers(request.headers)
        body = await request.text()
        message = SmartAppFromMessage(body, headers=headers, headers_required=False,
                                      validators=self.from_msg_validators)

        status, reason, answer = await self.handle_message(message)

        return aiohttp.web.json_response(
            status=status, reason=reason, data=answer.as_dict,
            headers=self._get_outgoing_headers(headers, answer.command)
        )
예제 #11
0
    def test_validation(self):
        input_msg = {
            "uuid": {
                "userChannel": "web",
                "userId": 99,
                "chatId": 80
            },
            "messageName": "some_name",
            "messageId": "random_id",
            "sessionId": "random_id",
            "payload": {
                "pi": 3.14159265358979,
            }
        }
        headers = [('test_header', 'result')]

        message = SmartAppFromMessage(json.dumps(input_msg,
                                                 ensure_ascii=False),
                                      headers=headers,
                                      validators=(PieMessageValidator(), ))
        self.assertTrue(message.validate())

        input_msg["payload"]["pi"] = 2.7182818284
        message = SmartAppFromMessage(json.dumps(input_msg,
                                                 ensure_ascii=False),
                                      headers=headers,
                                      validators=(PieMessageValidator(), ))
        self.assertFalse(message.validate())
예제 #12
0
    def handle_message(
        self, message: SmartAppFromMessage
    ) -> typing.Tuple[int, str, SmartAppToMessage]:
        if not message.validate():
            return 400, "BAD REQUEST", SmartAppToMessage(
                self.BAD_REQUEST_COMMAND, message=message, request=None)

        answer, stats = self.process_message(message)
        if not answer:
            return 204, "NO CONTENT", SmartAppToMessage(self.NO_ANSWER_COMMAND,
                                                        message=message,
                                                        request=None)

        return 200, "OK", SmartAppToMessage(answer, message, request=None)
    def test_1(self):
        input_msg = {
            "messageId": 2,
            "uuid": {"userChannel": "B2C", "userId": "userId", "sub": "sub"},
            "payload": {
                "message": {
                    "original_text": "сверни приложение"
                },
                "device": {
                    "platformType": "android",
                    "platformVersion": "9",
                    "surface": "STARGATE",
                    "surfaceVersion": "1.56.20200828144304",
                    "features": {"appTypes": ["APK", "WEB_APP", "DIALOG"]},
                    "capabilities": {"mic": {"available": True},
                                     "screen": {"available": True},
                                     "speak": {"available": True}},
                    "deviceId": "34534545345345",
                    "deviceManufacturer": "SberDevices",
                    "deviceModel": "stargate",
                    "additionalInfo": {}
                }
            },
            "messageName": "MESSAGE_TO_SKILL"
        }
        json_input_msg = json.dumps(input_msg, ensure_ascii=False)
        topic = "test"
        headers = []
        current_time = current_time_ms()
        message = SmartAppFromMessage(value=json_input_msg, topic_key=topic, headers=headers, creation_time=current_time)

        self.assertAlmostEqual(message.creation_time, current_time)
        self.assertEqual(2, message.incremental_id)
        self.assertEqual(input_msg["uuid"]["userChannel"], message.channel)
        self.assertEqual(input_msg["messageName"], message.type)
        self.assertEqual(input_msg["uuid"]["userId"], message.uid)
        self.assertEqual(json_input_msg, message.value)
        self.assertEqual("sub_userId_B2C", message.db_uid)
        self.assertDictEqual(input_msg["uuid"], message.uuid)
        self.assertDictEqual(input_msg["payload"], message.payload)
        device = input_msg["payload"]["device"]
        self.assertEqual(device["platformType"], message.device.platform_type)
        self.assertEqual(device["platformVersion"], message.device.platform_version)
        self.assertEqual(device["surface"], message.device.surface)
        self.assertEqual(device["surfaceVersion"], message.device.surface_version)
        self.assertEqual(device["features"], message.device.features)
        self.assertEqual(device["capabilities"], message.device.capabilities)
        self.assertEqual(device["additionalInfo"], message.device.additional_info)
        self.assertEqual(topic, message.topic_key)
    async def handle_message(self, message: SmartAppFromMessage) -> typing.Tuple[int, str, SmartAppToMessage]:
        if not message.validate():
            return 400, "BAD REQUEST", SmartAppToMessage(self.BAD_REQUEST_COMMAND, message=message, request=None)

        answer, stats = await self.process_message(message)
        if not answer:
            return 204, "NO CONTENT", SmartAppToMessage(self.NO_ANSWER_COMMAND, message=message, request=None)

        answer_message = SmartAppToMessage(
            answer, message, request=None,
            validators=self.to_msg_validators)
        if answer_message.validate():
            return 200, "OK", answer_message
        else:
            return 500, "BAD ANSWER", SmartAppToMessage(self.BAD_ANSWER_COMMAND, message=message, request=None)
예제 #15
0
    def iterate(self, environ, start_response):
        try:
            content_length = int(environ.get('CONTENT_LENGTH', '0'))
            body = environ["wsgi.input"].read(content_length).decode()
            headers = self._get_headers(environ)
        except KeyError:
            log("Error in request data", level="ERROR")
            raise Exception("Error in request data")

        message = SmartAppFromMessage(body,
                                      headers=headers,
                                      headers_required=False)

        status, reason, answer = self.handle_message(message)

        start_response(f"{status} {reason}",
                       self._get_outgoing_headers(headers, answer.command))
        return [answer.value.encode()]
예제 #16
0
    def create_message(self, data, headers=None):
        defaults = Environment().as_dict

        predefined_fields = data.get("predefined_fields")
        is_payload_field = data.get("payload")
        if predefined_fields:
            predefined_fields_data = self.storaged_predefined_fields[
                predefined_fields]
            if not is_payload_field and not predefined_fields_data.get(
                    "payload"):
                predefined_fields_data = {"payload": predefined_fields_data}
            if is_payload_field and predefined_fields_data.get("payload"):
                raise Exception(
                    "Payload field is in test case and in predefined_fields object, check it!"
                )
            defaults.update(predefined_fields_data)
            del data["predefined_fields"]

        message = data.get("message")
        if message:
            defaults["payload"].update({"message": message})

        defaults.update(data)
        return SmartAppFromMessage(json.dumps(defaults), headers=headers)
예제 #17
0
    def iterate(self, kafka_key):
        consumer = self.consumers[kafka_key]
        mq_message = None
        message_value = None
        try:
            mq_message = None
            message_value = None
            stats = ""
            with StatsTimer() as poll_timer:
                mq_message = consumer.poll()
            if mq_message:
                stats += "Polling time: {} msecs\n".format(poll_timer.msecs)
                topic_key = self._get_topic_key(mq_message)

                save_tries = 0
                user_save_no_collisions = False
                user = None
                db_uid = None
                while save_tries < self.user_save_collisions_tries and not user_save_no_collisions:
                    save_tries += 1
                    message_value = mq_message.value()
                    message = SmartAppFromMessage(
                        message_value,
                        headers=mq_message.headers(),
                        masking_fields=self.masking_fields)

                    # TODO вернуть проверку ключа!!!
                    if message.validate(
                    ):  # and self.check_message_key(message, mq_message.key()):
                        log("INCOMING FROM TOPIC: %(topic)s partition %(message_partition)s HEADERS: %(headers)s DATA: %(incoming_data)s",
                            params={
                                log_const.KEY_NAME: "incoming_message",
                                "topic": mq_message.topic(),
                                "message_partition": mq_message.partition(),
                                "message_key": mq_message.key(),
                                "kafka_key": kafka_key,
                                "incoming_data": str(message.masked_value),
                                "headers": str(mq_message.headers()),
                                MESSAGE_ID_STR: message.incremental_id
                            })

                        db_uid = message.db_uid

                        span = jaeger_utils.get_incoming_spam(
                            self.tracer, message, mq_message)

                        with self.tracer.scope_manager.activate(span,
                                                                True) as scope:
                            with StatsTimer() as load_timer:
                                user = self.load_user(db_uid, message)

                        with self.tracer.scope_manager.activate(span,
                                                                True) as scope:
                            with self.tracer.start_span(
                                    'Loading time',
                                    child_of=scope.span) as span:
                                smart_kit_metrics.sampling_load_time(
                                    self.app_name, load_timer.secs)
                                stats += "Loading time: {} msecs\n".format(
                                    load_timer.msecs)
                                with StatsTimer() as script_timer:
                                    commands = self.model.answer(message, user)

                            with self.tracer.start_span(
                                    'Script time',
                                    child_of=scope.span) as span:
                                answers = self._generate_answers(
                                    user=user,
                                    commands=commands,
                                    message=message,
                                    topic_key=topic_key,
                                    kafka_key=kafka_key)
                                smart_kit_metrics.sampling_script_time(
                                    self.app_name, script_timer.secs)
                                stats += "Script time: {} msecs\n".format(
                                    script_timer.msecs)

                            with self.tracer.start_span(
                                    'Saving time',
                                    child_of=scope.span) as span:
                                with StatsTimer() as save_timer:
                                    user_save_no_collisions = self.save_user(
                                        db_uid, user, message)

                            smart_kit_metrics.sampling_save_time(
                                self.app_name, save_timer.secs)
                            stats += "Saving time: {} msecs\n".format(
                                save_timer.msecs)
                            if not user_save_no_collisions:
                                log("MainLoop.iterate: save user got collision on uid %(uid)s db_version %(db_version)s.",
                                    user=user,
                                    params={
                                        log_const.KEY_NAME:
                                        "ignite_collision",
                                        "db_uid":
                                        db_uid,
                                        "message_key":
                                        mq_message.key(),
                                        "message_partition":
                                        mq_message.partition(),
                                        "kafka_key":
                                        kafka_key,
                                        "uid":
                                        user.id,
                                        "db_version":
                                        str(
                                            user.variables.get(
                                                user.USER_DB_VERSION))
                                    },
                                    level="WARNING")
                                continue

                            self.save_behavior_timeouts(
                                user, mq_message, kafka_key)

                        if mq_message.headers() is None:
                            mq_message.set_headers([])
                        self.tracer.inject(span_context=span.context,
                                           format=jaeger_kafka_codec.KAFKA_MAP,
                                           carrier=mq_message.headers())

                        if answers:
                            for answer in answers:
                                with StatsTimer() as publish_timer:
                                    self._send_request(user, answer,
                                                       mq_message)
                                stats += "Publishing time: {} msecs".format(
                                    publish_timer.msecs)
                                log(stats)
                    else:
                        try:
                            data = message.masked_value
                        except:
                            data = "<DATA FORMAT ERROR>"
                        log(f"Message validation failed, skip message handling.",
                            params={
                                log_const.KEY_NAME: "invalid_message",
                                "data": data
                            },
                            level="ERROR")
                        smart_kit_metrics.counter_invalid_message(
                            self.app_name)
                if user and not user_save_no_collisions:
                    log("MainLoop.iterate: db_save collision all tries left on uid %(uid)s db_version %(db_version)s.",
                        user=user,
                        params={
                            log_const.KEY_NAME:
                            "ignite_collision",
                            "db_uid":
                            db_uid,
                            "message_key":
                            mq_message.key(),
                            "message_partition":
                            mq_message.partition(),
                            "kafka_key":
                            kafka_key,
                            "uid":
                            user.id,
                            "db_version":
                            str(user.variables.get(user.USER_DB_VERSION))
                        },
                        level="WARNING")

                    smart_kit_metrics.counter_save_collision_tries_left(
                        self.app_name)
                consumer.commit_offset(mq_message)
        except KafkaException as kafka_exp:
            log("kafka error: %(kafka_exp)s. MESSAGE: {}.".format(
                message_value),
                params={
                    log_const.KEY_NAME: log_const.STARTUP_VALUE,
                    "kafka_exp": str(kafka_exp),
                    log_const.REQUEST_VALUE: str(message_value)
                },
                level="ERROR",
                exc_info=True)
        except Exception:
            try:
                log("%(class_name)s iterate error. Kafka key %(kafka_key)s MESSAGE: {}."
                    .format(message_value),
                    params={
                        log_const.KEY_NAME: log_const.STARTUP_VALUE,
                        "kafka_key": kafka_key
                    },
                    level="ERROR",
                    exc_info=True)
                consumer.commit_offset(mq_message)
            except Exception:
                log("Error handling worker fail exception.",
                    level="ERROR",
                    exc_info=True)
    def process_message(self, mq_message, consumer, kafka_key, stats):
        topic_key = self._get_topic_key(mq_message, kafka_key)

        save_tries = 0
        user_save_no_collisions = False
        user = None
        db_uid = None
        message = None
        while save_tries < self.user_save_collisions_tries and not user_save_no_collisions:
            save_tries += 1
            message_value = mq_message.value()
            message = SmartAppFromMessage(
                message_value,
                headers=mq_message.headers(),
                masking_fields=self.masking_fields,
                creation_time=consumer.get_msg_create_time(mq_message))

            # TODO вернуть проверку ключа!!!
            if message.validate():
                waiting_message_time = 0
                if message.creation_time:
                    waiting_message_time = time.time(
                    ) * 1000 - message.creation_time
                    stats += "Waiting message: {} msecs\n".format(
                        waiting_message_time)

                stats += "Mid: {}\n".format(message.incremental_id)
                smart_kit_metrics.sampling_mq_waiting_time(
                    self.app_name, waiting_message_time / 1000)

                self.check_message_key(message, mq_message.key(), user)
                log("INCOMING FROM TOPIC: %(topic)s partition %(message_partition)s HEADERS: %(headers)s DATA: %(incoming_data)s",
                    params={
                        log_const.KEY_NAME: "incoming_message",
                        "topic": mq_message.topic(),
                        "message_partition": mq_message.partition(),
                        "message_key": mq_message.key(),
                        "message_id": message.incremental_id,
                        "kafka_key": kafka_key,
                        "incoming_data": str(message.masked_value),
                        "length": len(message.value),
                        "headers": str(mq_message.headers()),
                        "waiting_message": waiting_message_time,
                        "surface": message.device.surface,
                        MESSAGE_ID_STR: message.incremental_id
                    },
                    user=user)

                db_uid = message.db_uid
                with StatsTimer() as load_timer:
                    user = self.load_user(db_uid, message)
                smart_kit_metrics.sampling_load_time(self.app_name,
                                                     load_timer.secs)
                stats += "Loading time: {} msecs\n".format(load_timer.msecs)
                with StatsTimer() as script_timer:
                    commands = self.model.answer(message, user)

                answers = self._generate_answers(user=user,
                                                 commands=commands,
                                                 message=message,
                                                 topic_key=topic_key,
                                                 kafka_key=kafka_key)
                smart_kit_metrics.sampling_script_time(self.app_name,
                                                       script_timer.secs)
                stats += "Script time: {} msecs\n".format(script_timer.msecs)

                with StatsTimer() as save_timer:
                    user_save_no_collisions = self.save_user(
                        db_uid, user, message)

                smart_kit_metrics.sampling_save_time(self.app_name,
                                                     save_timer.secs)
                stats += "Saving time: {} msecs\n".format(save_timer.msecs)
                if not user_save_no_collisions:
                    log("MainLoop.iterate: save user got collision on uid %(uid)s db_version %(db_version)s.",
                        user=user,
                        params={
                            log_const.KEY_NAME:
                            "ignite_collision",
                            "db_uid":
                            db_uid,
                            "message_key":
                            mq_message.key(),
                            "message_partition":
                            mq_message.partition(),
                            "kafka_key":
                            kafka_key,
                            "uid":
                            user.id,
                            "db_version":
                            str(user.variables.get(user.USER_DB_VERSION))
                        },
                        level="WARNING")
                    continue

                self.save_behavior_timeouts(user, mq_message, kafka_key)

                if mq_message.headers() is None:
                    mq_message.set_headers([])

                if answers:
                    for answer in answers:
                        with StatsTimer() as publish_timer:
                            self._send_request(user, answer, mq_message)
                        stats += "Publishing time: {} msecs".format(
                            publish_timer.msecs)
                        log(stats, user=user)
            else:
                try:
                    data = message.masked_value
                except:
                    data = "<DATA FORMAT ERROR>"
                log(f"Message validation failed, skip message handling.",
                    params={
                        log_const.KEY_NAME: "invalid_message",
                        "data": data
                    },
                    level="ERROR")
                smart_kit_metrics.counter_invalid_message(self.app_name)
        if user and not user_save_no_collisions:
            log("MainLoop.iterate: db_save collision all tries left on uid %(uid)s db_version %(db_version)s.",
                user=user,
                params={
                    log_const.KEY_NAME: "ignite_collision",
                    "db_uid": db_uid,
                    "message_key": mq_message.key(),
                    "message_partition": mq_message.partition(),
                    "kafka_key": kafka_key,
                    "uid": user.id,
                    "db_version": str(user.variables.get(user.USER_DB_VERSION))
                },
                level="WARNING")
            self.postprocessor.postprocess(user, message)
            smart_kit_metrics.counter_save_collision_tries_left(self.app_name)
        consumer.commit_offset(mq_message)
예제 #19
0
def create_message(data, headers=None):
    defaults = Environment().as_dict
    defaults.update(data)

    return SmartAppFromMessage(json.dumps(defaults), headers=headers)