예제 #1
0
async def _request_execute(app_engine: AppEngine, event_name: str,
                           context: EventContext, query_args: Dict[str, Any],
                           payload: Optional[EventPayloadType],
                           preprocess_hook: PreprocessHook) -> ResponseType:
    """
    Executes request using engine event handler
    """
    response_hook = PostprocessHook()
    result = await app_engine.preprocess(context=context,
                                         query_args=query_args,
                                         payload=payload,
                                         request=preprocess_hook)
    if (preprocess_hook.status is None) or (preprocess_hook.status == 200):
        result = await app_engine.execute(context=context,
                                          query_args=query_args,
                                          payload=result)
        result = await app_engine.postprocess(context=context,
                                              payload=result,
                                              response=response_hook)
    else:
        response_hook.set_status(preprocess_hook.status)
    # body = Json.to_json(result, key=event_name)
    response = _response(track_ids=context.track_ids,
                         key=event_name,
                         payload=result,
                         hook=response_hook)
    logger.done(context,
                extra=combined(_response_info(response), metrics(context)))
    return response
예제 #2
0
async def __postprocess__(payload: Something, context: EventContext,
                          response: PostprocessHook) -> str:  # noqa: C0103
    assert context.event_info.write_stream
    msg = f"events submitted to stream: {context.event_info.write_stream.name}"
    logger.info(context, msg)
    response.set_header("X-Stream-Name", context.event_info.write_stream.name)
    return msg
예제 #3
0
async def __postprocess__(payload: str, context: EventContext, response: PostprocessHook) -> str:
    if payload.startswith('ok:'):
        response.set_status(200)
        response.set_header('X-Status', 'ok')
        response.set_cookie('Test-Cookie', 'ok')
        return payload
    response.set_status(400)
    response.del_cookie('Test-Cookie')
    return "not-ok: " + payload
예제 #4
0
def set_refresh_token(app: AppDescriptor, auth_info: dict,
                      payload: AuthInfoExtended, response: PostprocessHook):
    """
    sets information to a hook providing a way for http servers
    to set an http-only cookie containing the refresh_token
    """
    response.set_cookie(name=f"{app.app_key()}.refresh",
                        value=f"Refresh {payload.refresh_token}",
                        httponly="true",
                        expires=payload.refresh_token_expiration,
                        max_age=payload.refresh_token_expiration,
                        path=f"{app_base_route_name(app)}/",
                        domain=auth_info.get("domain"))
예제 #5
0
async def invoke_execute(engine: AppEngine,
                         from_app: AppConfig,
                         event_name: str,
                         query_args: dict,
                         payload: DataObject,
                         expected: DataObject,
                         track_ids: Dict[str, str],
                         postprocess_expected: Optional[dict] = None):
    event_settings = get_event_settings(from_app.effective_settings,
                                        event_name)  # type: ignore
    context = EventContext(app_config=from_app,
                           plugin_config=engine.app_config,
                           event_name=event_name,
                           settings=event_settings,
                           track_ids=track_ids,
                           auth_info={
                               'auth_type': AuthType.UNSECURED,
                               'allowed': 'true'
                           })
    res = await engine.execute(context=context,
                               query_args=query_args,
                               payload=payload)
    assert res == expected
    if postprocess_expected:
        hook = PostprocessHook()
        await engine.postprocess(context=context, payload=res, response=hook)
        assert hook.headers == postprocess_expected['headers']
        assert hook.cookies == postprocess_expected['cookies']
        assert hook.status == postprocess_expected['status']
예제 #6
0
async def test_postprocess_host_create_web_stream_response(monkeypatch):

    monkeypatch.setattr(context_mod.web, "StreamResponse", MockStreamResponse)

    request = MagicMock()
    hook = PostprocessHook(request)
    context = MagicMock()
    track_ids = {
        "track1": "id1",
        "track2": "id2"
    }
    context.track_ids = {
        **track_ids
    }
    stream_response = await hook.prepare_stream_response(
        context, "test-disposition", "test-type", 42
    )
    assert stream_response.headers['Content-Disposition'] == "test-disposition"
    assert stream_response.headers['Content-Type'] == "test-type"
    assert stream_response.resp.content_type == "test-type"
    assert stream_response.resp.content_length == 42
    assert stream_response.resp.headers['X-Track1'] == "id1"
    assert stream_response.resp.headers['X-Track2'] == "id2"
    assert stream_response.resp.data == b''
    assert hook.content_type == "test-type"
