diff --git a/synapseclient/models/curation.py b/synapseclient/models/curation.py index 54ee59c3c..b8db5e105 100644 --- a/synapseclient/models/curation.py +++ b/synapseclient/models/curation.py @@ -3350,6 +3350,9 @@ def fill_grid_session_from_response(self, grid_session: "Grid") -> "Grid": grid_session.last_replica_id_service = data.get("lastReplicaIdService", None) grid_session.grid_json_schema_id = data.get("gridJsonSchema$Id", None) grid_session.source_entity_id = data.get("sourceEntityId", None) + grid_session.source_entity_version_number = data.get( + "sourceEntityVersionNumber", None + ) owner_principal_id = data.get("ownerPrincipalId") grid_session.owner_principal_id = ( int(owner_principal_id) if owner_principal_id is not None else None @@ -4530,6 +4533,8 @@ class GridSession: last_replica_id_service: The last replica ID issued to a service grid_json_schema_id: The $id of the JSON schema used for model validation source_entity_id: The synId of the table/view/csv that this grid was cloned from + source_entity_version_number: The version number of the source entity + that this grid was cloned from """ session_id: Optional[str] = None @@ -4559,6 +4564,9 @@ class GridSession: source_entity_id: Optional[str] = None """The synId of the table/view/csv that this grid was cloned from""" + source_entity_version_number: Optional[int] = None + """The version number of the source entity that this grid was cloned from""" + def fill_from_dict(self, synapse_response: Dict[str, Any]) -> "GridSession": """ Converts a response from the REST API into this dataclass. @@ -4580,6 +4588,9 @@ def fill_from_dict(self, synapse_response: Dict[str, Any]) -> "GridSession": ) self.grid_json_schema_id = synapse_response.get("gridJsonSchema$Id", None) self.source_entity_id = synapse_response.get("sourceEntityId", None) + self.source_entity_version_number = synapse_response.get( + "sourceEntityVersionNumber", None + ) return self @@ -5343,6 +5354,8 @@ class Grid(EnumCoercionMixin, GridSynchronousProtocol): last_replica_id_service: The last replica ID issued to a service grid_json_schema_id: The $id of the JSON schema used for model validation source_entity_id: The synId of the table/view/csv that this grid was cloned from + source_entity_version_number: The version number of the source entity + that this grid was cloned from record_set_version_number: The version number of the exported record set validation_summary_statistics: Summary statistics for validation results @@ -5450,6 +5463,9 @@ class Grid(EnumCoercionMixin, GridSynchronousProtocol): source_entity_id: Optional[str] = None """The synId of the table/view/csv that this grid was cloned from""" + source_entity_version_number: Optional[int] = None + """The version number of the source entity that this grid was cloned from""" + record_set_version_number: Optional[int] = None """The version number of the exported record set""" @@ -5540,6 +5556,9 @@ async def main(): self.last_replica_id_service = existing_session.last_replica_id_service self.grid_json_schema_id = existing_session.grid_json_schema_id self.source_entity_id = existing_session.source_entity_id + self.source_entity_version_number = ( + existing_session.source_entity_version_number + ) return self # No existing session found, create a new one @@ -5695,6 +5714,9 @@ def fill_from_dict(self, synapse_response: Dict[str, Any]) -> "Grid": ) self.grid_json_schema_id = synapse_response.get("gridJsonSchema$Id", None) self.source_entity_id = synapse_response.get("sourceEntityId", None) + self.source_entity_version_number = synapse_response.get( + "sourceEntityVersionNumber", None + ) owner_principal_id = synapse_response.get("ownerPrincipalId") self.owner_principal_id = ( int(owner_principal_id) if owner_principal_id is not None else None diff --git a/tests/integration/synapseclient/models/async/test_grid_async.py b/tests/integration/synapseclient/models/async/test_grid_async.py index 0f787dc7f..7321f3035 100644 --- a/tests/integration/synapseclient/models/async/test_grid_async.py +++ b/tests/integration/synapseclient/models/async/test_grid_async.py @@ -135,6 +135,10 @@ async def test_create_and_list_grid_sessions_async( assert created_grid.started_on is not None assert created_grid.etag is not None assert created_grid.source_entity_id == record_set_fixture.id + assert ( + created_grid.source_entity_version_number + == record_set_fixture.version_number + ) # WHEN: Listing grid sessions sessions = [] @@ -156,6 +160,10 @@ async def test_create_and_list_grid_sessions_async( ) assert our_session.started_by == created_grid.started_by assert our_session.source_entity_id == record_set_fixture.id + assert ( + our_session.source_entity_version_number + == record_set_fixture.version_number + ) async def test_get_grid_session_async(self, record_set_fixture: RecordSet) -> None: # GIVEN: A grid session created from a record set @@ -176,6 +184,10 @@ async def test_get_grid_session_async(self, record_set_fixture: RecordSet) -> No assert fetched_grid.started_on == created_grid.started_on assert fetched_grid.etag == created_grid.etag assert fetched_grid.source_entity_id == record_set_fixture.id + assert ( + fetched_grid.source_entity_version_number + == record_set_fixture.version_number + ) async def test_create_grid_session_with_authorization_mode_async( self, record_set_fixture: RecordSet @@ -198,6 +210,10 @@ async def test_create_grid_session_with_authorization_mode_async( assert created_grid is grid assert created_grid.session_id is not None assert created_grid.source_entity_id == record_set_fixture.id + assert ( + created_grid.source_entity_version_number + == record_set_fixture.version_number + ) assert created_grid.authorization_mode == AuthorizationMode.SOURCE_BENEFACTOR async def test_create_grid_session_and_reuse_session_async( @@ -231,6 +247,10 @@ async def test_create_grid_session_and_reuse_session_async( assert created_grid2.started_by == created_grid1.started_by assert created_grid2.started_on == created_grid1.started_on assert created_grid2.source_entity_id == record_set_fixture.id + assert ( + created_grid2.source_entity_version_number + == record_set_fixture.version_number + ) async def test_create_grid_session_validation_error_async(self) -> None: # GIVEN: A Grid instance with no record_set_id or initial_query @@ -328,6 +348,9 @@ async def file_indexed() -> bool: # THEN: The session ID is unchanged assert synced_grid.session_id == created_grid.session_id assert synced_grid.source_entity_id == ev.id + # AND the server currently omits the version for an EntityView source; + # update this assert if the server starts returning one + assert synced_grid.source_entity_version_number is None # AND: The downloaded CSV reflects the newly uploaded file dest = tempfile.mkdtemp() @@ -359,6 +382,10 @@ async def test_synchronize_grid_recordset_async( # THEN: The session ID is unchanged and the source entity is still the RecordSet assert synced_grid.session_id == created_grid.session_id assert synced_grid.source_entity_id == record_set_fixture.id + assert ( + synced_grid.source_entity_version_number + == record_set_fixture.version_number + ) async def test_import_csv_to_grid_session_async( self, diff --git a/tests/unit/synapseclient/models/async/unit_test_curation_async.py b/tests/unit/synapseclient/models/async/unit_test_curation_async.py index 095391e89..588f02925 100644 --- a/tests/unit/synapseclient/models/async/unit_test_curation_async.py +++ b/tests/unit/synapseclient/models/async/unit_test_curation_async.py @@ -97,6 +97,7 @@ RECORD_SET_ID = "syn3456789" SESSION_ID = "session-abc-123" SOURCE_ENTITY_ID = "syn5555555" +SOURCE_ENTITY_VERSION_NUMBER = 13 GRID_ETAG = "grid-etag-456" STARTED_BY = "user-1" STARTED_ON = "2024-03-01T00:00:00.000Z" @@ -167,6 +168,7 @@ def _get_grid_session_response(): "lastReplicaIdService": -5, "gridJsonSchema$Id": "my-schema-id", "sourceEntityId": SOURCE_ENTITY_ID, + "sourceEntityVersionNumber": SOURCE_ENTITY_VERSION_NUMBER, # The server returns ownerPrincipalId as a string; the client coerces to int. "ownerPrincipalId": str(OWNER_PRINCIPAL_ID), "authorizationMode": "SESSION_OWNER", @@ -2807,6 +2809,7 @@ def test_fill_from_dict(self) -> None: assert grid.last_replica_id_service == -5 assert grid.grid_json_schema_id == "my-schema-id" assert grid.source_entity_id == SOURCE_ENTITY_ID + assert grid.source_entity_version_number == SOURCE_ENTITY_VERSION_NUMBER # AND the owner principal id is coerced from the response string to an int assert grid.owner_principal_id == OWNER_PRINCIPAL_ID assert isinstance(grid.owner_principal_id, int) @@ -2838,6 +2841,7 @@ async def test_create_async_with_record_set_id(self) -> None: assert result.started_by == STARTED_BY assert result.started_on == STARTED_ON assert result.source_entity_id == SOURCE_ENTITY_ID + assert result.source_entity_version_number == SOURCE_ENTITY_VERSION_NUMBER assert result.authorization_mode == AuthorizationMode.SESSION_OWNER async def test_create_async_forwards_authorization_mode_to_request(self) -> None: @@ -2900,6 +2904,7 @@ async def mock_list_async(*args, **kwargs): assert result.session_id == SESSION_ID assert result.started_by == STARTED_BY assert result.source_entity_id == SOURCE_ENTITY_ID + assert result.source_entity_version_number == SOURCE_ENTITY_VERSION_NUMBER async def test_create_async_attach_to_previous_no_existing(self) -> None: # GIVEN a Grid with a record_set_id @@ -2935,6 +2940,7 @@ async def mock_list_async(*args, **kwargs): # THEN a new grid session should be created assert result.session_id == SESSION_ID + assert result.source_entity_version_number == SOURCE_ENTITY_VERSION_NUMBER async def test_export_to_record_set_async(self) -> None: # GIVEN a Grid with a session_id @@ -3013,6 +3019,7 @@ async def test_list_async(self) -> None: "lastReplicaIdService": -10, "gridJsonSchema$Id": None, "sourceEntityId": "syn6666666", + "sourceEntityVersionNumber": 2, } async def mock_list(*args, **kwargs): @@ -3032,8 +3039,12 @@ async def mock_list(*args, **kwargs): assert len(results) == 2 assert results[0].session_id == SESSION_ID assert results[0].source_entity_id == SOURCE_ENTITY_ID + assert ( + results[0].source_entity_version_number == SOURCE_ENTITY_VERSION_NUMBER + ) assert results[1].session_id == "session-xyz-999" assert results[1].source_entity_id == "syn6666666" + assert results[1].source_entity_version_number == 2 async def test_list_async_with_source_id(self) -> None: # GIVEN mock API responses filtered by source_id @@ -3244,6 +3255,7 @@ def test_fill_grid_session_from_response(self) -> None: assert grid.started_by == STARTED_BY assert grid.etag == GRID_ETAG assert grid.source_entity_id == SOURCE_ENTITY_ID + assert grid.source_entity_version_number == SOURCE_ENTITY_VERSION_NUMBER # AND the owner principal id is coerced from the response string to an int assert grid.owner_principal_id == OWNER_PRINCIPAL_ID assert isinstance(grid.owner_principal_id, int)