Yield with uploads (#1339)

This commit is contained in:
Elijah Ahianyo 2023-07-18 21:06:57 +00:00 committed by GitHub
parent 3f151f054d
commit ca11b82432
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 53 additions and 36 deletions

View File

@ -323,21 +323,26 @@ export const uploadFiles = async (state, setResult, handler) => {
} }
// Send the file to the server. // Send the file to the server.
await axios.post(UPLOADURL, formdata, headers).then((response) => { await axios.post(UPLOADURL, formdata, headers)
// Apply the delta and set the result. .then(() => { return true; })
const update = response.data; .catch(
applyDelta(state, update.delta); error => {
if (error.response) {
// Set processing to false and return. // The request was made and the server responded with a status code
setResult({ // that falls out of the range of 2xx
state: state, console.log(error.response.data);
events: update.events, } else if (error.request) {
final: true, // The request was made but no response was received
processing: false, // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
}); // http.ClientRequest in node.js
}); console.log(error.request);
} else {
return true; // Something happened in setting up the request that triggered an Error
console.log(error.message);
}
return false;
}
)
}; };
/** /**

View File

@ -88,6 +88,9 @@ class App(Base):
# The component to render if there is a connection error to the server. # The component to render if there is a connection error to the server.
connect_error_component: Optional[Component] = None connect_error_component: Optional[Component] = None
# The async server name space
event_namespace: Optional[AsyncNamespace] = None
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Initialize the app. """Initialize the app.
@ -127,11 +130,10 @@ class App(Base):
self.socket_app = ASGIApp(self.sio, socketio_path="") self.socket_app = ASGIApp(self.sio, socketio_path="")
# Create the event namespace and attach the main app. Not related to any paths. # Create the event namespace and attach the main app. Not related to any paths.
event_namespace = EventNamespace("/event", self) self.event_namespace = EventNamespace("/event", self)
# Register the event namespace with the socket. # Register the event namespace with the socket.
self.sio.register_namespace(event_namespace) self.sio.register_namespace(self.event_namespace)
# Mount the socket app with the API. # Mount the socket app with the API.
self.api.mount(str(constants.Endpoint.EVENT), self.socket_app) self.api.mount(str(constants.Endpoint.EVENT), self.socket_app)
@ -592,9 +594,6 @@ def upload(app: App):
Args: Args:
files: The file(s) to upload. files: The file(s) to upload.
Returns:
The state update after processing the event.
Raises: Raises:
ValueError: if there are no args with supported annotation. ValueError: if there are no args with supported annotation.
""" """
@ -603,10 +602,10 @@ def upload(app: App):
for file in files: for file in files:
assert file.filename is not None assert file.filename is not None
file.filename = file.filename.split(":")[-1] file.filename = file.filename.split(":")[-1]
# Get the state for the session. # Get the state for the session.
state = app.state_manager.get_state(token) state = app.state_manager.get_state(token)
# get the current session ID
sid = state.get_sid()
# get the current state(parent state/substate) # get the current state(parent state/substate)
path = handler.split(".")[:-1] path = handler.split(".")[:-1]
current_state = state.get_substate(path) current_state = state.get_substate(path)
@ -636,12 +635,17 @@ def upload(app: App):
name=handler, name=handler,
payload={handler_upload_param[0]: files}, payload={handler_upload_param[0]: files},
) )
# TODO: refactor this to handle yields. async for update in state._process(event):
update = await state._process(event).__anext__() # Postprocess the event.
update = await app.postprocess(state, event, update)
# Send update to client
await asyncio.create_task(
app.event_namespace.emit( # type: ignore
str(constants.SocketEvent.EVENT), update.json(), to=sid
)
)
# Set the state for the session. # Set the state for the session.
app.state_manager.set_state(event.token, state) app.state_manager.set_state(event.token, state)
return update
return upload_file return upload_file

View File

@ -602,7 +602,7 @@ async def test_dict_mutation_detection__plain_list(
@pytest.mark.asyncio @pytest.mark.asyncio
@pytest.mark.parametrize( @pytest.mark.parametrize(
"fixture, expected", "fixture, delta",
[ [
( (
"upload_state", "upload_state",
@ -626,22 +626,23 @@ async def test_dict_mutation_detection__plain_list(
), ),
], ],
) )
async def test_upload_file(fixture, request, expected): async def test_upload_file(fixture, request, delta):
"""Test that file upload works correctly. """Test that file upload works correctly.
Args: Args:
fixture: The state. fixture: The state.
request: Fixture request. request: Fixture request.
expected: Expected delta delta: Expected delta
""" """
app = App(state=request.getfixturevalue(fixture))
app.event_namespace.emit = AsyncMock() # type: ignore
current_state = app.state_manager.get_state("token")
data = b"This is binary data" data = b"This is binary data"
# Create a binary IO object and write data to it # Create a binary IO object and write data to it
bio = io.BytesIO() bio = io.BytesIO()
bio.write(data) bio.write(data)
app = App(state=request.getfixturevalue(fixture))
file1 = UploadFile( file1 = UploadFile(
filename="token:file_upload_state.multi_handle_upload:True:image1.jpg", filename="token:file_upload_state.multi_handle_upload:True:image1.jpg",
file=bio, file=bio,
@ -650,10 +651,17 @@ async def test_upload_file(fixture, request, expected):
filename="token:file_upload_state.multi_handle_upload:True:image2.jpg", filename="token:file_upload_state.multi_handle_upload:True:image2.jpg",
file=bio, file=bio,
) )
fn = upload(app) upload_fn = upload(app)
result = await fn([file1, file2]) # type: ignore await upload_fn([file1, file2])
assert isinstance(result, StateUpdate) state_update = StateUpdate(delta=delta, events=[], final=True)
assert result.delta == expected
app.event_namespace.emit.assert_called_with( # type: ignore
"event", state_update.json(), to=current_state.get_sid()
)
assert app.state_manager.get_state("token").dict()["img_list"] == [
"image1.jpg",
"image2.jpg",
]
@pytest.mark.asyncio @pytest.mark.asyncio