예제 #7
0
async def mock_handle_postprocess(app_config, *, payload, expected,
                                  expected_response):
    handler = EventHandler(app_config=app_config,
                           plugins=[],
                           effective_events=app_config.events)
    context = EventContext(app_config=app_config,
                           plugin_config=app_config,
                           event_name='plugin_event',
                           track_ids={},
                           auth_info={
                               'auth_type': AuthType.UNSECURED,
                               'allowed': 'true'
                           })
    intermediate_result = None
    async for res in handler.handle_async_event(context=context,
                                                query_args={},
                                                payload=None):
        intermediate_result = res
    hook = PostprocessHook()
    response = await handler.postprocess(context=context,
                                         payload=intermediate_result,
                                         response=hook)
    assert hook.headers == expected_response['headers']
    assert hook.cookies == expected_response['cookies']
    assert hook.status == expected_response['status']
    assert response == expected
async def __postprocess__(
        payload: Union[Something, SomethingNotFound], context: EventContext,
        response: PostprocessHook) -> Union[Something, SomethingNotFound]:
    if isinstance(payload, SomethingNotFound):
        logger.debug(context, '404 - File %s not found', payload.id)
        response.status = 404
    return payload
예제 #9
0
async def invoke_postprocess(payload: None, context: EventContext):
    hook = PostprocessHook()
    result = await logout.__postprocess__(payload, context, response=hook)
    assert hook.del_cookies == [('test_app.test.refresh', (), {
        'path': '/api/test-app/test/',
        'domain': None
    })]
    assert result == 'Logged out.'
예제 #10
0
async def __postprocess__(result: RuntimeAppsConfig, context: EventContext,
                          response: PostprocessHook) -> str:
    """
    Renders html from template, using cytospace data json
    """
    response.set_content_type("text/html")

    app_prefix = result.options.app_prefix

    view_link = f"apps-visualizer?app_prefix={result.options.app_prefix}"
    view_link += f"&host_filter={result.options.host_filter}"
    view_link += f"&expanded_view={str(not result.options.expanded_view).lower()}"
    view_link += f"&live={str(result.options.live).lower()}"

    live_link = f"apps-visualizer?app_prefix={result.options.app_prefix}"
    live_link += f"&host_filter={result.options.host_filter}"
    live_link += f"&expanded_view={str(result.options.expanded_view).lower()}"
    live_link += f"&live={str(not result.options.live).lower()}"

    app_prefix = f"{result.options.app_prefix}*" if result.options.app_prefix else 'All running apps'
    host_filter = f"*{result.options.host_filter}*" if result.options.host_filter else 'All servers'
    view_type = "Effective Events" if result.options.expanded_view else "Configured Events"
    live_type = "Live!" if result.options.live else "Static"

    refresh_endpoint_comps = (['event-stats', 'live'] if result.options.live
                              else ['apps', 'events-graph'])
    refresh_endpoint = route_name("api", context.app.name, context.app.version,
                                  *refresh_endpoint_comps)
    refresh_endpoint += f"?app_prefix={result.options.app_prefix}"
    refresh_endpoint += f"&host_filter={result.options.host_filter}"
    refresh_endpoint += f"&expanded_view={str(result.options.expanded_view).lower()}"
    refresh_endpoint += f"&live={str(result.options.live).lower()}"

    with open(_dir_path / 'events_graph_template.html') as f:
        template = f.read()
        template = template.replace("{{ app_prefix }}", app_prefix)
        template = template.replace("{{ host_filter }}", host_filter)
        template = template.replace("{{ view_link }}", view_link)
        template = template.replace("{{ live_link }}", live_link)
        template = template.replace("{{ refresh_endpoint }}", refresh_endpoint)
        template = template.replace("{{ view_type }}", view_type)
        template = template.replace("{{ live_type }}", live_type)
        return template
예제 #11
0
async def __postprocess__(img_file: ImagePng, context: EventContext,
                          response: PostprocessHook) -> str:

    if os.path.isfile(img_file.file_path):
        response.set_header('Content-Disposition',
                            f'attachment; filename="{img_file.file_name}"')
        response.set_content_type(img_file.content_type)
        response.set_file_response(img_file.file_path)
        return f"File {img_file.file_name}"

    response.set_status(400)
    return f"File {img_file.file_name} not found"
