Skip to content

Commit

Permalink
Fix login service (#654)
Browse files Browse the repository at this point in the history
* Fix login service

* Fix hypha main version
  • Loading branch information
oeway authored Aug 14, 2024
1 parent 18eab02 commit 6295a2a
Show file tree
Hide file tree
Showing 12 changed files with 76 additions and 42 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

- Support invoke token
- Add basic web ui for the workspace
- BREAKING Change: Change the signature, now you need to pass a dictionary as options for `get_service`, `get_service_info`, `register_service` etc.

### 0.20.15

Expand Down
6 changes: 5 additions & 1 deletion docs/migration-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ The full service id format: `{workspace}/{client_id}:{service_id}@{app_id}`. We

**Note: Instead of return null, the new `get_service()` function will raise error if not found**

#### 5. Optionally, annotate functions with JSON schema using Pydantic
#### 5. Fix config options for register_service

Previously we allow passing keywords arguements for `get_service` (e.g. `register_service({...}, overwrite=True)`), now you need to pass it as a dictionary, e.g. `register_service({...}, {"overwrite": True})`.

#### 6. Optionally, annotate functions with JSON schema using Pydantic

To make your functions more compatible with LLMs, you can optionally use Pydantic to annotate them with JSON schema. This helps in creating well-defined interfaces for your services.

Expand Down
2 changes: 1 addition & 1 deletion hypha/VERSION
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"version": "0.20.20"
"version": "0.20.20.post1"
}
4 changes: 3 additions & 1 deletion hypha/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@
(Path(__file__).parent / "VERSION").read_text(encoding="utf-8").strip()
)
__version__ = VERSION_INFO["version"]
parts = __version__.split(".")
main_version = f"{parts[0]}.{parts[1]}.{parts[2]}"

__all__ = ["__version__"]
__all__ = ["__version__", "main_version"]
6 changes: 3 additions & 3 deletions hypha/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from aiobotocore.session import get_session
from jinja2 import Environment, PackageLoader, select_autoescape

