예제 #1
0
 def mock_hooks(module, context: EventContext,
                preprocess_hook: PreprocessHook,
                postprocess_hook: PostprocessHook):
     preprocess_hook.headers = PreprocessHeaders.from_dict(
         {'X-Track-Request-Id': 'Testing!'})
     preprocess_hook.payload_raw = b'OK\n'
     assert postprocess_hook.headers.get('recognized') is None
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 = Json.parse_form_field(args['object'], Something)
    return FileUploadInfo(id=args['id'],
                          user=args['user'],
                          object=something_obj,
                          uploaded_files=uploaded_files)
예제 #3
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 = Json.parse_form_field(fields['field2'], MockData)
    return MockData(value=f"field1={fields['field1']} field2={data.value} attachment={fields['attachment']}")
예제 #4
0
async def __preprocess__(payload: SomethingParams, context: EventContext,
                         request: PreprocessHook) -> Union[str, SomethingParams]:
    user_agent = request.headers.get('user-agent')
    if (user_agent is None) or (user_agent.strip() == ''):
        logger.info(context, "Missing required user-agent")
        request.set_status(400)
        return "Missing required user-agent"

    logger.info(context, "Save request", extra=extra(user_agent=user_agent))
    return payload
예제 #5
0
async def _handle_multipart_invocation(app_engine: AppEngine, impl: AppEngine,
                                       event_name: str,
                                       datatype: Optional[Type[DataObject]],
                                       auth_types: List[AuthType],
                                       request: web.Request) -> ResponseType:
    """
    Handler to execute POST calls
    """
    context = None
    try:
        context = _request_start(app_engine, impl, event_name, request)
        query_args = dict(request.query)
        _validate_authorization(app_engine.app_config, context, auth_types,
                                request)
        hook = PreprocessHook(  # type: ignore
            headers=request.headers,
            multipart_reader=await request.multipart()  # type: ignore
        )
        return await _request_execute(impl,
                                      event_name,
                                      context,
                                      query_args,
                                      payload=None,
                                      preprocess_hook=hook)
    except Unauthorized as e:
        return _ignored_response(context, 401, e)
    except BadRequest as e:
        return _ignored_response(context, 400, e)
    except Exception as e:  # pylint: disable=broad-except
        return _failed_response(context, e)
예제 #6
0
async def _handle_get_invocation(app_engine: AppEngine, impl: AppEngine,
                                 event_name: str, auth_types: List[AuthType],
                                 request: web.Request) -> ResponseType:
    """
    Handler to execute GET calls
    """
    context = None
    try:
        context = _request_start(app_engine, impl, event_name, request)
        _validate_authorization(app_engine.app_config, context, auth_types,
                                request)
        query_args = dict(request.query)
        payload = query_args.get('payload')
        if payload is not None:
            del query_args['payload']
        hook: PreprocessHook[NoopMultiparReader] = PreprocessHook(
            headers=request.headers)
        return await _request_execute(impl,
                                      event_name,
                                      context,
                                      query_args,
                                      payload=payload,
                                      preprocess_hook=hook)
    except Unauthorized as e:
        return _ignored_response(context, 401, e)
    except BadRequest as e:
        return _ignored_response(context, 400, e)
    except Exception as e:  # pylint: disable=broad-except
        return _failed_response(context, e)
예제 #7
0
async def _handle_post_invocation(app_engine: AppEngine, impl: AppEngine,
                                  event_name: str,
                                  datatype: Optional[Type[DataObject]],
                                  auth_types: List[AuthType],
                                  request: web.Request) -> ResponseType:
    """
    Handler to execute POST calls
    """
    context = None
    try:
        event_settings = get_event_settings(app_engine.settings, event_name)
        context = _request_start(app_engine, impl, event_name, event_settings,
                                 request)
        query_args = dict(request.query)
        _validate_authorization(app_engine.app_config, context, auth_types,
                                request)
        payload, payload_raw = await _request_process_payload(
            context, datatype, request)
        hook: PreprocessHook[NoopMultiparReader] = PreprocessHook(
            headers=request.headers, payload_raw=payload_raw)
        return await _request_execute(impl,
                                      event_name,
                                      context,
                                      query_args,
                                      payload,
                                      preprocess_hook=hook,
                                      request=request)
    except Unauthorized as e:
        return _ignored_response(context, 401, e)
    except BadRequest as e:
        return _ignored_response(context, 400, e)
    except Exception as e:  # pylint: disable=broad-except
        return _failed_response(context, e)
예제 #8
0
async def test_preprocess_hook_parsed_args():
    fields = {'a': 'field-a', 'b': 'field-b'}
    reader = MockMultipartReader(fields=fields, attachments={})
    hook = PreprocessHook(
        headers={},
        multipart_reader=reader,
        file_hook_factory=MockFileHook
    )
    args = await hook.parsed_args()
    assert args == fields
예제 #9
0
async def test_preprocess_hook():
    attachment_data = b'testdatatestdatatestdatatestdata'
    fields = {'a': 'field-a', 'b': 'field-b', 'file-a': 'filename-a', 'file-b': 'filename-b',
              'c': {"name": "field-c"}}
    attachments = {'file-a': attachment_data, 'file-b': attachment_data}
    reader = MockMultipartReader(fields=fields, attachments=attachments)
    hook = PreprocessHook(
        headers={},
        multipart_reader=reader,
        file_hook_factory=MockFileHook
    )

    file_data = {'file-a': b'', 'file-b': b''}
    async for file in hook.files():
        assert file.file_name == fields[file.name]
        async for chunk in file.read_chunks():
            file_data[file.name] += chunk

    assert file_data == attachments

    args = await hook.parsed_args()
    assert args == fields
예제 #10
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)
예제 #11
0
 def mock_hooks(module, context: EventContext,
                preprocess_hook: PreprocessHook,
                postprocess_hook: PostprocessHook):
     preprocess_hook.headers = PreprocessHeaders.from_dict(
         {'user-agent': 'Testing!'})
     assert postprocess_hook.headers.get('recognized') is None
예제 #12
0
def mock_user_agent_header(module, context, *,
                           preprocess_hook: PreprocessHook):
    preprocess_hook.headers = PreprocessHeaders.from_dict(
        {'user-agent': 'Testing!'})