예제 #12
0
async def __postprocess__(message: Optional[MyMessage], context: EventContext,
                          response: PostprocessHook) -> Union[MyMessage, str]:
    """
    Special handler to customize what's returned as a response to the POST request received.
    Sets status to 400 if the message was invalid and returns just a message.
    Returns the validated message otherwise.
    Notice that this step can occur after MyMessage was already submitted to the stream.
    """
    if message is None:
        response.status = 400
        return "invalid data received"
    return message
예제 #13
0
async def __postprocess__(img_file: ImagePng, context: EventContext, *,
                          response: PostprocessHook) -> str:
    response.set_header("Content-Disposition",
                        f"attachment; filename={img_file.file_name}")
    response.set_content_type(img_file.content_type)
    response.set_file_response(path=img_file.file_path)
    return img_file.file_name
예제 #14
0
 async def postprocess(self, *, context: EventContext,
                       payload: Optional[EventPayload],
                       response: PostprocessHook) -> Optional[EventPayload]:
     response.set_header("PluginHeader", "PluginHeaderValue")
     response.set_cookie("PluginCookie", "PluginCookieValue")
     response.set_status(999)
     return payload
예제 #15
0
async def __postprocess__(path: str, context: EventContext, *,
                          response: PostprocessHook) -> str:
    file_name = Path(path).name
    response.set_header("Content-Disposition",
                        f"attachment; filename={file_name}")
    response.set_header("Content-Type", 'text/plain')
    response.set_file_response(path=path)
    return path
예제 #16
0
async def invoke_postprocess(payload: AuthInfoExtended, context: EventContext):
    hook = PostprocessHook()
    result = await login.__postprocess__(payload, context, response=hook)
    assert hook.cookies['test_app.test.refresh'] == (
        f"Refresh {payload.refresh_token}", tuple(), {
            'domain': None,
            'expires': 3600,
            'httponly': 'true',
            'max_age': 3600,
            'path': '/api/test-app/test/'
        })
    assert result == AuthInfo(access_token=payload.access_token,
                              token_type=payload.token_type,
                              renew=payload.renew)
예제 #17
0
async def test_postprocess_host_create_stream_response():
    hook = PostprocessHook()
    context = MagicMock()
    track_ids = {
        "track1": "id1",
        "track2": "id2"
    }
    context.track_ids = {
        **track_ids
    }
    stream_response = await hook.prepare_stream_response(
        context, "test-disposition", "test-type", 42
    )
    assert stream_response.headers['Content-Disposition'] == "test-disposition"
    assert stream_response.headers['Content-Type'] == "test-type"
    assert stream_response.resp.headers['Content-Disposition'] == "test-disposition"
    assert stream_response.resp.headers['Content-Type'] == "test-type"
    assert stream_response.resp.headers['Content-Length'] == "42"
    assert stream_response.resp.headers['X-Track1'] == "id1"
    assert stream_response.resp.headers['X-Track2'] == "id2"
    assert stream_response.resp.data == b''
    assert hook.content_type == "test-type"
예제 #18
0
async def __postprocess__(payload: None, context: EventContext, *,
                          response: PostprocessHook) -> str:
    response.del_cookie(name=f"{context.app_key}.refresh",
                        path=f"{app_base_route_name(context.app)}/",
                        domain=context.auth_info.get("domain"))
    return "Logged out."
async def __postprocess__(payload: MockData, context: EventContext,
                          response: PostprocessHook) -> MockData:
    response.set_header('recognized', payload.value)
    return payload
예제 #20
0
async def __postprocess__(payload: str, context: EventContext, *,
                          response: PostprocessHook) -> str:
    response.set_header("PluginHeader", "PluginHeaderValue")
    response.set_cookie("PluginCookie", "PluginCookieValue")
    response.set_status(999)
    return "PluginEvent.postprocess"
async def __postprocess__(
        payload: Union[Something, SomethingNotFound], context: EventContext,
        response: PostprocessHook) -> Union[Something, SomethingNotFound]:
    if isinstance(payload, SomethingNotFound):
        response.status = 404
    return payload
