|
| 1 | +import os |
| 2 | +import sys |
| 3 | +import requests |
| 4 | + |
| 5 | +# This is where BookStack API details can be hard-coded if you prefer |
| 6 | +# to write them in this script instead of using environment variables. |
| 7 | +default_bookstack_options = { |
| 8 | + "url": "", |
| 9 | + "token_id": "", |
| 10 | + "token_secret": "", |
| 11 | +} |
| 12 | + |
| 13 | + |
| 14 | +# Gather the BookStack API options either from the hard-coded details above otherwise |
| 15 | +# it defaults back to environment variables. |
| 16 | +def gather_api_options() -> dict: |
| 17 | + return { |
| 18 | + "url": default_bookstack_options["url"] or os.getenv("BS_URL"), |
| 19 | + "token_id": default_bookstack_options["token_id"] or os.getenv("BS_TOKEN_ID"), |
| 20 | + "token_secret": default_bookstack_options["token_secret"] or os.getenv("BS_TOKEN_SECRET"), |
| 21 | + } |
| 22 | + |
| 23 | + |
| 24 | +# Send a multipart post request to BookStack, at the given endpoint with the given data. |
| 25 | +def bookstack_post_multipart(endpoint: str, data: dict) -> dict: |
| 26 | + # Fetch the API-specific options |
| 27 | + bs_api_opts = gather_api_options() |
| 28 | + |
| 29 | + # Format the request URL and the authorization header, so we can access the API |
| 30 | + request_url = bs_api_opts["url"].rstrip("/") + "/api/" + endpoint.lstrip("/") |
| 31 | + request_headers = { |
| 32 | + "Authorization": "Token {}:{}".format(bs_api_opts["token_id"], bs_api_opts["token_secret"]) |
| 33 | + } |
| 34 | + |
| 35 | + # Make the request to bookstack with the gathered details |
| 36 | + response = requests.post(request_url, headers=request_headers, files=data) |
| 37 | + |
| 38 | + # Throw an error if the request was not successful |
| 39 | + response.raise_for_status() |
| 40 | + |
| 41 | + # Return the response data decoded from it's JSON format |
| 42 | + return response.json() |
| 43 | + |
| 44 | + |
| 45 | +# Error out and exit the app |
| 46 | +def error_out(message: str): |
| 47 | + print(message) |
| 48 | + exit(1) |
| 49 | + |
| 50 | + |
| 51 | +# Run this when called on command line |
| 52 | +if __name__ == '__main__': |
| 53 | + |
| 54 | + # Check arguments provided |
| 55 | + if len(sys.argv) < 3: |
| 56 | + error_out("Both <page_id> and <file_path> arguments need to be provided") |
| 57 | + |
| 58 | + # Gather details from the command line arguments and create a file name |
| 59 | + # from the file path |
| 60 | + page_id = sys.argv[1] |
| 61 | + file_path = sys.argv[2] |
| 62 | + file_name = os.path.basename(file_path) |
| 63 | + |
| 64 | + # Ensure the file exists |
| 65 | + if not os.path.isfile(file_path): |
| 66 | + error_out("Could not find provided file: {}".format(file_path)) |
| 67 | + |
| 68 | + # Gather the data we'll be sending to BookStack. |
| 69 | + # The format matches that what the "requests" library expects |
| 70 | + # to be provided for its "files" parameter. |
| 71 | + post_data = { |
| 72 | + "file": open(file_path, "rb"), |
| 73 | + "name": (None, file_name), |
| 74 | + "uploaded_to": (None, page_id) |
| 75 | + } |
| 76 | + |
| 77 | + # Send the upload request and get back the attachment data |
| 78 | + try: |
| 79 | + attachment = bookstack_post_multipart("/attachments", post_data) |
| 80 | + except requests.HTTPError as e: |
| 81 | + error_out("Upload failed with status {} and data: {}".format(e.response.status_code, e.response.text)) |
| 82 | + |
| 83 | + # Output the results |
| 84 | + print("File successfully uploaded to page {}.".format(page_id)) |
| 85 | + print(" - Attachment ID: {}".format(attachment['id'])) |
| 86 | + print(" - Attachment Name: {}".format(attachment['name'])) |
0 commit comments