def update(id: str, new_list: ListModel, user_sub: str) -> Optional[ListModel]: curr_list = ListsDto.get(id, user_sub) if not curr_list: return None else: if user_sub in curr_list.guests: # is guest, cannot modify listName and guests if curr_list.listName != new_list.listName or curr_list.guests != new_list.guests: raise NoAccessError( 'Guest is not allowed to modify those attributes') # rewrite immutable properties from existing item new_list.id = id new_list.createdAt = curr_list.createdAt new_list.userId = curr_list.userId # update access entries for users denied access for non_guest in (set(curr_list.guests) - set(new_list.guests)): UserToLists.remove_list(non_guest, id) # update access entries for users given access for guest in new_list.guests: UserToLists.add_list(guest, id) Lists.save(ListMappers.map_dto_to_doc(new_list)) return new_list
def test_lists_dto_update__when_user_is_guest__and_updates_only_items__then_update_list( dynamodb_lists_table, dynamodb_user_to_lists_table, sample_data): from sf_shopping_list.data.db.lists import Lists from sf_shopping_list.data.dto.lists_dto import ListsDto from sf_shopping_list.data.clients.dynamodb import lists_table list1 = sample_data['lists'][1] new_list1 = ListModel.parse_obj(list1) Lists.save(ListMappers.map_dto_to_doc(new_list1)) # guest is allowed to change items new_list1.items = ['item3', 'item4', 'item5'] new_list1_res = ListsDto.update(new_list1.id, new_list1, tested_user_id) new_list1_saved = lists_table().get_item(Key={'id': new_list1.id}) assert new_list1_res == new_list1 assert ListDocModel.from_db_doc( new_list1_saved['Item']) == ListMappers.map_dto_to_doc(new_list1) # guest is not allowed to modify guests list or name new_list1.guests += 'some_other_user_id' with pytest.raises(NoAccessError): ListsDto.update(new_list1.id, new_list1, tested_user_id) new_list1.listName = 'new list name' with pytest.raises(NoAccessError): ListsDto.update(new_list1.id, new_list1, tested_user_id)
def test_lists_dto_update__when_user_has_no_access__raise_no_access_error( dynamodb_lists_table, sample_data): from sf_shopping_list.data.dto.lists_dto import ListsDto from sf_shopping_list.data.clients.dynamodb import lists_table list2 = sample_data['lists'][2] lists_table().put_item(Item=list2) new_list = ListModel.parse_obj(list2) with pytest.raises(NoAccessError): ListsDto.update(new_list.id, new_list, tested_user_id)
def test_lists_dto_update__when_list_does_not_exist__return_none( dynamodb_lists_table): from sf_shopping_list.data.dto.lists_dto import ListsDto new_list = ListModel(id='ABCdef', userId=tested_user_id, listName='list name', createdAt=datetime.utcnow(), items=['item0', 'item1'], guests=[]) assert ListsDto.update('ABCdef', new_list, tested_user_id) is None
def create(new_list_dto: NewList, user_sub: str) -> ListModel: list_dto = ListModel( id=ShortUUID.random(length=6), userId=user_sub, listName=new_list_dto.name, createdAt=datetime.utcnow(), items=new_list_dto.items, guests=new_list_dto.guests, ) Lists.save(ListMappers.map_dto_to_doc(list_dto)) UserToLists.add_list(user_sub, list_dto.id) for guest_id in new_list_dto.guests: UserToLists.add_list(guest_id, list_dto.id) return list_dto
def test_lists_dto_update__when_user_is_guest__and_updates_guests_or_name__then_raise_no_access_error( dynamodb_lists_table, dynamodb_user_to_lists_table, sample_data): from sf_shopping_list.data.db.lists import Lists from sf_shopping_list.data.dto.lists_dto import ListsDto list1 = sample_data['lists'][1] new_list1 = ListModel.parse_obj(list1) Lists.save(ListMappers.map_dto_to_doc(new_list1)) # guest is not allowed to modify guests list or name new_list1.guests += 'some_other_user_id' with pytest.raises(NoAccessError): ListsDto.update(new_list1.id, new_list1, tested_user_id) new_list1.listName = 'new list name' with pytest.raises(NoAccessError): ListsDto.update(new_list1.id, new_list1, tested_user_id)
def test_lists_dto_update__when_user_is_owner__then_update_list( dynamodb_lists_table, dynamodb_user_to_lists_table, sample_data): from sf_shopping_list.data.dto.lists_dto import ListsDto from sf_shopping_list.data.db.lists import Lists from sf_shopping_list.data.clients.dynamodb import lists_table from sf_shopping_list.data.clients.dynamodb import user_to_lists_table list0 = sample_data['lists'][0] new_list0 = ListModel.parse_obj(list0) Lists.save(ListMappers.map_dto_to_doc(new_list0)) # owner can change items, guests and listName new_list0.items = ['item4', 'item5', 'item6'] new_list0.guests = {other_user_id} new_list0.listName = 'new list name' new_list0_res = ListsDto.update(new_list0.id, new_list0, new_list0.userId) new_list0_saved = lists_table().get_item(Key={'id': new_list0.id}) # updated list is returned assert new_list0_res == new_list0 # updated list is saved assert ListDocModel.from_db_doc( new_list0_saved['Item']) == ListMappers.map_dto_to_doc(new_list0) # if guests were added, so their user_to_lists entries were updated other_user_lists = user_to_lists_table().get_item( Key={'user_id': other_user_id}) assert new_list0.id in UserToListsDocModel.from_db_doc( other_user_lists['Item']).lists # try removing guest new_list0.guests = set() new_list0_res = ListsDto.update(new_list0.id, new_list0, new_list0.userId) new_list0_saved = lists_table().get_item(Key={'id': new_list0.id}) assert new_list0_res == new_list0 assert ListDocModel.from_db_doc( new_list0_saved['Item']) == ListMappers.map_dto_to_doc(new_list0) # guests should have their user_to_lists entries updated other_user_lists = user_to_lists_table().get_item( Key={'user_id': other_user_id}) assert new_list0.id not in UserToListsDocModel.from_db_doc( other_user_lists['Item']).lists # try adding guest back new_list0.guests = {other_user_id} new_list0_res = ListsDto.update(new_list0.id, new_list0, new_list0.userId) new_list0_saved = lists_table().get_item(Key={'id': new_list0.id}) assert new_list0_res == new_list0 assert ListDocModel.from_db_doc( new_list0_saved['Item']) == ListMappers.map_dto_to_doc(new_list0) # guests should have their user_to_lists entries updated other_user_lists = user_to_lists_table().get_item( Key={'user_id': other_user_id}) # immutable fields changes should be ignored original_new_list_0 = new_list0.copy() new_list0.id = 'foo' new_list0.createdAt = datetime.utcnow() new_list0.userId = other_user_id new_list0_res = ListsDto.update(original_new_list_0.id, new_list0, original_new_list_0.userId) new_list_0_saved = lists_table().get_item(Key={'id': new_list0.id}) assert new_list0_res == original_new_list_0 assert ListDocModel.from_db_doc( new_list_0_saved['Item']) == ListMappers.map_dto_to_doc( original_new_list_0) assert new_list0.id in UserToListsDocModel.from_db_doc( other_user_lists['Item']).lists
def map_doc_to_dto(doc: ListDocModel) -> ListModel: return ListModel.parse_obj({ **doc.dict(), 'createdAt': datetime.fromtimestamp(doc.createdAt) })
def map_dto_to_doc(dto: ListModel) -> ListDocModel: return ListDocModel.parse_obj({ **dto.dict(), 'createdAt': int(dto.createdAt.timestamp()) })