Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions terminusdb_client/tests/integration_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ def is_local_server_running():
"""Check if local TerminusDB server is running at http://127.0.0.1:6363"""
try:
response = requests.get("http://127.0.0.1:6363", timeout=2)
# Server responds with 404 for root path, which means it's running
# Server responds with 200 (success) or 404 (not found but server is up)
# 401 (unauthorized) also indicates server is running but needs auth
return response.status_code in [200, 404]
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout):
return False
Expand Down Expand Up @@ -73,7 +74,9 @@ def docker_url_jwt(pytestconfig):

# Check if JWT server is already running (port 6367)
if is_jwt_server_running():
print("\n✓ Using existing JWT Docker TerminusDB server at http://127.0.0.1:6367")
print(
"\n✓ Using existing JWT Docker TerminusDB server at http://127.0.0.1:6367"
)
yield ("http://127.0.0.1:6367", jwt_token)
return # Don't clean up - server was already running

Expand Down Expand Up @@ -128,7 +131,9 @@ def docker_url_jwt(pytestconfig):

if seconds_waited > MAX_CONTAINER_STARTUP_TIME:
clean_up_container()
raise RuntimeError(f"JWT Container was too slow to startup (waited {MAX_CONTAINER_STARTUP_TIME}s)")
raise RuntimeError(
f"JWT Container was too slow to startup (waited {MAX_CONTAINER_STARTUP_TIME}s)"
)

yield (test_url, jwt_token)
clean_up_container()
Expand All @@ -145,9 +150,15 @@ def docker_url(pytestconfig):
"""
# Check if local test server is already running (port 6363)
if is_local_server_running():
print("\n✓ Using existing local TerminusDB test server at http://127.0.0.1:6363")
print("⚠️ WARNING: Local server should be started with TERMINUSDB_AUTOLOGIN=true")
print(" Or use: TERMINUSDB_SERVER_AUTOLOGIN=true ./tests/terminusdb-test-server.sh restart")
print(
"\n✓ Using existing local TerminusDB test server at http://127.0.0.1:6363"
)
print(
"⚠️ WARNING: Local server should be started with TERMINUSDB_AUTOLOGIN=true"
)
print(
" Or use: TERMINUSDB_SERVER_AUTOLOGIN=true ./tests/terminusdb-test-server.sh restart"
)
yield "http://127.0.0.1:6363"
return # Don't clean up - server was already running

Expand Down Expand Up @@ -200,7 +211,9 @@ def docker_url(pytestconfig):
response = requests.get(test_url)
# Server responds with 404 for root path, which means it's running
assert response.status_code in [200, 404]
print(f"✓ Docker container started successfully after {seconds_waited}s")
print(
f"✓ Docker container started successfully after {seconds_waited}s"
)
break
except (requests.exceptions.ConnectionError, AssertionError):
pass
Expand All @@ -210,7 +223,9 @@ def docker_url(pytestconfig):

if seconds_waited > MAX_CONTAINER_STARTUP_TIME:
clean_up_container()
raise RuntimeError(f"Container was too slow to startup (waited {MAX_CONTAINER_STARTUP_TIME}s)")
raise RuntimeError(
f"Container was too slow to startup (waited {MAX_CONTAINER_STARTUP_TIME}s)"
)

yield test_url
clean_up_container()
Expand Down
52 changes: 41 additions & 11 deletions terminusdb_client/woqlquery/woql_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,8 +411,8 @@ def _clean_object(self, user_obj, target=None):
obj["node"] = user_obj
elif type(user_obj) is list:
elts = []
for obj in user_obj:
elts.append(self._clean_object(obj))
for item in user_obj:
elts.append(self._clean_object(item))
return elts
elif isinstance(user_obj, Var):
return self._expand_value_variable(user_obj)
Expand Down Expand Up @@ -1510,10 +1510,14 @@ def woql_as(self, *args):
def file(self, fpath, opts=None):
"""Provides details of a file source in a JSON format that includes a URL property

Note: CSV files can no longer be read from the filesystem. Only files submitted
as part of the request can be processed. Use remote() for URLs or submit files
via the API.

Parameters
----------
fpath : dict
file data source in a JSON format
fpath : dict or str
file data source in a JSON format or file path
opts : input options
optional

Expand All @@ -1523,7 +1527,7 @@ def file(self, fpath, opts=None):
query object that can be chained and/or execute
Example
-------
To load a local csv file:
To reference a file (must be submitted with request):
>>> WOQLQuery().file("/app/local_files/my.csv")
See Also
--------
Expand All @@ -1537,8 +1541,10 @@ def file(self, fpath, opts=None):
if self._cursor.get("@type"):
self._wrap_cursor_with_and()
self._cursor["@type"] = "QueryResource"
fpath["@type"] = "Source"
self._cursor["source"] = fpath
if isinstance(fpath, str):
self._cursor["source"] = {"@type": "Source", "file": fpath}
else:
self._cursor["source"] = fpath
self._cursor["format"] = "csv"
if opts:
self._cursor["options"] = opts
Expand Down Expand Up @@ -1600,21 +1606,45 @@ def remote(self, uri, opts=None):
if self._cursor.get("@type"):
self._wrap_cursor_with_and()
self._cursor["@type"] = "QueryResource"
uri["@type"] = "Source"
self._cursor["source"] = uri
if isinstance(uri, dict):
uri["@type"] = "Source"
self._cursor["source"] = uri
else:
self._cursor["source"] = {"@type": "Source", "url": uri}
self._cursor["format"] = "csv"
if opts:
self._cursor["options"] = opts
return self

def post(self, fpath, opts=None):
"""Specifies a file to be posted as part of the request for processing.

Note: CSV files can no longer be read from the filesystem. Only files submitted
as part of the request can be processed. This method should be used with files
that are uploaded via the API.

Parameters
----------
fpath : str or dict
file path/identifier or dict with file details
opts : dict, optional
additional options for file processing

Returns
-------
WOQLQuery object
query object that can be chained and/or execute
"""
if fpath and fpath == "args":
return ["source", "format", "options"]
if self._cursor.get("@type"):
self._wrap_cursor_with_and()
self._cursor["@type"] = "QueryResource"
fpath["@type"] = "Source"
self._cursor["source"] = fpath
if isinstance(fpath, dict):
fpath["@type"] = "Source"
self._cursor["source"] = fpath
else:
self._cursor["source"] = {"@type": "Source", "post": fpath}
self._cursor["format"] = "csv"
if opts:
self._cursor["options"] = opts
Expand Down