Search code examples
pythonpython-requestsput

Python requests PUT not updating data


I have a yaml file : file.yaml structured as follows :

index:
  - uid: "uid"
    name: "name"
    headline: "headline"
    overview: "overview"
    features: "features"
    instructions: "instructions"
    callback_url: "https://some-url.com/params"
    edit_url: "https://edit-url/params"
    uninstall_hook: "https://uninstall-url/params"
    svg: 
    screenshot1: 
    screenshot2: 
    screenshot3: 

I have to upload those informations to an api endpoint by performing a PUT request. I managed to do it first using the register.py following script that I just run python register.py:

import json
import requests
from pathlib import Path
import base64
import yaml

BASE_URL = "https://url.com"  # API Host
FILE_FOLDER = Path.cwd()  # Current working directory


if __name__ == "__main__":

    public_key = <public_key>
    private_key = <private_key>

    auth_key = "{}:{}".format(public_key, private_key).encode("utf-8")

    encodedKey = base64.b64encode(auth_key).decode("utf-8")

    headers = {"Authorization": f"Basic {encodedKey}", "Content-type": "application/json"}

    def update_app_info():
        infos_file = FILE_FOLDER / "file.yaml"
        with open(infos_file) as infos_file_data:
            yamlcontent = yaml.safe_load(infos_file_data)  # Parse file.yaml and produce a dictionary of it
        file_infos = yamlcontent["index"][0]  # retrieve actual configuration informations
        response = requests.put(
            f"{BASE_URL}/path/to/api_endpoint/{public_key}", data=json.dumps(file_infos), headers=headers
        )
        print(response)
        print(response.json())

    update_app_info()

That gives a 202 success response. As you may observe, I tried to get content of the yaml file as a dicitonary and send that in data. I proceeded that way regarding format of data at GET https://url.com/path/to/api_endpoint (mock example for illustration...) . Having the dictionary file_infos seemed more appropriate and gets me a success response. Sending directly the file itself or 'infos_file_data' gave me some errors I got over with the above script.

The issue is when I update svg, screenshot1, screenshot2 & screenshot3 so that file.yaml is now :

index:
  - uid: "uid"
    name: "name"
    headline: "headline"
    overview: "overview"
    features: "features"
    instructions: "instructions"
    callback_url: "https://some-url.com/params"
    edit_url: "https://edit-url/params"
    uninstall_hook: "https://uninstall-url/params"
    svg: "icon.svg"
    screenshot1: "screenshot1.png"
    screenshot2: "screenshot2.png"
    screenshot3: "screenshot3.png"

That gives now :

<Response [400]>

{'error': {'message': {'svg': ['The submitted data was not a file. Check the encoding type on the form.'], 'screenshot1': ['The submitted data was not a file. Check the encoding type on the form.'], 'screenshot2': ['The submitted data was not a file. Check the encoding type on the form.'], 'screenshot3': ['The submitted data was not a file. Check the encoding type on the form.']}, 'code': 400}}

I've done multiple searches (1 , 2 , 3 , 4 , 5...) but their application and few other errors, eventually get me to this :

import base64
import json
from pathlib import Path

import requests
import yaml

from requests_toolbelt.multipart.encoder import MultipartEncoder

BASE_URL = "https://url.com"  # API Host
FILE_FOLDER = Path.cwd()  # Current working directory


if __name__ == "__main__":

    public_key = <public_key>
    private_key = <private_key>

    auth_key = "{}:{}".format(public_key, private_key).encode("utf-8")

    encodedKey = base64.b64encode(auth_key).decode("utf-8")

    def update_app_info():
        infos_file = FILE_FOLDER / "file.yaml"
        with open(infos_file) as infos_file_data:
            yamlcontent = yaml.safe_load(infos_file_data)  # Parse file.yaml and produce a dictionary of it
        file_infos = yamlcontent["index"][0]  # retrieve actual configuration informations
        m = MultipartEncoder(fields=file_infos)
        #print(m.content_type)
        headers = {
            "Authorization": f"Basic {encodedKey}",
            "Content-Type": m.content_type,
        }

        response = requests.put(
            f"{BASE_URL}/path/to/api_endpoint/{public_key}",
            data=json.dumps(file_infos),
            headers=headers
        )
        print(response)
        print(response.json())

    update_app_info()

That is also giving me the 202 success response but the file svg, screenshot1, screenshot2 & screenshot3 fields are not updated.

I'll share more informations where needed. Your help is very welcome.


Solution

  • I've got additional resources that helped.

    As I was trying to solve my issue, I found this. It happens I didn't wrote files part as it should, plus I was having data as a JSON string. That causes a ValueError: Data must not be a string. error. This was useful to get it fixed.

    Now, for what it's worth, here's the working script :

    import base64
    from pathlib import Path
    
    import requests
    import yaml
    
    
    BASE_URL = "https://url.com"  # API Host
    FILE_FOLDER = Path.cwd()  # Current working directory
    
    
    if __name__ == "__main__":
    
        public_key = <public_key>
        private_key = <private_key>
    
        auth_key = "{}:{}".format(public_key, private_key).encode("utf-8")
        encodedKey = base64.b64encode(auth_key).decode("utf-8")
    
        def update_app_info():
            infos_file = FILE_FOLDER / "file.yaml"
            with open(infos_file) as infos_file_data:
                yamlcontent = yaml.safe_load(infos_file_data)  # Parse file.yaml and produce a dictionary of it
            if "index" in yamlcontent:
                file_infos = yamlcontent["index"][0]  # retrieve actual configuration informations
                headers = {
                    "Authorization": f"Basic {encodedKey}",
                }
                files = {
                    "svg": open("icon.svg", "rb"),
                    "screenshot1": open("screenshot1.png", "rb"),
                    "screenshot2": open("screenshot2.png", "rb"),
                    "screenshot3": open("screenshot3.png", "rb"),
                }
                response = requests.put(
                    f"{BASE_URL}/path/to/api_endpoint/{public_key}", data=file_infos, files=files, headers=headers
                )
                print("\n", response)
                print("\n", response.headers)
                print("\n", response.json())
    
        update_app_info()