Skip to content
This repository was archived by the owner on Jan 7, 2024. It is now read-only.

Commit

Permalink
Adds download_submission method in the API
Browse files Browse the repository at this point in the history
We can now download a given submission on a given path.
  • Loading branch information
kushaldas committed Aug 2, 2018
1 parent 3cf7fa7 commit 5f86412
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 1 deletion.
106 changes: 106 additions & 0 deletions data/test-download-submission.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
interactions:
- request:
body: null
headers:
Accept: [application/json]
Accept-Encoding: ['gzip, deflate']
Authorization: [token eyJhbGciOiJIUzI1NiIsImV4cCI6MTUzMzIzNDg3OSwiaWF0IjoxNTMzMjA2MDc5fQ.eyJpZCI6MX0.xBkt6YFC5sjIKT_aXVPPD5194pgxGsLSbGxlUSyS7wE]
Connection: [keep-alive]
Content-Type: [application/json]
User-Agent: [python-requests/2.19.1]
method: GET
uri: http://localhost:8081/api/v1/submissions
response:
body: {string: "{\n \"submissions\": [\n {\n \"download_url\": \"/api/v1/sources/57682829-759b-4c92-89a0-a3c49ca7eca2/submissions/833fcb33-d5be-4119-bffd-7d7d442bc328/download\"\
, \n \"filename\": \"1-confused_blotter-msg.gpg\", \n \"is_read\"\
: false, \n \"size\": 604, \n \"source_url\": \"/api/v1/sources/57682829-759b-4c92-89a0-a3c49ca7eca2\"\
, \n \"submission_url\": \"/api/v1/sources/57682829-759b-4c92-89a0-a3c49ca7eca2/submissions/833fcb33-d5be-4119-bffd-7d7d442bc328\"\
, \n \"uuid\": \"833fcb33-d5be-4119-bffd-7d7d442bc328\"\n }, \n \
\ {\n \"download_url\": \"/api/v1/sources/57682829-759b-4c92-89a0-a3c49ca7eca2/submissions/85d6c0bf-1245-4c57-88f4-3039979c2acd/download\"\
, \n \"filename\": \"2-confused_blotter-msg.gpg\", \n \"is_read\"\
: false, \n \"size\": 604, \n \"source_url\": \"/api/v1/sources/57682829-759b-4c92-89a0-a3c49ca7eca2\"\
, \n \"submission_url\": \"/api/v1/sources/57682829-759b-4c92-89a0-a3c49ca7eca2/submissions/85d6c0bf-1245-4c57-88f4-3039979c2acd\"\
, \n \"uuid\": \"85d6c0bf-1245-4c57-88f4-3039979c2acd\"\n }, \n \
\ {\n \"download_url\": \"/api/v1/sources/5c4cd9d8-d421-4a39-bc6b-6c79346ab987/submissions/ce2b7a2a-a076-4e1e-abbc-d91ab6366231/download\"\
, \n \"filename\": \"1-traveled_worship-msg.gpg\", \n \"is_read\"\
: false, \n \"size\": 604, \n \"source_url\": \"/api/v1/sources/5c4cd9d8-d421-4a39-bc6b-6c79346ab987\"\
, \n \"submission_url\": \"/api/v1/sources/5c4cd9d8-d421-4a39-bc6b-6c79346ab987/submissions/ce2b7a2a-a076-4e1e-abbc-d91ab6366231\"\
, \n \"uuid\": \"ce2b7a2a-a076-4e1e-abbc-d91ab6366231\"\n }, \n \
\ {\n \"download_url\": \"/api/v1/sources/5c4cd9d8-d421-4a39-bc6b-6c79346ab987/submissions/a28c8fcb-ac8c-4120-bf56-1477197ee8e2/download\"\
, \n \"filename\": \"2-traveled_worship-msg.gpg\", \n \"is_read\"\
: false, \n \"size\": 604, \n \"source_url\": \"/api/v1/sources/5c4cd9d8-d421-4a39-bc6b-6c79346ab987\"\
, \n \"submission_url\": \"/api/v1/sources/5c4cd9d8-d421-4a39-bc6b-6c79346ab987/submissions/a28c8fcb-ac8c-4120-bf56-1477197ee8e2\"\
, \n \"uuid\": \"a28c8fcb-ac8c-4120-bf56-1477197ee8e2\"\n }\n ]\n\
}\n"}
headers:
Content-Length: ['2045']
Content-Type: [application/json]
Date: ['Thu, 02 Aug 2018 10:34:39 GMT']
Server: [Werkzeug/0.12.2 Python/2.7.6]
Set-Cookie: [js=eyJleHBpcmVzIjp7IiBkIjoiVGh1LCAwMiBBdWcgMjAxOCAxMjozNDozOSBHTVQifX0.DkRvvw.O0y66nWFuV5umboAf3YwaxIKBKk;
HttpOnly; Path=/]
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: [application/json]
Accept-Encoding: ['gzip, deflate']
Authorization: [token eyJhbGciOiJIUzI1NiIsImV4cCI6MTUzMzIzNDg3OSwiaWF0IjoxNTMzMjA2MDc5fQ.eyJpZCI6MX0.xBkt6YFC5sjIKT_aXVPPD5194pgxGsLSbGxlUSyS7wE]
Connection: [keep-alive]
Content-Type: [application/json]
User-Agent: [python-requests/2.19.1]
method: GET
uri: http://localhost:8081/api/v1/sources/57682829-759b-4c92-89a0-a3c49ca7eca2/submissions/833fcb33-d5be-4119-bffd-7d7d442bc328/download
response:
body:
string: !!binary |
hQIMA8PnxMCiIBsqAQ/+LyqyFGfYX1S248xECMqqVH2qHUF/K4Hr8aIOne0em1pkTNYT+ZbcbrK9
dPSl1B/NMm6DEUdR5DyAmVMP6rI+TVgJrPodhoWPkTDCKKsXz4CScyE3H4Ws5BfdCxN5EeQmyhYu
s/ImGKB9mMXryH9kXKGQPjEGsIWxoziySNAFgfk9k0pa/XSr/m+BKVU2cFWANfZewq/c0ykwupvk
0FQNft8Z2dNbJNB2X6D5m5/yx7DpZ3gv/IOgZKJskCY4WJj8Z/52z00nwbBAl66io04+FX8Q/mnU
rU1dW79Ir4YEuwdX13bmOLpKHbwGB3JTUws9MCwYBZqWxXV4va/ov+i9gCXuJSFlXx/+g2/etd8l
tTwn6L/AB4mXpZE7zJTQAvG0dJZkpz+bTXqSBwLcvpLdPeS4ubEsuJs8pv24qB5QbJccuNv7CfrZ
j7To91iuxERsHq2hkr5gVkJTZjouJiX2a85Mzol+dF37bpSbVIwKOIYvK/BmVtbv0dnnsnQ0btdQ
PXci0DhWF6+7dja1CM64kR29cdwY+bniZUN4Vg/PRoSg4yRMwpoa79pIOL9zdcF6Z/1GElugDxSr
jBLC6fQdva+XcQcU0+WRw4eIZtx1lxwixIsPydLmZ4Hme7TCTkZV9l8hH6wY6/LdD5FhbXCI0IXe
q78R3M8+USS/K6z+xoTSSwFagXPoJfr7O78VmYx49Ga+t1hnrqQECnkk+bAz0uGnAGlxzklJ5T47
98Qt6VhqRCa4M6d4xREXaQAqaxqKYEyyS9aexI2qP4wULQ==
headers:
Cache-Control: ['public, max-age=43200']
Content-Disposition: [attachment; filename=1-confused_blotter-msg.gpg]
Content-Length: ['604']
Content-Type: [application/pgp-encrypted]
Date: ['Thu, 02 Aug 2018 10:34:39 GMT']
Etag: ['"sha256:fed2cb0c519573d6a0db5a78782d9031d389a8f2d1d5504c73708f6ee6eab915"']
Expires: ['Thu, 02 Aug 2018 22:34:39 GMT']
Last-Modified: ['Thu, 02 Aug 2018 10:31:47 GMT']
Server: [Werkzeug/0.12.2 Python/2.7.6]
Set-Cookie: [js=eyJleHBpcmVzIjp7IiBkIjoiVGh1LCAwMiBBdWcgMjAxOCAxMjozNDozOSBHTVQifX0.DkRvvw.O0y66nWFuV5umboAf3YwaxIKBKk;
HttpOnly; Path=/]
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: [application/json]
Accept-Encoding: ['gzip, deflate']
Authorization: [token eyJhbGciOiJIUzI1NiIsImV4cCI6MTUzMzIzNDg3OSwiaWF0IjoxNTMzMjA2MDc5fQ.eyJpZCI6MX0.xBkt6YFC5sjIKT_aXVPPD5194pgxGsLSbGxlUSyS7wE]
Connection: [keep-alive]
Content-Type: [application/json]
User-Agent: [python-requests/2.19.1]
method: GET
uri: http://localhost:8081/api/v1/sources/57682829-759b-4c92-89a0-a3c49ca7eca2/submissions/833fcb33-d5be-4119-bffd-7d7d442bc328
response:
body: {string: "{\n \"download_url\": \"/api/v1/sources/57682829-759b-4c92-89a0-a3c49ca7eca2/submissions/833fcb33-d5be-4119-bffd-7d7d442bc328/download\"\
, \n \"filename\": \"1-confused_blotter-msg.gpg\", \n \"is_read\": true,\
\ \n \"size\": 604, \n \"source_url\": \"/api/v1/sources/57682829-759b-4c92-89a0-a3c49ca7eca2\"\
, \n \"submission_url\": \"/api/v1/sources/57682829-759b-4c92-89a0-a3c49ca7eca2/submissions/833fcb33-d5be-4119-bffd-7d7d442bc328\"\
, \n \"uuid\": \"833fcb33-d5be-4119-bffd-7d7d442bc328\"\n}\n"}
headers:
Content-Length: ['466']
Content-Type: [application/json]
Date: ['Thu, 02 Aug 2018 10:34:39 GMT']
Server: [Werkzeug/0.12.2 Python/2.7.6]
Set-Cookie: [js=eyJleHBpcmVzIjp7IiBkIjoiVGh1LCAwMiBBdWcgMjAxOCAxMjozNDozOSBHTVQifX0.DkRvvw.O0y66nWFuV5umboAf3YwaxIKBKk;
HttpOnly; Path=/]
status: {code: 200, message: OK}
version: 1
35 changes: 34 additions & 1 deletion sdclientapi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from pprint import pprint
import os
import json
import requests

