Example #1
0
def test_from_json_malformed():
    with pytest.raises(ValueError):
        Payload.from_json(
            '{"item1": {"id": "1", "value": "ok-1", "nested": {"ts": "1970-01-01T00:00:00+00:00"}',
            MockData)

    with pytest.raises(ValueError):
        Payload.from_json('BAD STRING', MockData)
Example #2
0
def test_from_json_missing_fields():
    with pytest.raises(ValueError):
        Payload.from_json('{"id": "1", "value": "ok-1", "nested": {}',
                          MockData)

    with pytest.raises(ValueError):
        Payload.from_json(
            '{"value": 42, "nested": {"ts": "1970-01-01T00:00:00+00:00"}}',
            MockData)
Example #3
0
def test_from_json_invalid_types():
    with pytest.raises(ValueError):
        Payload.from_json(
            '{"id": "1", "value": "ok-1", "nested": {"ts": "INVALID DATE"}}',
            MockData)

    with pytest.raises(ValueError):
        Payload.from_json(
            '{"id": 42, "value": 42, "nested": {"ts": "1970-01-01T00:00:00+00:00"}}',
            MockData)
Example #4
0
def test_to_obj_invalid_types():
    with pytest.raises(ValueError):
        Payload.to_obj(
            MockData(id=42,
                     value='ok-value',
                     nested=MockNested(ts=datetime.now(
                         tz=timezone.utc))))  # type: ignore

    with pytest.raises(ValueError):
        Payload.to_obj(
            MockData(id='1',
                     value='ok-value',
                     nested=MockNested(ts="NOT A DATETIME")))  # type: ignore
async def __preprocess__(payload: None, context: EventContext,
                         request: PreprocessHook, *,
                         something_id: str) -> Union[str, FileUploadInfo]:
    uploaded_files = []
    save_path = Path(str(context.env['upload_something']['save_path']))
    chunk_size = int(context.env['upload_something']['chunk_size'])
    async for file_hook in request.files():
        file_name = f"{file_hook.name}-{file_hook.file_name}"
        path = save_path / file_name
        logger.info(context, f"Saving {path}...")
        await save_multipart_attachment(file_hook, path, chunk_size=chunk_size)
        uploaded_file = UploadedFile(file_hook.name,
                                     file_name,
                                     save_path.as_posix(),
                                     size=file_hook.size)
        uploaded_files.append(uploaded_file)
    args = await request.parsed_args()
    if not all(x in args for x in ('id', 'user', 'attachment', 'object')):
        request.status = 400
        return "Missing required fields"
    something_obj = Payload.parse_form_field(args['object'], Something)
    return FileUploadInfo(id=args['id'],
                          user=args['user'],
                          object=something_obj,
                          uploaded_files=uploaded_files)
Example #6
0
async def test_buffer_object_and_flush_signal(app_config,
                                              test_objs):  # noqa: F811
    test_save_path = app_config.settings["test_stream_batch_storage"]["path"]

    # Buffer single object
    test_obj = test_objs[0]
    result = await execute_event(app_config=app_config,
                                 event_name='test_stream_batch_storage',
                                 payload=test_obj)
    assert result is None

    # Send flush partition signal to force flush single object
    partition_key = test_obj.object_ts.strftime("%Y/%m/%d/%H") + '/'
    signal = FlushSignal(partition_key)
    result = await execute_event(app_config=app_config,
                                 event_name='test_stream_batch_storage',
                                 payload=signal)
    assert result is None

    # Load saved object and check is correct
    await asyncio.sleep(1)  # Allow aiofiles to save
    saved_objects = {}
    for file_name in glob(f'{test_save_path}/{partition_key}/*.jsonlines'):
        with open(file_name) as f:
            for line in f:
                obj = Payload.from_json(line, datatype=MyObject)
                saved_objects[obj.object_id] = obj
    assert len(saved_objects) == 1
    assert test_obj == saved_objects[test_obj.object_id]
Example #7
0
def test_to_json_dataobject():
    assert Payload.to_json(
        MockData(
            id='test',
            value='ok',
            nested=MockNested(ts=datetime.fromtimestamp(0, tz=timezone.utc)))
    ) == '{"id": "test", "value": "ok", "nested": {"ts": "1970-01-01T00:00:00+00:00"}}'
