gdrive: Add download_file_by_id command

If we already know the ID of a file, we want to download it directly
without jumping through the fuzzy matching hoops there are with
download_file_by_name. Extract the download code to a common function to
achieve this without duplicating the actual download code.

Change-Id: I3f2a3746e0046fa9d7c08f0003ea3cb70aed280c
diff --git a/gdrive_client/gdrive b/gdrive_client/gdrive
index f141154..827838d 100755
--- a/gdrive_client/gdrive
+++ b/gdrive_client/gdrive
@@ -263,6 +263,19 @@
     )
 
 
+def download_file(service: Resource, file_id: str, out_filepath: str) -> None:
+    """Download a file by id to the provided path.
+    """
+    request = service.files().get_media(fileId=file_id)
+    file = FileIO(out_filepath, mode="wb")
+    downloader = MediaIoBaseDownload(file, request)
+    done = False
+    while done is False:
+        status, done = downloader.next_chunk()
+        logger.debug("Downloaded %s%%.", int(status.progress() * 100))
+    logger.info("Download done.")
+
+
 def upload_file(
     service: Resource,
     file_path: Path,
@@ -512,15 +525,16 @@
         file_id,
         args.out_filepath,
     )
+    download_file(service, file_id, args.out_filepath)
 
-    request = service.files().get_media(fileId=file_id)
-    file = FileIO(args.out_filepath, mode="wb")
-    downloader = MediaIoBaseDownload(file, request)
-    done = False
-    while done is False:
-        status, done = downloader.next_chunk()
-        logger.debug("Downloaded %s%%.", int(status.progress() * 100))
-    logger.info("Download done.")
+
+def cmd_download_file_by_id(args: argparse.Namespace, service: Resource) -> None:
+    logger.info(
+        'Downloading file with id=%s to %s',
+        args.file_id,
+        args.out_filepath,
+    )
+    download_file(service, args.file_id, args.out_filepath)
 
 
 def cmd_find_file(args: argparse.Namespace, service: Resource) -> Optional[int]:
@@ -761,6 +775,25 @@
         required=True,
     )
 
+    parser_download_file_by_id = subparsers.add_parser(
+        "download_file_by_id",
+        help="Download a file by id.",
+    )
+    parser_download_file_by_id.set_defaults(func=cmd_download_file_by_id)
+    parser_download_file_by_id.add_argument(
+        "-i",
+        "--file-id",
+        help=("Find files by unique file id."),
+        type=str,
+    )
+    parser_download_file_by_id.add_argument(
+        "-o",
+        "--out-filepath",
+        help="Output filename or path to download the file to.",
+        type=str,
+        required=True,
+    )
+
     parser_find_file = subparsers.add_parser(
         "find_file",
         help="Find files by name on a Drive. Print attributes of matching files " "as json.",