Repository: freedomofpress/securedrop

Issue:
include `filename` in response when posting a reply
## Description

This request is motivated by implementing the replies feature in the SD client. When we make a reply, we get an object back from the API like

```json
{
    "message": "Message created or whaterver",
    "uuid": "some-uuud-abc123"
}
```

This is sufficient for us to track in-flight message, but it is not enough information for us to be able to correctly order replies. We need the `filename` that the server generates as well to be able to display the messages in the correct order. This is especially true if the Tor connections that are made cause messages to reach the server out of order.

We might as well return the entire object and drop the `message` field humans will not be seeing this and such messages are best conveyed the HTTP status code.

Without returning this field, if we want to have correct ordering of messages, for every reply we send send, we must make an additional call to fetch the reply object from the API to retrieve the `filename` for ordering.

## User Stories

As a consumer of the API, I want to make as few calls as possible in order to display information in a UI.


Discussion:
@heartsucker makes sense to me, please write down the full list of items you want to get (returned from the server) in the API call.
`POST /api/v1/sources/<source_uuid>/replies` should return the same object as `GET /api/v1/sources/<source_uuid>/replies/<reply_uuid>` for consistency (even though some of that information is redundant).

This is actually the only major change. The rest of the `POST`s and `DELETE`s shouldn't return

```json
{
    "message": "The thing happened"
}
```

But just be a `204 No Content` and have an empty body IMO. This is a breaking change, but since we're the only consumers of this API, I think that's acceptable.

It makes sense to add the filename to the reply API response so we know the interaction count on the client side without doing a sync.

> We might as well return the entire object and drop the message field humans will not be seeing this and such messages are best conveyed the HTTP status code.

Why remove an informative message (and thus make a breaking change, which the other suggestion in this ticket isn't)? Developers are humans. 
In all the cases where we have a confirmation message, it is always the same for a given HTTP code. I like to use the Github API as an example of a fairly good API, and in the case of object creation, they return the full object. In the case of all `POST`s or `DELETE`s, they don't return a status message outside of the HTTP code.

But w/e that's not really that important. I have a PR ready for the filename change which is all I really need to keep working.

Resolving patch:
diff --git a/securedrop/journalist_app/api.py b/securedrop/journalist_app/api.py
--- a/securedrop/journalist_app/api.py
+++ b/securedrop/journalist_app/api.py
@@ -267,7 +267,8 @@ def all_source_replies(source_uuid):
                     raise e
 
             return jsonify({'message': 'Your reply has been stored',
-                            'uuid': reply.uuid}), 201
+                            'uuid': reply.uuid,
+                            'filename': reply.filename}), 201
 
     @api.route('/sources/<source_uuid>/replies/<reply_uuid>',
                methods=['GET', 'DELETE'])


Test patch:
diff --git a/securedrop/tests/test_journalist_api.py b/securedrop/tests/test_journalist_api.py
--- a/securedrop/tests/test_journalist_api.py
+++ b/securedrop/tests/test_journalist_api.py
@@ -652,6 +652,9 @@ def test_authorized_user_can_add_reply(journalist_app, journalist_api_token,
     reply = Reply.query.filter_by(uuid=str(reply_uuid)).one_or_none()
     assert reply is not None
 
+    # check that the filename is present and correct (#4047)
+    assert response.json['filename'] == reply.filename
+
     with journalist_app.app_context():  # Now verify everything was saved.
         assert reply.journalist_id == test_journo['id']
         assert reply.source_id == source_id