From: Dan Brown Date: Sat, 3 Dec 2022 02:45:54 +0000 (+0000) Subject: Added python attachment upload example X-Git-Url: http://source.bookstackapp.com/api-scripts/commitdiff_plain/482b7723a9245db4d53fdee7eb09b4f9de83b202 Added python attachment upload example --- diff --git a/.gitignore b/.gitignore index 637d92c..c0be7c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea/ -node_modules/ \ No newline at end of file +node_modules/ +venv/ diff --git a/python-upload-attachment/main.py b/python-upload-attachment/main.py new file mode 100644 index 0000000..c2207b4 --- /dev/null +++ b/python-upload-attachment/main.py @@ -0,0 +1,86 @@ +import os +import sys +import requests + +# This is where BookStack API details can be hard-coded if you prefer +# to write them in this script instead of using environment variables. +default_bookstack_options = { + "url": "", + "token_id": "", + "token_secret": "", +} + + +# Gather the BookStack API options either from the hard-coded details above otherwise +# it defaults back to environment variables. +def gather_api_options() -> dict: + return { + "url": default_bookstack_options["url"] or os.getenv("BS_URL"), + "token_id": default_bookstack_options["token_id"] or os.getenv("BS_TOKEN_ID"), + "token_secret": default_bookstack_options["token_secret"] or os.getenv("BS_TOKEN_SECRET"), + } + + +# Send a multipart post request to BookStack, at the given endpoint with the given data. +def bookstack_post_multipart(endpoint: str, data: dict) -> dict: + # Fetch the API-specific options + bs_api_opts = gather_api_options() + + # Format the request URL and the authorization header, so we can access the API + request_url = bs_api_opts["url"].rstrip("/") + "/api/" + endpoint.lstrip("/") + request_headers = { + "Authorization": "Token {}:{}".format(bs_api_opts["token_id"], bs_api_opts["token_secret"]) + } + + # Make the request to bookstack with the gathered details + response = requests.post(request_url, headers=request_headers, files=data) + + # Throw an error if the request was not successful + response.raise_for_status() + + # Return the response data decoded from it's JSON format + return response.json() + + +# Error out and exit the app +def error_out(message: str): + print(message) + exit(1) + + +# Run this when called on command line +if __name__ == '__main__': + + # Check arguments provided + if len(sys.argv) < 3: + error_out("Both and arguments need to be provided") + + # Gather details from the command line arguments and create a file name + # from the file path + page_id = sys.argv[1] + file_path = sys.argv[2] + file_name = os.path.basename(file_path) + + # Ensure the file exists + if not os.path.isfile(file_path): + error_out("Could not find provided file: {}".format(file_path)) + + # Gather the data we'll be sending to BookStack. + # The format matches that what the "requests" library expects + # to be provided for its "files" parameter. + post_data = { + "file": open(file_path, "rb"), + "name": (None, file_name), + "uploaded_to": (None, page_id) + } + + # Send the upload request and get back the attachment data + try: + attachment = bookstack_post_multipart("/attachments", post_data) + except requests.HTTPError as e: + error_out("Upload failed with status {} and data: {}".format(e.response.status_code, e.response.text)) + + # Output the results + print("File successfully uploaded to page {}.".format(page_id)) + print(" - Attachment ID: {}".format(attachment['id'])) + print(" - Attachment Name: {}".format(attachment['name'])) diff --git a/python-upload-attachment/readme.md b/python-upload-attachment/readme.md new file mode 100644 index 0000000..4b3c7fa --- /dev/null +++ b/python-upload-attachment/readme.md @@ -0,0 +1,45 @@ +# Upload a file attachment to a BookStack page + +This script will take a path to any local file and attempt +to upload it to a BookStack page as an attachment +using the API using a multipart/form-data request. + +This is a simplistic example of a Python script. You will likely want to +alter and extend this script to suit your use-case. + +This was written without in-depth knowledge of Python nor experience +of Python common conventions, so the style and approaches may appear unconventional. + +## Requirements + +You will need Python 3 installed (3.10.7). +This also uses pip to import requests as a dependency. + +## Running + +First, download all the files in the same directory as this readme to a folder on your system +and run the below from within that directory. + +```bash +# Install dependencies via PIP +pip install -r requirements.txt + +# Setup +# ALTERNATIVELY: Open the script and add to the empty strings in the variables at the top. +export BS_URL=https://bookstack.example.com # Set to be your BookStack base URL +export BS_TOKEN_ID=abc123 # Set to be your API token_id +export BS_TOKEN_SECRET=123abc # Set to be your API token_secret + +# Running the script +python main.py +``` + +- `` - The ID of the page you want to upload the attachment to. +- `` - File you want to upload as an attachment. + +## Examples + +```bash +# Upload the 'cat-image-collection.zip' file as an attachment to page of ID 205 +python main.py 205 ./cat-image-collection.zip +``` \ No newline at end of file diff --git a/python-upload-attachment/requirements.txt b/python-upload-attachment/requirements.txt new file mode 100644 index 0000000..2a06dd1 --- /dev/null +++ b/python-upload-attachment/requirements.txt @@ -0,0 +1,5 @@ +certifi==2022.9.24 +charset-normalizer==2.1.1 +idna==3.4 +requests==2.28.1 +urllib3==1.26.13