from hypha import __version__
from hypha import main_version
from hypha.core import Card, UserInfo, ServiceInfo, UserPermission
from hypha.core.store import RedisStore
from hypha.plugin_parser import convert_config_to_card, parse_imjoy_plugin
Expand Down Expand Up @@ -269,7 +269,7 @@ async def install(
safe_join("apps", config["type"] + "-app.html")
)
source = temp.render(
hypha_version=__version__,
hypha_main_version=main_version,
hypha_rpc_websocket_mjs=self.public_base_url
+ "/hypha-rpc-websocket.mjs",
config={k: config[k] for k in config if k in PLUGIN_CONFIG_FIELDS},
Expand All @@ -291,7 +291,7 @@ async def install(
default_config.update(config or {})
config = default_config
source = temp.render(
hypha_version=__version__,
hypha_main_version=main_version,
hypha_rpc_websocket_mjs=self.public_base_url
+ "/hypha-rpc-websocket.mjs",
script=source,
Expand Down
25 changes: 16 additions & 9 deletions hypha/core/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,8 @@ async def check_login(key, timeout=MAXIMUM_LOGIN_TIME, profile=False):
assert await redis.exists(key), "Invalid key, key does not exist"
if timeout <= 0:
user_info = await redis.get(key)
if user_info == b"":
return None
user_info = json.loads(user_info)
user_info = UserTokenInfo.model_validate(user_info)
if user_info:
Expand All @@ -381,15 +383,20 @@ async def check_login(key, timeout=MAXIMUM_LOGIN_TIME, profile=False):
count = 0
while True:
user_info = await redis.get(key)
user_info = json.loads(user_info)
user_info = UserTokenInfo.model_validate(user_info)
if user_info is None:
raise Exception(
f"Login session expired, the maximum login time is {MAXIMUM_LOGIN_TIME} seconds"
)
if user_info:
await redis.delete(key)
return user_info.model_dump(mode="json") if profile else user_info.token
if user_info != b"":
user_info = json.loads(user_info)
user_info = UserTokenInfo.model_validate(user_info)
if user_info is None:
raise Exception(
f"Login session expired, the maximum login time is {MAXIMUM_LOGIN_TIME} seconds"
)
if user_info:
await redis.delete(key)
return (
user_info.model_dump(mode="json")
if profile
else user_info.token
)
await asyncio.sleep(1)
count += 1
if count > timeout:
Expand Down
17 changes: 11 additions & 6 deletions hypha/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,14 @@
import jose
import os

from starlette.datastructures import Headers

from hypha_rpc import RPC
from hypha import __version__
from hypha import main_version
from hypha.core import UserPermission
from hypha.core.auth import AUTH0_DOMAIN
from hypha.core.store import RedisStore
from hypha.utils import GzipRoute, safe_join, is_safe_path
from hypha.s3 import FSFileResponse
from starlette.datastructures import Headers
from aiobotocore.session import get_session


class MsgpackResponse(Response):
Expand Down Expand Up @@ -211,7 +210,7 @@ async def auth_proxy(request: Request):
@router.get(norm_url("/hypha-rpc-websocket.mjs"))
async def hypha_rpc_websocket_mjs(request: Request):
if not self.rpc_lib_esm_content:
_rpc_lib_script = f"https://cdn.jsdelivr.net/npm/hypha-rpc@{__version__}/dist/hypha-rpc-websocket.mjs"
_rpc_lib_script = f"https://cdn.jsdelivr.net/npm/hypha-rpc@{main_version}/dist/hypha-rpc-websocket.mjs"
response = requests.get(_rpc_lib_script)
response.raise_for_status()
self.rpc_lib_esm_content = response.content
Expand All @@ -222,7 +221,7 @@ async def hypha_rpc_websocket_mjs(request: Request):
@router.get(norm_url("/hypha-rpc-websocket.js"))
async def hypha_rpc_websocket_js(request: Request):
if not self.rpc_lib_umd_content:
_rpc_lib_script = f"https://cdn.jsdelivr.net/npm/hypha-rpc@{__version__}/dist/hypha-rpc-websocket.js"
_rpc_lib_script = f"https://cdn.jsdelivr.net/npm/hypha-rpc@{main_version}/dist/hypha-rpc-websocket.js"
response = requests.get(_rpc_lib_script)
response.raise_for_status()
self.rpc_lib_umd_content = response.content
Expand Down Expand Up @@ -458,6 +457,9 @@ async def run_app(
else:
key = safe_join(workspace, path)
assert self.s3_enabled, "S3 is not enabled."
from hypha.s3 import FSFileResponse
from aiobotocore.session import get_session

s3_client = get_session().create_client(
"s3",
endpoint_url=self.endpoint_url,
Expand Down Expand Up @@ -626,6 +628,9 @@ async def get_browser_app_file(
)
key = safe_join(self.workspace_etc_dir, workspace, app_id, path)
assert self.s3_enabled, "S3 is not enabled."
from hypha.s3 import FSFileResponse
from aiobotocore.session import get_session

s3_client = get_session().create_client(
"s3",
endpoint_url=self.endpoint_url,
Expand Down
2 changes: 0 additions & 2 deletions hypha/s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import sys
from datetime import datetime
from email.utils import formatdate
from pathlib import Path
from typing import Any, Dict

import botocore
Expand All @@ -23,7 +22,6 @@
from hypha.utils import (
generate_password,
list_objects_async,
list_objects_sync,
remove_objects_sync,
safe_join,
)
Expand Down
4 changes: 2 additions & 2 deletions hypha/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles

from hypha import __version__ as VERSION
from hypha import __version__
from hypha.core.auth import create_login_service
from hypha.core.store import RedisStore
from hypha.core.queue import create_queue_service
Expand Down Expand Up @@ -186,7 +186,7 @@ async def lifespan(app: FastAPI):
"A serverless application framework for \
large-scale data management and AI model serving"
),
version=VERSION,
version=__version__,
)
application.router.route_class = GzipRoute

Expand Down
2 changes: 1 addition & 1 deletion hypha/templates/apps/web-python-app.html
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
async def run():
try:
await micropip.install(["numpy", "pydantic", "hypha-rpc=={{ hypha_version }}", {% for req in requirements %}"{{req}}", {% endfor %}])
await micropip.install(["numpy", "pydantic", "hypha-rpc=={{ hypha_main_version }}", {% for req in requirements %}"{{req}}", {% endfor %}])
js.__resolve()
except Exception as e:
console.error("Failed to install packages: ", traceback.format_exc())
Expand Down
23 changes: 7 additions & 16 deletions hypha/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@

<!-- Render the App component -->
<script type="text/babel" data-presets="env,react">
const Navbar = ({ isLoggedIn, user, onLogin, onLogout }) => {
const Navbar = ({ isLoggedIn, user, onLogin, onLogout, onAboutClick }) => {
return (
<nav className="navbar flex justify-between items-center p-4 bg-gray-900 shadow-lg">
<div className="flex items-center space-x-4">
Expand All @@ -82,7 +82,7 @@
<a href="#" className="hover:text-gray-400 transition duration-300">Dashboard</a>
<a href="#" className="hover:text-gray-400 transition duration-300">Marketplace</a>
<a href="https://docs.amun.ai/" target="_blank" className="hover:text-gray-400 transition duration-300">Documentation</a>
<a href="#" id="about-link" className="hover:text-gray-400 transition duration-300">About</a>
<a href="#" className="hover:text-gray-400 transition duration-300" onClick={onAboutClick}>About</a>
{isLoggedIn ? (
<div className="dropdown">
<button className="hover:text-gray-400 transition duration-300">
Expand Down Expand Up @@ -230,13 +230,17 @@ <h3 className="text-xl font-bold">{title}</h3>
setUser(null);
};

const onAboutClick = () => {
setIsModalOpen(true);
};

if (!config) {
return <div>Loading...</div>; // Show a loading state while fetching config
}

return (
<div>
<Navbar isLoggedIn={isLoggedIn} user={user} onLogin={onLogin} onLogout={onLogout} />
<Navbar isLoggedIn={isLoggedIn} user={user} onLogin={onLogin} onLogout={onLogout} onAboutClick={onAboutClick} />
<div className="container mx-auto mt-16 pt-20 text-center">
<h1 className="text-4xl font-bold">Welcome to Hypha</h1>
<p className="text-xl text-gray-400 mt-4">A collaborative workspace for data analysis</p>
Expand Down Expand Up @@ -271,19 +275,6 @@ <h1 className="text-4xl font-bold">Welcome to Hypha</h1>
);
};

document.addEventListener('DOMContentLoaded', function() {
const links = document.querySelectorAll('a');
links.forEach(link => {
link.setAttribute('target', '_blank');
link.setAttribute('rel', 'noopener noreferrer');
});

document.getElementById('about-link').addEventListener('click', function(e) {
e.preventDefault();
setIsModalOpen(true);
});
});

ReactDOM.render(<App />, document.getElementById('app'));
</script>
</body>
Expand Down
26 changes: 26 additions & 0 deletions tests/test_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,32 @@ def add2(a: int, b: int) -> int:
assert svc_info2["service_schema"]


async def test_login_service(fastapi_server):
"""Test login to the server."""
async with connect_to_server(
{"name": "test client", "server_url": SERVER_URL}
) as api:
svc = await api.get_service("public/hypha-login")
assert svc and callable(svc.start)
info = await svc.start()
key = info["key"]
data = await svc.check(key, timeout=-1)
assert data is None
await svc.report(key, "test")
token = await svc.check(key, timeout=1)
assert token == "test"

# with user info
info = await svc.start()
key = info["key"]
data = await svc.check(key, timeout=-1)
assert data is None
await svc.report(key, "test", email="[email protected]")
user_profile = await svc.check(key, timeout=1, profile=True)
assert user_profile["token"] == "test"
assert user_profile["email"] == "[email protected]"


async def test_login(fastapi_server):
"""Test login to the server."""
async with connect_to_server(
Expand Down

0 comments on commit 6295a2a

Please sign in to comment.