diff --git a/gspread/client.py b/gspread/client.py index 1a07bcb8a..039bb9a00 100644 --- a/gspread/client.py +++ b/gspread/client.py @@ -327,26 +327,30 @@ def copy( permissions = original.list_permissions() for p in permissions: - if p.get("deleted"): + # Share the new spreadsheet with the same permissions as the original + # but don't share it with the owner of the original spreadsheet as + # Google API rejects that with the error response: + # APIError: [403]: The transferOwnership parameter must be enabled when the permission role is 'owner'. + if p.get("deleted") or p.get("role") == "owner": continue - # .list_permissions() returns a list of permissions, - # even the folder permissions if the file is in a shared folder. - # We only want the permissions that are directly applied to the - # spreadsheet file, i.e. 'writer', 'commenter' and 'reader'. - perm_details = { - p_details.get("permissionType"): p_details.get("inherited") - for p_details in p.get("permissionDetails") - } - if p.get("role") in ("organizer", "fileOrganizer") and ( - perm_details.get("file") or perm_details.get("member") - ): - continue + # .list_permissions() when used in shared drive returns a list + # of permissions, even the folder permissions if the file is in + # a shared folder. We only want the permissions that are directly + # applied to the spreadsheet file, i.e. 'writer', 'commenter' and 'reader'. + if p.get("permissionDetails"): + perm_details = { + p_details.get("permissionType"): p_details.get("inherited") + for p_details in p.get("permissionDetails") + } + if p.get("role") in ("organizer", "fileOrganizer") and ( + perm_details.get("file") or perm_details.get("member") + ): + continue # In case of domain type the domain extract the domain # In case of user/group extract the emailAddress # Otherwise use None for type 'Anyone' - email_or_domain = "" if str(p["type"]) == "domain": email_or_domain = str(p["domain"]) diff --git a/gspread/spreadsheet.py b/gspread/spreadsheet.py index 236347258..d9b7255cd 100644 --- a/gspread/spreadsheet.py +++ b/gspread/spreadsheet.py @@ -543,7 +543,7 @@ def export(self, format: ExportFormat = ExportFormat.PDF) -> bytes: """ return self.client.export(self.id, format) - def list_permissions(self) -> List[Dict[str, Union[str, bool]]]: + def list_permissions(self) -> List[Dict[str, Union[str, bool, List[Dict[str, Union[str, bool]]]]]]: """Lists the spreadsheet's permissions.""" return self.client.list_permissions(self.id) diff --git a/tests/client_test.py b/tests/client_test.py index b27cd49a5..9d2edf00a 100644 --- a/tests/client_test.py +++ b/tests/client_test.py @@ -72,6 +72,16 @@ def test_copy(self): copy_metadata = spreadsheet_copy.fetch_sheet_metadata() self.assertEqual(original_metadata["sheets"], copy_metadata["sheets"]) + @pytest.mark.vcr() + def test_copy_in_shared_drive(self): + original_spreadsheet = self.spreadsheet + spreadsheet_copy = self.gc.copy(original_spreadsheet.id, copy_permissions=True) + self.assertIsInstance(spreadsheet_copy, gspread.Spreadsheet) + + original_metadata = original_spreadsheet.fetch_sheet_metadata() + copy_metadata = spreadsheet_copy.fetch_sheet_metadata() + self.assertEqual(original_metadata["sheets"], copy_metadata["sheets"]) + @pytest.mark.vcr() def test_import_csv(self): spreadsheet = self.spreadsheet