Răsfoiți Sursa

[aws][raz] Fix S3 RAZ client parameter passing bug causing AttributeError (#4280)

Fix incorrect positional argument passing when calling RAZ client from S3 connection layer,
which caused 'str' object has no attribute 'items' error in new storage browser with RAZ enabled.

Problem:
- When accessing S3 browser with RAZ enabled (new storage browser on, new storage connector off),
  requests failed with: AttributeError: 'str' object has no attribute 'items'
- Error occurred in raz_client.py when trying to call headers.items() on line 253

Root Cause:
- In s3connection.py line 145, get_signed_url() was calling raz_client.get_url() with 
  positional arguments in wrong order
- The call: raz_client.get_url(action, url, headers, data)
- Expected signature: get_url(action='GET', path=None, params=None, headers=None, data=None)
- This caused parameters to be misaligned:
  * headers was passed to params parameter
  * data (empty string '') was passed to headers parameter
- When headers received empty string instead of dict, the .items() call failed

Solution:
1. Primary fix (s3connection.py):
   - Changed from positional to keyword arguments to ensure correct parameter mapping
   - Now: raz_client.get_url(action=action, path=url, headers=headers, data=data)

2. Defensive fix (raz_client.py):
   - Added type checking to ensure params and headers are always dicts
   - Prevents similar issues from other code paths
   - Changes: params/headers = value if value and isinstance(value, dict) else {}

Testing:
- Reproduces the exact error from live setup logs
- Fix resolves S3 browser access with RAZ enabled in test setup
- No impact on non-RAZ flows or new boto3 client (works via different code paths)
Harsh Gupta 1 lună în urmă
părinte
comite
6b10e29dbf

+ 3 - 5
desktop/core/src/desktop/lib/raz/raz_client.py

@@ -16,11 +16,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import sys
-import json
-import uuid
 import base64
 import logging
+import uuid
 from urllib.parse import unquote as lib_urlunquote, urlparse as lib_urlparse
 
 import requests
@@ -63,8 +61,8 @@ class RazClient(object):
 
     path = lib_urlparse(url)
     url_params = dict([p.split('=') if '=' in p else (p, '') for p in path.query.split('&') if path.query])  # ?delete, ?prefix=/hue
-    params = params if params is not None else {}
-    headers = headers if headers is not None else {}
+    params = params if params and isinstance(params, dict) else {}
+    headers = headers if headers and isinstance(headers, dict) else {}
 
     endpoint = "%s://%s" % (path.scheme, path.netloc)
     resource_path = path.path.lstrip("/")

+ 1 - 8
desktop/libs/aws/src/aws/s3/s3connection.py

@@ -17,15 +17,8 @@
 import logging
 from urllib.parse import parse_qs, unquote, urlencode
 
-import boto
-from boto.connection import HTTPRequest
-from boto.exception import BotoClientError
-from boto.regioninfo import connect
-from boto.resultset import ResultSet
-from boto.s3 import S3RegionInfo
 from boto.s3.bucket import Bucket, Key
 from boto.s3.connection import NoHostProvided, S3Connection
-from boto.s3.prefix import Prefix
 
 from desktop.conf import RAZ
 from desktop.lib.raz.clients import S3RazClient
@@ -142,7 +135,7 @@ class RazS3Connection(S3Connection):
   def get_signed_url(self, action='GET', url=None, headers=None, data=None):
     raz_client = S3RazClient(username=self.username)
 
-    return raz_client.get_url(action, url, headers, data)
+    return raz_client.get_url(action=action, path=url, headers=headers, data=data)
 
   def _required_auth_capability(self):
     """