from typing import Optional, Dict, List
from typing import Optional, Dict, List, Tuple
from mypy_extensions import TypedDict

T_token = TypedDict("T_token", {"expiration": str, "token": str})
Expand Down Expand Up @@ -329,6 +330,38 @@ def delete_submission(self, submission: Submission) -> bool:
# We should never reach here
return False

def download_submission(self, submission: Submission, path: str) -> Tuple[str, str]:
url = self.server.rstrip("/") + submission.download_url

if os.path.exists(path) and not os.path.isdir(path):
raise BaseError("Please provide a vaild directory to save.")

try:
res = requests.get(url, headers=self.auth_header, stream=True)

if res.status_code == 404:
raise WrongUUIDError("Missing submission {}".format(submission.uuid))

# Get the headers
headers = res.headers
etag = headers["Etag"]

# This is where we will save our downloaded file
filepath = os.path.join(path, submission.filename)
with open(filepath, "wb") as fobj:
for chunk in res.iter_content(
chunk_size=1024
): # Getting 1024 in each chunk
if chunk:
fobj.write(chunk)

# Because etag comes as JSON encoded string
etag = json.loads(etag)
# Return the tuple of sha256sum, filepath
return etag[7:], filepath
except Exception as err:
raise BaseError(err)

def flag_source(self, source: Source) -> bool:
url = self.server.rstrip("/") + "/api/v1/sources/{}/flag".format(source.uuid)

Expand Down
28 changes: 28 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
import os
import time
import json
import hashlib
import shutil
import tempfile
import unittest
from sdclientapi import *

Expand Down Expand Up @@ -147,3 +150,28 @@ def test_reply_source(self):
data = fobj.read()

self.assertTrue(self.api.reply_source(s, data))

@vcr.use_cassette("data/test-download-submission.yml")
def test_download_submission(self):
s = self.api.get_all_submissions()[0]

self.assertFalse(s.is_read)

# We need a temporary directory to download
tmpdir = tempfile.mkdtemp()
etag, filepath = self.api.download_submission(s, tmpdir)

# now let us read the downloaded file
with open(filepath, "rb") as fobj:
data = fobj.read()

shasum = hashlib.sha256(data).hexdigest()
self.assertEqual(etag, shasum)

# Now the submission should have is_read as True.

s = self.api.get_submission(s)
self.assertTrue(s.is_read)

# Let us remove the temporary directory
shutil.rmtree(tmpdir)

0 comments on commit 5f86412

Please sign in to comment.