-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Cog 475 local file endpoint deletion #391
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
f6800b9
ee6bc17
d9368c6
c139d52
418cb65
1406b6f
6cb7fef
7232b04
d7195f9
a4fe33c
450bef4
14dbe6c
e631161
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
Added test that checks local files created by cognee will be deleted and those not created by cognee won't Test COG-475
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -128,15 +128,15 @@ async def delete_data_entity(self, data_id: UUID): | |
|
|
||
| # Check if other data objects point to the same raw data location | ||
| raw_data_location_entities = (await session.execute( | ||
| select(Data).where(Data.raw_data_location == data_entity.raw_data_location))).all() | ||
| select(Data.raw_data_location).where(Data.raw_data_location == data_entity.raw_data_location))).all() | ||
|
|
||
| # Don't delete local file unless this is the only reference to the file in the database | ||
| if len(raw_data_location_entities) == 1: | ||
| # delete local file only if it's created by cognee | ||
| from cognee.base_config import get_base_config | ||
| config = get_base_config() | ||
| if config.data_root_directory in raw_data_location_entities[0].raw_data_location: | ||
| os.remove(raw_data_location_entities[0]) | ||
| os.remove(raw_data_location_entities[0].raw_data_location) | ||
|
||
|
|
||
| await session.execute(delete(Data).where(Data.id == data_id)) | ||
| await session.commit() | ||
|
Comment on lines
+114
to
+152
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Add error handling and validation for file deletion operations The verification revealed several issues with file deletion implementations:
Key locations requiring attention:
🔗 Analysis chainVerify file deletion implementation across codebase. Let's verify that all file deletion scenarios are properly handled and there are no orphaned files. 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Description: Check for potential file handling issues in the codebase
# Look for other file deletion operations that might need similar handling
echo "Checking for other file deletion operations:"
rg -l "os\.remove|unlink" --type py
# Look for file path handling that might need validation
echo -e "\nChecking for file path handling:"
rg -l "raw_data_location|file_path|path\.join" --type py
# Look for TODO/FIXME comments related to file handling
echo -e "\nChecking for TODO/FIXME comments related to file handling:"
rg -i "todo|fixme|hack|xxx" -C 2 --type py | rg -i "file|path|delete"
Length of output: 3747 Script: #!/bin/bash
# Let's examine the file deletion implementations in the identified files
echo "Checking SqlAlchemyAdapter.py implementation:"
rg "os\.remove" -B 5 -A 5 cognee/infrastructure/databases/relational/sqlalchemy/SqlAlchemyAdapter.py
echo -e "\nChecking LocalStorage.py implementation:"
rg "os\.remove" -B 5 -A 5 cognee/infrastructure/files/storage/LocalStorage.py
echo -e "\nChecking networkx adapter implementation:"
rg "os\.remove" -B 5 -A 5 cognee/infrastructure/databases/graph/networkx/adapter.py
echo -e "\nChecking for file existence validation before deletion:"
rg "os\.path\.exists|os\.path\.isfile" -B 2 -A 2 cognee/infrastructure/files/storage/LocalStorage.py cognee/infrastructure/databases/relational/sqlalchemy/SqlAlchemyAdapter.py cognee/infrastructure/databases/graph/networkx/adapter.py
echo -e "\nChecking for exception handling around file operations:"
rg "try.*:.*os\.remove|except.*os\." -B 2 -A 2 cognee/infrastructure/files/storage/LocalStorage.py cognee/infrastructure/databases/relational/sqlalchemy/SqlAlchemyAdapter.py cognee/infrastructure/databases/graph/networkx/adapter.py
Length of output: 5352 |
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,12 +2,52 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| import logging | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import pathlib | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import cognee | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from cognee.modules.data.models import Data | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from cognee.api.v1.search import SearchType | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from cognee.modules.retrieval.brute_force_triplet_search import brute_force_triplet_search | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from cognee.modules.users.methods import get_default_user | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logging.basicConfig(level=logging.DEBUG) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def test_local_file_deletion(data_text, file_location): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from sqlalchemy import select | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import hashlib | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from cognee.infrastructure.databases.relational import get_relational_engine | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| engine = get_relational_engine() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async with engine.get_async_session() as session: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Get hash of data contents | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| encoded_text = data_text.encode("utf-8") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data_hash = hashlib.md5(encoded_text).hexdigest() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+22
to
+23
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider using a more secure hashing algorithm MD5 is cryptographically weak and susceptible to hash collisions. Consider using a more secure algorithm like SHA-256. - encoded_text = data_text.encode("utf-8")
- data_hash = hashlib.md5(encoded_text).hexdigest()
+ encoded_text = data_text.encode("utf-8")
+ data_hash = hashlib.sha256(encoded_text).hexdigest()📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Get data entry from database based on hash contents | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data = (await session.scalars(select(Data).where(Data.content_hash == data_hash))).one() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert os.path.isfile(data.raw_data_location), f"Data location doesn't exist: {data.raw_data_location}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
borisarzentar marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Test deletion of data along with local files created by cognee | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await engine.delete_data_entity(data.id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert not os.path.exists(data.raw_data_location), f"Data location exists: {data.raw_data_location}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
dexters1 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async with engine.get_async_session() as session: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Get data entry from database based on file path | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data = (await session.scalars(select(Data).where(Data.raw_data_location == file_location))).one() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert os.path.isfile(data.raw_data_location), f"Data location doesn't exist: {data.raw_data_location}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Test local files not created by cognee won't get deleted | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await engine.delete_data_entity(data.id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert os.path.exists(data.raw_data_location), f"Data location doesn't exists: {data.raw_data_location}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def test_getting_of_documents(dataset_name_1): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Test getting of documents for search per dataset | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from cognee.modules.users.permissions.methods import get_document_ids_for_user | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user = await get_default_user() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| document_ids = await get_document_ids_for_user(user.id, [dataset_name_1]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert len(document_ids) == 1, f"Number of expected documents doesn't match {len(document_ids)} != 1" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Test getting of documents for search when no dataset is provided | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user = await get_default_user() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| document_ids = await get_document_ids_for_user(user.id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert len(document_ids) == 2, f"Number of expected documents doesn't match {len(document_ids)} != 2" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+40
to
+50
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Improve test function structure and assertions Several improvements needed:
async def test_getting_of_documents(dataset_name_1):
+ """
+ Test document retrieval functionality:
+ 1. Verify correct document count when filtering by dataset
+ 2. Verify correct document count when no dataset filter is applied
+ """
# Test getting of documents for search per dataset
from cognee.modules.users.permissions.methods import get_document_ids_for_user
user = await get_default_user()
document_ids = await get_document_ids_for_user(user.id, [dataset_name_1])
- assert len(document_ids) == 1, f"Number of expected documents doesn't match {len(document_ids)} != 1"
+ assert len(document_ids) == 1, f"Expected 1 document when filtering by dataset, but got {len(document_ids)}"
# Test getting of documents for search when no dataset is provided
- user = await get_default_user() # Duplicate user retrieval
document_ids = await get_document_ids_for_user(user.id)
- assert len(document_ids) == 2, f"Number of expected documents doesn't match {len(document_ids)} != 2"
+ assert len(document_ids) == 2, f"Expected 2 documents without dataset filter, but got {len(document_ids)}"📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def main(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cognee.config.set_vector_db_config( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -67,16 +107,7 @@ async def main(): | |||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from cognee.infrastructure.databases.vector import get_vector_engine | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Test getting of documents for search per dataset | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from cognee.modules.users.permissions.methods import get_document_ids_for_user | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user = await get_default_user() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| document_ids = await get_document_ids_for_user(user.id, [dataset_name_1]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert len(document_ids) == 1, f"Number of expected documents doesn't match {len(document_ids)} != 1" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Test getting of documents for search when no dataset is provided | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user = await get_default_user() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| document_ids = await get_document_ids_for_user(user.id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert len(document_ids) == 2, f"Number of expected documents doesn't match {len(document_ids)} != 2" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await test_getting_of_documents(dataset_name_1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| vector_engine = get_vector_engine() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| random_node = (await vector_engine.search("entity_name", "Quantum computer"))[0] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -106,6 +137,8 @@ async def main(): | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| results = await brute_force_triplet_search('What is a quantum computer?') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert len(results) > 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await test_local_file_deletion(text, explanation_file_path) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await cognee.prune.prune_data() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert not os.path.isdir(data_directory_path), "Local data files are not deleted" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.