예제 #22
0
async def execute_event(
    app_config: AppConfig,
    event_name: str,
    payload: Optional[EventPayload],
    mocks: Optional[List[Callable[[ModuleType, EventContext], None]]] = None,
    *,
    fields: Optional[Dict[str, str]] = None,
    upload: Optional[Dict[str, bytes]] = None,
    preprocess: bool = False,
    postprocess: bool = False,
    **kwargs
) -> Union[Optional[EventPayload], List[EventPayload], Tuple[
        Optional[EventPayload], EventPayload, PostprocessHook], Tuple[
            List[EventPayload], EventPayload, PostprocessHook]]:
    """
    Test executes an app event.

    Notice that event implementation file needs to be saved to disk since this will simulate
    execution similar to how engine actually execute events. Writing to stream will be ignored.

    :param app_config: AppConfig, load using `app_config = config('path/to/app-config.json')`
    :param event_name: str, name of the event / module to execute
    :param payload: test payload to send to initial step
    :param mocks: lists of functions to execute in order to mock functionality
    :param postprocess: enables testing __postprocess__ called with last step result or
        result before a SHUFFLE step if present.
    :param kwargs: that will be forwarded to the initial step of the event
    :return: the results of executing the event, for simple events it will be a single object,
        for events with initial Spawn[...] the results will be collected as a list.
        If postprocess is true, a tuple of 3 elements is return, first element is results as described
        above, second element the output of call to __postprocess__, and third one a PostprocessHook
        with response information used during call to __postprocess__
    """
    async def _postprocess(hook: PostprocessHook,
                           results: List[EventPayload]) -> EventPayload:
        pp_payload = results[-1] if len(results) > 0 else None
        return await handler.postprocess(context=context,
                                         payload=pp_payload,
                                         response=hook)

    async def _preprocess(hook: PreprocessHook,
                          payload: EventPayload) -> EventPayload:
        return await handler.preprocess(context=context,
                                        query_args=kwargs,
                                        payload=payload,
                                        request=hook)

    context = create_test_context(app_config, event_name)
    impl = find_event_handler(app_config=app_config, event_name=event_name)

    event_info = app_config.events[event_name]
    effective_events = {
        **split_event_stages(app_config.app, event_name, event_info, impl)
    }
    handler = EventHandler(app_config=app_config,
                           plugins=[],
                           effective_events=effective_events)

    preprocess_hook, postprocess_hook = None, None
    if preprocess:
        preprocess_hook = PreprocessHook(
            headers=CIMultiDictProxy(CIMultiDict()),
            multipart_reader=MockMultipartReader(fields or {}, upload
                                                 or {}),  # type: ignore
            file_hook_factory=MockFileHook)
    if postprocess:
        postprocess_hook = PostprocessHook()
    if mocks is not None:
        _apply_mocks(context, handler, event_name, effective_events,
                     preprocess_hook, postprocess_hook, mocks)

    if preprocess_hook:
        payload = await _preprocess(preprocess_hook, payload)
        if postprocess_hook and preprocess_hook.status is not None:
            postprocess_hook.set_status(preprocess_hook.status)
    datatype = find_datatype_handler(app_config=app_config,
                                     event_name=event_name)
    if datatype is None:
        if payload is not None:
            return (payload, payload,
                    postprocess_hook) if postprocess else payload
    elif not isinstance(payload, datatype):
        return (payload, payload, postprocess_hook) if postprocess else payload

    on_queue, pp_result, pp_called = [payload], None, False
    for effective_event_name, event_info in effective_events.items():
        context = create_test_context(app_config, effective_event_name)
        stage_results = []
        for elem in on_queue:
            async for res in handler.handle_async_event(context=context,
                                                        query_args=kwargs,
                                                        payload=elem):
                stage_results.append(res)
        on_queue = stage_results if len(stage_results) > 0 else on_queue
        if postprocess_hook and not pp_called:
            pp_called = True
            pp_result = await _postprocess(postprocess_hook, on_queue)
        kwargs = {}

    if postprocess:
        if len(on_queue) == 0:
            return None, pp_result, postprocess_hook
        if len(on_queue) == 1:
            return on_queue[0], pp_result, postprocess_hook
        return list(on_queue), pp_result, postprocess_hook

    if len(on_queue) == 0:
        return None
    if len(on_queue) == 1:
        return on_queue[0]
    return list(on_queue)