Repository: burnash/gspread

Issue:
spreadsheet.lastUpdateTime throws KeyError exception
**Describe the bug**
I tried using the `spreadsheet.lastUpdateTime` property and it throws a `KeyError` exception because it cannot find the `modifiedTime` attribute. I enabled the **Google Sheet API** and the **Google Drive API** and my credentials are correct.

**To reproduce**
Steps to reproduce the behavior:
1. Create a Google Sheet document
2. Make sure to enable the Google Sheet and the Google Drive APIs from the Google Cloud Console
3. Edit your spreadsheet
4. Try to retrieve the `modifiedTime` attribute using `spreadsheet.lastUpdateTime`

**Expected behavior**
I should be able to retrieve the `modifiedTime` attribute without any errors.

**Code example**
```python
import gspread

spreadsheetId = "<SPREADSHEET_ID>"

client = gspread.service_account(filename="credentials.json")
spreadsheet = client.open_by_key(spreadsheetId)
worksheet = spreadsheet.sheet1
worksheet.update_acell("A1", "a")
last_edit = spreadsheet.lastUpdateTime  # This throws a KeyError exception
```

**Environment info**
- Operating System: _Windows 10_
- Python version: _3.11_
- gspread version: _5.7.2_

**Stack trace or other output that would be helpful**
```
Traceback (most recent call last):
  File "C:\Python311\Lib\site-packages\gspread\spreadsheet.py", line 71, in lastUpdateTime
    return self._properties["modifiedTime"]
           ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
KeyError: 'modifiedTime'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:\python-projects\gsheet-test\test.py", line 9, in <module>
    last_edit = spreadsheet.lastUpdateTime
                ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\site-packages\gspread\spreadsheet.py", line 75, in lastUpdateTime
    metadata = finditem(
               ^^^^^^^^^
  File "C:\Python311\Lib\site-packages\gspread\utils.py", line 133, in finditem
    return next(item for item in seq if func(item))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
StopIteration
```

**Additional context**
If I use the `google-api-python-client` package, it works as expected without any issues:
```python
from google.oauth2 import service_account
from googleapiclient.discovery import build

# Authenticate
creds = service_account.Credentials.from_service_account_file("credentials.json")
service = build("drive", "v3", credentials=creds)

# Get the last edit time
last_edit = service.files().get(fileId="<SPREADSHEET_ID>", fields="modifiedTime").execute()["modifiedTime"]
```


Discussion:

Patch:
diff --git a/gspread/client.py b/gspread/client.py
index 3f2e8e6..8d899cf 100644
--- a/gspread/client.py
+++ b/gspread/client.py
@@ -91,6 +91,24 @@ class Client:
         else:
             raise APIError(response)
 