Example #8
0
    async def store(self, key: str, value: DataObject, **kwargs):
        """
        Stores value under specified key

        :param key: str
        :param value: DataObject, instance of dataclass annotated with @dataobject
        :param **kwargs: You can use arguments expected by the set method in the aioredis library i.e.:
            ex sets an expire flag on key name for ex seconds.
            px sets an expire flag on key name for px milliseconds.
            nx if set to True, set the value at key name to value only if it does not exist.
            xx if set to True, set the value at key name to value only if it already exists.
            keepttl if True, retain the time to live associated with the key. (Available since Redis 6.0).
            *These arguments may vary depending on the version of aioredis installed.

        i.e. store object:
        ```
        redis_store.store(key='my_key', value=my_dataobject)
        ```

        i.e. store object with kwargs option, adding `ex=60` redis set a ttl of 60 seconds for the object:
        ```
        redis_store.store(key='my_key', value=my_dataobject, ex=60)
        ```

        """
        assert self._conn
        payload_str = str(Payload.to_json(value))
        await self._conn.set(key, payload_str, **kwargs)
Example #9
0
 def __init__(self, app_config: AppConfig, app_connection: str):
     self.app_key = app_config.app_key()
     self.app_conn_key = app_connection
     self.app_connection = app_config.app_connections[app_connection]
     settings_key = self.app_connection.settings or app_connection
     self.settings = Payload.from_obj(
         app_config.settings.get(settings_key, {}), AppsClientSettings)
     self.event_connections = {
         event_name: {
             conn.event: conn
             for conn in event_info.connections
             if conn.app_connection == app_connection
         }
         for event_name, event_info in app_config.events.items()
     }
     self.routes = {
         conn.event: self._get_route(conn.event)
         for event_info in app_config.events.values()
         for conn in event_info.connections
         if conn.app_connection == app_connection
     }
     self.conn_state: Optional[AppConnectionState] = None
     self.session: Optional[Any] = None
     self.token: Optional[str] = None
     self.token_expire: int = 0
async def __preprocess__(payload: None, context: EventContext,
                         request: PreprocessHook) -> MockData:
    args = await request.parsed_args()
    data = Payload.parse_form_field(args['field2'], MockData)
    return MockData(
        value=f"field1:{args['field1']} field2:{data.value} file:{args['file']}"
    )
Example #11
0
def test_from_obj_dataobject_list():
    assert Payload.from_obj(
        [{
            "id": "test1",
            "value": "ok",
            "nested": {
                "ts": "1970-01-01T00:00:00+00:00"
            }
        }, {
            "id": "test2",
            "value": "ok",
            "nested": {
                "ts": "1970-01-01T00:00:00+00:00"
            }
        }],
        list,
        item_datatype=MockData) == [
            MockData(
                id='test1',
                value='ok',
                nested=MockNested(
                    ts=datetime.fromtimestamp(0.0).astimezone(timezone.utc))),
            MockData(
                id='test2',
                value='ok',
                nested=MockNested(
                    ts=datetime.fromtimestamp(0.0).astimezone(timezone.utc)))
        ]
Example #12
0
def test_to_json_dict_dataobject():
    assert json.loads(
        Payload.to_json({
            'item1':
            MockData(id='1',
                     value='ok-1',
                     nested=MockNested(
                         ts=datetime.fromtimestamp(0, tz=timezone.utc))),
            'item2':
            MockData(id='2',
                     value='ok-2',
                     nested=MockNested(
                         ts=datetime.fromtimestamp(0, tz=timezone.utc))),
        })) == {
            "item1": {
                "id": "1",
                "value": "ok-1",
                "nested": {
                    "ts": "1970-01-01T00:00:00+00:00"
                }
            },
            "item2": {
                "id": "2",
                "value": "ok-2",
                "nested": {
                    "ts": "1970-01-01T00:00:00+00:00"
                }
            }
        }
Example #13
0
def test_from_obj_python_types():
    assert Payload.from_obj({"value": "str"}, str) == "str"
    assert Payload.from_obj({"value": 123}, int) == int(123)
    assert Payload.from_obj({"value": 123.45}, float) == float(123.45)
    assert Payload.from_obj({"value": True}, bool) is True
    assert Payload.from_obj({"custom": "str"}, str, key='custom') == "str"
    assert Payload.from_obj({"test": "dict"}, dict) == {'test': 'dict'}
    assert Payload.from_obj(["test1", "test2"], set) == {'test2', 'test1'}
    assert Payload.from_obj(["test1", "test2"], list) == ['test1', 'test2']
Example #14
0
def test_from_json_python_types():
    assert Payload.from_json('{"value": "str"}', str) == "str"
    assert Payload.from_json('{"value": 123}', int) == int(123)
    assert Payload.from_json('{"value": 123.45}', float) == float(123.45)
    assert Payload.from_json('{"value": true}', bool) is True
    assert Payload.from_json('{"custom": "str"}', str, key='custom') == "str"
    assert Payload.from_json('{"test": "dict"}', dict) == {'test': 'dict'}
    assert Payload.from_json('["test1", "test2"]', set) == {'test2', 'test1'}
    assert Payload.from_json('["test1", "test2"]', list) == ['test1', 'test2']
