]> BookStack Code Mirror - api-scripts/commitdiff
Added python attachment upload example
authorDan Brown <redacted>
Sat, 3 Dec 2022 02:45:54 +0000 (02:45 +0000)
committerDan Brown <redacted>
Sat, 3 Dec 2022 02:45:54 +0000 (02:45 +0000)
.gitignore
python-upload-attachment/main.py [new file with mode: 0644]
python-upload-attachment/readme.md [new file with mode: 0644]
python-upload-attachment/requirements.txt [new file with mode: 0644]

index 637d92c60e58ba9b0e1da235518f163bda25838c..c0be7c6990ba4a78ee6ee9e0f5f29c4e661989a0 100644 (file)
@@ -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 (file)
index 0000000..c2207b4
--- /dev/null
@@ -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 <page_id> and <file_path> 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 (file)
index 0000000..4b3c7fa
--- /dev/null
@@ -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 <page_id> <file_path>
+```
+
+- `<page_id>` - The ID of the page you want to upload the attachment to.
+- `<file_path>` - 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 (file)
index 0000000..2a06dd1
--- /dev/null
@@ -0,0 +1,5 @@
+certifi==2022.9.24
+charset-normalizer==2.1.1
+idna==3.4
+requests==2.28.1
+urllib3==1.26.13