+    def _get_file_drive_metadata(self, id):
+        """Get the metadata from the Drive API for a specific file
+        This method is mainly here to retrieve the create/update time
+        of a file (these metadata are only accessible from the Drive API).
+        """
+
+        url = DRIVE_FILES_API_V3_URL + "/{}".format(id)
+
+        params = {
+            "supportsAllDrives": True,
+            "includeItemsFromAllDrives": True,
+            "fields": "id,name,createdTime,modifiedTime",
+        }
+
+        res = self.request("get", url, params=params)
+
+        return res.json()
+
     def list_spreadsheet_files(self, title=None, folder_id=None):
         """List all the spreadsheet files
 
diff --git a/gspread/spreadsheet.py b/gspread/spreadsheet.py
index 5d5a2bb..d941403 100644
--- a/gspread/spreadsheet.py
+++ b/gspread/spreadsheet.py
@@ -55,12 +55,7 @@ class Spreadsheet:
         try:
             return self._properties["createdTime"]
         except KeyError:
-            # Filter the list using the name to reduce the request size
-            # Filter the item using the unique ID to ensure we update the exacte same item
-            metadata = finditem(
-                lambda x: x["id"] == self.id,
-                self.client.list_spreadsheet_files(self.title),
-            )
+            metadata = self.client._get_file_drive_metadata(self.id)
             self._properties.update(metadata)
             return self._properties["createdTime"]
 
@@ -70,12 +65,7 @@ class Spreadsheet:
         try:
             return self._properties["modifiedTime"]
         except KeyError:
-            # Filter the list using the name to reduce the request size
-            # Filter the item using the unique ID to ensure we update the exacte same item
-            metadata = finditem(
-                lambda x: x["id"] == self.id,
-                self.client.list_spreadsheet_files(self.title),
-            )
+            metadata = self.client._get_file_drive_metadata(self.id)
             self._properties.update(metadata)
             return self._properties["modifiedTime"]
 


Test patch:
diff --git a/test-requirements.txt b/test-requirements.txt
index c485a7f..35d7e8c 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -3,3 +3,4 @@ google-auth-oauthlib>=0.4.1
 vcrpy
 pytest
 pytest-vcr
+urllib3==1.26.15
diff --git a/tests/cassettes/SpreadsheetTest.test_get_updated_time.json b/tests/cassettes/SpreadsheetTest.test_get_updated_time.json
new file mode 100644
index 0000000..d5427af
--- /dev/null
+++ b/tests/cassettes/SpreadsheetTest.test_get_updated_time.json
@@ -0,0 +1,303 @@
+{
+    "version": 1,
+    "interactions": [
+        {
+            "request": {
+                "method": "POST",
+                "uri": "https://www.googleapis.com/drive/v3/files?supportsAllDrives=True",
+                "body": "{\"name\": \"Test SpreadsheetTest test_get_updated_time\", \"mimeType\": \"application/vnd.google-apps.spreadsheet\"}",
+                "headers": {
+                    "User-Agent": [
+                        "python-requests/2.29.0"
+                    ],
+                    "Accept-Encoding": [
+                        "gzip, deflate"
+                    ],
+                    "Accept": [
+                        "*/*"
+                    ],
+                    "Connection": [
+                        "keep-alive"
+                    ],
+                    "Content-Length": [
+                        "109"
+                    ],
+                    "Content-Type": [
+                        "application/json"
+                    ],
+                    "authorization": [
+                        "<ACCESS_TOKEN>"
+                    ]
+                }
+            },
+            "response": {
+                "status": {
+                    "code": 200,
+                    "message": "OK"
+                },
+                "headers": {
+                    "Cache-Control": [
+                        "no-cache, no-store, max-age=0, must-revalidate"
+                    ],
+                    "Date": [
+                        "Thu, 04 May 2023 14:22:27 GMT"
+                    ],
+                    "Alt-Svc": [
+                        "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"
+                    ],
+                    "X-Content-Type-Options": [
+                        "nosniff"
+                    ],
+                    "Transfer-Encoding": [
+                        "chunked"
+                    ],
+                    "Server": [
+                        "ESF"
+                    ],
+                    "Pragma": [
+                        "no-cache"
+                    ],
+                    "X-XSS-Protection": [
+                        "0"
+                    ],
+                    "X-Frame-Options": [
+                        "SAMEORIGIN"
+                    ],
+                    "Expires": [
+                        "Mon, 01 Jan 1990 00:00:00 GMT"
+                    ],
+                    "Vary": [
+                        "Origin, X-Origin"
+                    ],
+                    "Content-Type": [
+                        "application/json; charset=UTF-8"
+                    ],
+                    "content-length": [
+                        "196"
+                    ]
+                },
+                "body": {
+                    "string": "{\n  \"kind\": \"drive#file\",\n  \"id\": \"1GxGLTZYypNjmhSji2Wto99rAoCz0KqweztppVnhBK8c\",\n  \"name\": \"Test SpreadsheetTest test_get_updated_time\",\n  \"mimeType\": \"application/vnd.google-apps.spreadsheet\"\n}\n"
+                }
+            }
+        },
+        {
+            "request": {
+                "method": "GET",
+                "uri": "https://sheets.googleapis.com/v4/spreadsheets/1GxGLTZYypNjmhSji2Wto99rAoCz0KqweztppVnhBK8c?includeGridData=false",
+                "body": null,
+                "headers": {
+                    "User-Agent": [
+                        "python-requests/2.29.0"
+                    ],
+                    "Accept-Encoding": [
+                        "gzip, deflate"
+                    ],
+                    "Accept": [
+                        "*/*"
+                    ],
+                    "Connection": [
+                        "keep-alive"
+                    ],
+                    "authorization": [
+                        "<ACCESS_TOKEN>"
+                    ]
+                }
+            },
+            "response": {
+                "status": {
+                    "code": 200,
+                    "message": "OK"
+                },
+                "headers": {
+                    "Cache-Control": [
+                        "private"
+                    ],
+                    "Date": [
+                        "Thu, 04 May 2023 14:22:27 GMT"
+                    ],
+                    "Alt-Svc": [
+                        "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"
+                    ],
+                    "X-Content-Type-Options": [
+                        "nosniff"
+                    ],
+                    "Transfer-Encoding": [
+                        "chunked"
+                    ],
+                    "Server": [
+                        "ESF"
+                    ],
+                    "X-XSS-Protection": [
+                        "0"
+                    ],
+                    "X-Frame-Options": [
+                        "SAMEORIGIN"
+                    ],
+                    "Vary": [
+                        "Origin",
+                        "X-Origin",
+                        "Referer"
+                    ],
+                    "Content-Type": [
+                        "application/json; charset=UTF-8"
+                    ],
+                    "content-length": [
+                        "3340"
+                    ]
+                },
+                "body": {
+                    "string": "{\n  \"spreadsheetId\": \"1GxGLTZYypNjmhSji2Wto99rAoCz0KqweztppVnhBK8c\",\n  \"properties\": {\n    \"title\": \"Test SpreadsheetTest test_get_updated_time\",\n    \"locale\": \"en_US\",\n    \"autoRecalc\": \"ON_CHANGE\",\n    \"timeZone\": \"Etc/GMT\",\n    \"defaultFormat\": {\n      \"backgroundColor\": {\n        \"red\": 1,\n        \"green\": 1,\n        \"blue\": 1\n      },\n      \"padding\": {\n        \"top\": 2,\n        \"right\": 3,\n        \"bottom\": 2,\n        \"left\": 3\n      },\n      \"verticalAlignment\": \"BOTTOM\",\n      \"wrapStrategy\": \"OVERFLOW_CELL\",\n      \"textFormat\": {\n        \"foregroundColor\": {},\n        \"fontFamily\": \"arial,sans,sans-serif\",\n        \"fontSize\": 10,\n        \"bold\": false,\n        \"italic\": false,\n        \"strikethrough\": false,\n        \"underline\": false,\n        \"foregroundColorStyle\": {\n          \"rgbColor\": {}\n        }\n      },\n      \"backgroundColorStyle\": {\n        \"rgbColor\": {\n          \"red\": 1,\n          \"green\": 1,\n          \"blue\": 1\n        }\n      }\n    },\n    \"spreadsheetTheme\": {\n      \"primaryFontFamily\": \"Arial\",\n      \"themeColors\": [\n        {\n          \"colorType\": \"TEXT\",\n          \"color\": {\n            \"rgbColor\": {}\n          }\n        },\n        {\n          \"colorType\": \"BACKGROUND\",\n          \"color\": {\n            \"rgbColor\": {\n              \"red\": 1,\n              \"green\": 1,\n              \"blue\": 1\n            }\n          }\n        },\n        {\n          \"colorType\": \"ACCENT1\",\n          \"color\": {\n            \"rgbColor\": {\n              \"red\": 0.25882354,\n              \"green\": 0.52156866,\n              \"blue\": 0.95686275\n            }\n          }\n        },\n        {\n          \"colorType\": \"ACCENT2\",\n          \"color\": {\n            \"rgbColor\": {\n              \"red\": 0.91764706,\n              \"green\": 0.2627451,\n              \"blue\": 0.20784314\n            }\n          }\n        },\n        {\n          \"colorType\": \"ACCENT3\",\n          \"color\": {\n            \"rgbColor\": {\n              \"red\": 0.9843137,\n              \"green\": 0.7372549,\n              \"blue\": 0.015686275\n            }\n          }\n        },\n        {\n          \"colorType\": \"ACCENT4\",\n          \"color\": {\n            \"rgbColor\": {\n              \"red\": 0.20392157,\n              \"green\": 0.65882355,\n              \"blue\": 0.3254902\n            }\n          }\n        },\n        {\n          \"colorType\": \"ACCENT5\",\n          \"color\": {\n            \"rgbColor\": {\n              \"red\": 1,\n              \"green\": 0.42745098,\n              \"blue\": 0.003921569\n            }\n          }\n        },\n        {\n          \"colorType\": \"ACCENT6\",\n          \"color\": {\n            \"rgbColor\": {\n              \"red\": 0.27450982,\n              \"green\": 0.7411765,\n              \"blue\": 0.7764706\n            }\n          }\n        },\n        {\n          \"colorType\": \"LINK\",\n          \"color\": {\n            \"rgbColor\": {\n              \"red\": 0.06666667,\n              \"green\": 0.33333334,\n              \"blue\": 0.8\n            }\n          }\n        }\n      ]\n    }\n  },\n  \"sheets\": [\n    {\n      \"properties\": {\n        \"sheetId\": 0,\n        \"title\": \"Sheet1\",\n        \"index\": 0,\n        \"sheetType\": \"GRID\",\n        \"gridProperties\": {\n          \"rowCount\": 1000,\n          \"columnCount\": 26\n        }\n      }\n    }\n  ],\n  \"spreadsheetUrl\": \"https://docs.google.com/spreadsheets/d/1GxGLTZYypNjmhSji2Wto99rAoCz0KqweztppVnhBK8c/edit\"\n}\n"
+                }
+            }
+        },
+        {
+            "request": {
+                "method": "GET",
+                "uri": "https://www.googleapis.com/drive/v3/files/1GxGLTZYypNjmhSji2Wto99rAoCz0KqweztppVnhBK8c?supportsAllDrives=True&includeItemsFromAllDrives=True&fields=id%2Cname%2CcreatedTime%2CmodifiedTime",
+                "body": null,
+                "headers": {
+                    "User-Agent": [
+                        "python-requests/2.29.0"
+                    ],
+                    "Accept-Encoding": [
+                        "gzip, deflate"
+                    ],
+                    "Accept": [
+                        "*/*"
+                    ],
+                    "Connection": [
+                        "keep-alive"
+                    ],
+                    "authorization": [
+                        "<ACCESS_TOKEN>"
+                    ]
+                }
+            },
+            "response": {
+                "status": {
+                    "code": 200,
+                    "message": "OK"
+                },
+                "headers": {
+                    "Cache-Control": [
+                        "no-cache, no-store, max-age=0, must-revalidate"
+                    ],
+                    "Date": [
+                        "Thu, 04 May 2023 14:22:27 GMT"
+                    ],
+                    "Alt-Svc": [
+                        "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"
+                    ],
+                    "X-Content-Type-Options": [
+                        "nosniff"
+                    ],
+                    "Transfer-Encoding": [
+                        "chunked"
+                    ],
+                    "Server": [
+                        "ESF"
+                    ],
+                    "Pragma": [
+                        "no-cache"
+                    ],
+                    "X-XSS-Protection": [
+                        "0"
+                    ],
+                    "X-Frame-Options": [
+                        "SAMEORIGIN"
+                    ],
+                    "Expires": [
+                        "Mon, 01 Jan 1990 00:00:00 GMT"
+                    ],
+                    "Vary": [
+                        "Origin, X-Origin"
+                    ],
+                    "Content-Type": [
+                        "application/json; charset=UTF-8"
+                    ],
+                    "content-length": [
+                        "206"
+                    ]
+                },
+                "body": {
+                    "string": "{\n  \"id\": \"1GxGLTZYypNjmhSji2Wto99rAoCz0KqweztppVnhBK8c\",\n  \"name\": \"Test SpreadsheetTest test_get_updated_time\",\n  \"createdTime\": \"2023-05-04T14:22:24.809Z\",\n  \"modifiedTime\": \"2023-05-04T14:22:24.826Z\"\n}\n"
+                }
+            }
+        },
+        {
+            "request": {
+                "method": "DELETE",
+                "uri": "https://www.googleapis.com/drive/v3/files/1GxGLTZYypNjmhSji2Wto99rAoCz0KqweztppVnhBK8c?supportsAllDrives=True",
+                "body": null,
+                "headers": {
+                    "User-Agent": [
+                        "python-requests/2.29.0"
+                    ],
+                    "Accept-Encoding": [
+                        "gzip, deflate"
+                    ],
+                    "Accept": [
+                        "*/*"
+                    ],
+                    "Connection": [
+                        "keep-alive"
+                    ],
+                    "Content-Length": [
+                        "0"
+                    ],
+                    "authorization": [
+                        "<ACCESS_TOKEN>"
+                    ]
+                }
+            },
+            "response": {
+                "status": {
+                    "code": 204,
+                    "message": "No Content"
+                },
+                "headers": {
+                    "Cache-Control": [
+                        "no-cache, no-store, max-age=0, must-revalidate"
+                    ],
+                    "Date": [
+                        "Thu, 04 May 2023 14:22:28 GMT"
+                    ],
+                    "Alt-Svc": [
+                        "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"
+                    ],
+                    "X-Content-Type-Options": [
+                        "nosniff"
+                    ],
+                    "Server": [
+                        "ESF"
+                    ],
+                    "Content-Length": [
+                        "0"
+                    ],
+                    "Pragma": [
+                        "no-cache"
+                    ],
+                    "X-XSS-Protection": [
+                        "0"
+                    ],
+                    "X-Frame-Options": [
+                        "SAMEORIGIN"
+                    ],
+                    "Expires": [
+                        "Mon, 01 Jan 1990 00:00:00 GMT"
+                    ],
+                    "Vary": [
+                        "Origin, X-Origin"
+                    ],
+                    "Content-Type": [
+                        "text/html"
+                    ]
+                },
+                "body": {
+                    "string": ""
+                }
+            }
+        }
+    ]
+}
diff --git a/tests/spreadsheet_test.py b/tests/spreadsheet_test.py
index ecbc2bf..19c87a0 100644
--- a/tests/spreadsheet_test.py
+++ b/tests/spreadsheet_test.py
@@ -161,3 +161,17 @@ class SpreadsheetTest(GspreadTest):
 
         self.assertNotEqual(prev_title, properties["title"])
         self.assertEqual(new_title, properties["title"])
+
+    @pytest.mark.vcr()
+    def test_get_updated_time(self):
+        current_metadata = self.spreadsheet._properties
+        self.assertNotIn("modifiedTime", self.spreadsheet._properties)
+
+        res = self.spreadsheet.lastUpdateTime
+
+        self.assertIsNotNone(res)
+
+        new_metadata = self.spreadsheet._properties
+        self.assertIn("modifiedTime", self.spreadsheet._properties)
+
+        self.assertDictContainsSubset(current_metadata, new_metadata)