Example #15
0
def mock_client_app_config():
    return AppConfig(
        app=AppDescriptor(name='mock_client_app', version='test'),
        engine=AppEngineConfig(import_modules=['mock_client_app']),
        app_connections={
            "test_app_connection":
            AppConnection(name="test_app",
                          version=APPS_API_VERSION,
                          client="hopeit.apps_client.AppsClient"),
            "test_app_plugin_connection":
            AppConnection(name="test_app",
                          version=APPS_API_VERSION,
                          client="hopeit.apps_client.AppsClient",
                          plugin_name="test_plugin",
                          plugin_version=APPS_API_VERSION)
        },
        settings={
            "test_app_connection":
            Payload.to_obj(
                AppsClientSettings(
                    connection_str="http://test-host1,http://test-host2")),
            "test_app_plugin_connection":
            Payload.to_obj(
                AppsClientSettings(
                    connection_str="http://test-host1,http://test-host2",
                    auth_strategy=ClientAuthStrategy.FORWARD_CONTEXT))
        },
        events={
            "mock_client_event":
            EventDescriptor(
                type=EventType.GET,
                connections=[
                    EventConnection(app_connection="test_app_connection",
                                    event="test_event_get",
                                    type=EventConnectionType.GET),
                    EventConnection(app_connection="test_app_connection",
                                    event="test_event_post",
                                    type=EventConnectionType.POST),
                    EventConnection(
                        app_connection="test_app_plugin_connection",
                        event="test_event_plugin",
                        type=EventConnectionType.GET)
                ])
        },
        server=ServerConfig(logging=LoggingConfig(
            log_level="DEBUG", log_path="work/logs/test/"))).setup()
Example #16
0
def test_from_json_dataobject():
    assert Payload.from_json(
        '{"id": "test", "value": "ok", "nested": {"ts": "1970-01-01T00:00:00+00:00"}}',
        MockData) == MockData(
            id='test',
            value='ok',
            nested=MockNested(
                ts=datetime.fromtimestamp(0.0).astimezone(timezone.utc)))
Example #17
0
def _ignored_response(context: Optional[EventContext], status: int,
                      e: BaseException) -> web.Response:
    if context:
        logger.error(context, e)
        logger.ignored(context)
    else:
        logger.error(__name__, e)
    info = ErrorInfo.from_exception(e)
    return web.Response(status=status, body=Payload.to_json(info))
Example #18
0
def _failed_response(context: Optional[EventContext],
                     e: Exception) -> web.Response:
    if context:
        logger.error(context, e)
        logger.failed(context)
    else:
        logger.error(__name__, e)
    info = ErrorInfo.from_exception(e)
    return web.Response(status=500, body=Payload.to_json(info))
Example #19
0
def test_from_obj_list():
    assert Payload.from_obj([{
        "value": "ok1"
    }, {
        "value": "ok2"
    }],
                            list,
                            key='value',
                            item_datatype=str) == ["ok1", "ok2"]
Example #20
0
def test_to_obj_python_types():
    assert Payload.to_obj('str', key='test') == {"test": "str"}
    assert Payload.to_obj(123, key='test') == {"test": 123}
    assert Payload.to_obj(123.45, key='test') == {"test": 123.45}
    assert Payload.to_obj(True, key='test') == {"test": True}
    assert Payload.to_obj({'test': 'dict'}) == {"test": "dict"}
    assert Payload.to_obj({'test2', 'test1'}) == ["test1", "test2"]
    assert Payload.to_obj(['test2', 'test1']) == ["test2", "test1"]
async def _save_partition(partition_key: str, items: List[DataObject],
                          context: EventContext):
    settings = context.settings(datatype=FileStorageSettings)
    path = Path(settings.path) / partition_key
    file = path / f"{uuid.uuid4()}{SUFFIX}"
    logger.info(context, f"Saving {file}...")
    os.makedirs(path.resolve(), exist_ok=True)
    async with aiofiles.open(file, 'w') as f:
        for item in items:
            await f.write(Payload.to_json(item) + "\n")
Example #22
0
 async def _parse_response(
         self, response, context: EventContext,
         datatype: Type[EventPayloadType],
         target_event_name: str) -> List[EventPayloadType]:
     """
     Parses http response from external App, catching Unathorized errors
     and converting the result to the desired datatype
     """
     if response.status == 200:
         data = await response.json()
         if isinstance(data, list):
             return Payload.from_obj(data, list,
                                     item_datatype=datatype)  # type: ignore
         return [Payload.from_obj(data, datatype, key=target_event_name)]
     if response.status == 401:
         raise Unauthorized(context.app_key)
     if response.status >= 500:
         raise ServerException(await response.text())
     raise RuntimeError(await response.text())
Example #23
0
def effective_events_example():
    res = EFFECTIVE_EVENTS_EXAMPLE
    res = res.replace("${ENGINE_VERSION}", ENGINE_VERSION)
    res = res.replace("${APPS_API_VERSION}", APPS_API_VERSION)
    res = res.replace("${APPS_ROUTE_VERSION}", APPS_ROUTE_VERSION)
    res = json.loads(res)

    result = Payload.from_obj(res,
                              datatype=dict,
                              item_datatype=EventDescriptor)

    return result
Example #24
0
async def __preprocess__(payload: None, context: EventContext,
                         request: PreprocessHook, *,
                         query_arg1: str) -> Union[str, MockData]:
    fields = await request.parsed_args()
    if any(x not in fields for x in ('field1', 'field2', 'attachment')):
        request.set_status(400)
        return "Missing required fields"
    data = Payload.parse_form_field(fields['field2'], MockData)
    return MockData(
        value=
        f"field1={fields['field1']} field2={data.value} attachment={fields['attachment']}"
    )
Example #25
0
def _get_runtime_simple_example(url: str, source: str):
    with open(Path(__file__).parent / source) as f:
        res = f.read()
        res = res.replace("${HOST_NAME}", socket.gethostname())
        res = res.replace("${PID}", str(os.getpid()))
        res = res.replace("${URL}", url)
        res = res.replace("${ENGINE_VERSION}", ENGINE_VERSION)
        res = res.replace("${APPS_API_VERSION}", APPS_API_VERSION)
        res = res.replace("${APPS_ROUTE_VERSION}", APPS_ROUTE_VERSION)

    result = Payload.from_json(res, RuntimeApps)

    return result
Example #26
0
def mock_runtime(monkeypatch, effective_events):
    setattr(apps, "_expire", 0.0)
    monkeypatch.setattr(server_config.os, 'getenv', mock_getenv)
    app_config = config('apps/examples/simple-example/config/app-config.json')
    basic_auth_config = config(
        'plugins/auth/basic-auth/config/plugin-config.json')
    client_app_config = config(
        'apps/examples/client-example/config/app-config.json')
    server = MockServer(basic_auth_config, app_config, client_app_config)
    for app_key, app_effective_events in effective_events.items():
        server.app_engines[app_key].effective_events = Payload.from_obj(
            app_effective_events, datatype=dict, item_datatype=EventDescriptor)
    monkeypatch.setattr(runtime, "server", server)
Example #27
0
    async def get(self, key: str, *,
                  datatype: Type[DataObject]) -> Optional[DataObject]:
        """
        Retrieves value under specified key, converted to datatype

        :param key: str
        :param datatype: dataclass implementing @dataobject (@see DataObject)
        :return: instance of datatype or None if not found
        """
        assert self._conn
        payload_str = await self._conn.get(key)
        if payload_str:
            return Payload.from_json(payload_str, datatype)
        return None
Example #28
0
    async def store(self, key: str, value: DataObject) -> str:
        """
        Stores value under specified key

        :param key: str
        :param value: DataObject, instance of dataclass annotated with @dataobject
        :return: str, path where the object was stored
        """
        payload_str = Payload.to_json(value)
        path = self.path
        if self.partition_dateformat:
            path = path / get_partition_key(value, self.partition_dateformat)
            os.makedirs(path.resolve().as_posix(), exist_ok=True)
        return await self._save_file(payload_str, path=path, file_name=key + SUFFIX)
Example #29
0
def test_from_obj_dict():
    assert Payload.from_obj(
        {
            "item1": {
                "value": "ok1"
            },
            "item2": {
                "value": "ok2"
            }
        },
        dict,
        key='value',
        item_datatype=str) == {
            "item1": "ok1",
            "item2": "ok2"
        }
Example #30
0
async def _request_process_payload(
    context: EventContext, datatype: Optional[Type[EventPayloadType]],
    request: web.Request
) -> Tuple[Optional[EventPayloadType], Optional[bytes]]:
    """
    Extract payload from request.
    Returns payload if parsing succeeded. Raises BadRequest if payload fails to parse
    """
    try:
        payload_raw = await request.read()
        if datatype is not None:
            return Payload.from_json(payload_raw,
                                     datatype), payload_raw  # type: ignore
        return None, payload_raw
    except ValueError as e:
        logger.error(context, e)
        raise BadRequest(e) from e