Skip to content

Commit

Permalink
thumbnails support
Browse files Browse the repository at this point in the history
  • Loading branch information
aktech committed Jan 4, 2024
1 parent 2631b81 commit a2838f2
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 9 deletions.
3 changes: 2 additions & 1 deletion jhub_apps/service/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from datetime import datetime
from typing import Any, Dict, List, Optional


from pydantic import BaseModel


Expand Down Expand Up @@ -51,7 +52,7 @@ class UserOptions(BaseModel):
jhub_app: bool
display_name: str
description: str
thumbnail: typing.Optional[str] = str()
thumbnail: str = None
filepath: typing.Optional[str] = str()
framework: str = "panel"
custom_command: typing.Optional[str] = str()
Expand Down
32 changes: 28 additions & 4 deletions jhub_apps/service/service.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import typing
import dataclasses
import os
from datetime import timedelta

from fastapi import APIRouter, Depends, status, Request
from fastapi import APIRouter, Depends, status, Request, File, UploadFile, Form, HTTPException
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel, ValidationError
from starlette.responses import RedirectResponse

from jhub_apps.service.auth import create_access_token
Expand All @@ -14,7 +17,8 @@
from fastapi.templating import Jinja2Templates

from jhub_apps.hub_client.hub_client import HubClient
from jhub_apps.service.utils import get_conda_envs, get_jupyterhub_config, get_spawner_profiles
from jhub_apps.service.utils import get_conda_envs, get_jupyterhub_config, get_spawner_profiles, \
encode_file_to_data_url
from jhub_apps.spawner.types import FRAMEWORKS

app = FastAPI()
Expand Down Expand Up @@ -62,6 +66,7 @@ async def login(request: Request):
authorization_url = os.environ["PUBLIC_HOST"] + "/hub/api/oauth2/authorize?response_type=code&client_id=service-japps"
return RedirectResponse(authorization_url, status_code=302)


@router.get("/server/", description="Get all servers")
@router.get("/server/{server_name}", description="Get a server by server name")
async def get_server(user: User = Depends(get_current_user), server_name=None):
Expand All @@ -81,12 +86,31 @@ async def get_server(user: User = Depends(get_current_user), server_name=None):
return user_servers


class Checker:
def __init__(self, model: BaseModel):
self.model = model

def __call__(self, data: str = Form(...)):
try:
return self.model.model_validate_json(data)
except ValidationError as e:
raise HTTPException(
detail=jsonable_encoder(e.errors()),
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
)


@router.post("/server/")
async def create_server(
# request: Request,
server: ServerCreation,
server: ServerCreation = Depends(Checker(ServerCreation)),
thumbnail: typing.Optional[UploadFile] = File(...),
user: User = Depends(get_current_user),
):
if thumbnail:
thumbnail_contents = await thumbnail.read()
server.user_options.thumbnail = encode_file_to_data_url(
thumbnail.filename, thumbnail_contents
)
hub_client = HubClient()
return hub_client.create_server(
username=user.name,
Expand Down
12 changes: 12 additions & 0 deletions jhub_apps/service/utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import base64
import os

from jupyterhub.app import JupyterHub
Expand Down Expand Up @@ -44,3 +45,14 @@ def get_spawner_profiles(config):
raise ValueError(
f"Invalid value for config.KubeSpawner.profile_list: {profile_list}"
)


def encode_file_to_data_url(filename, file_contents):
"""Converts image file to data url to display in browser."""
base64_encoded = base64.b64encode(file_contents)
filename_ = filename.lower()
mime_type = "image/png"
if filename_.endswith(".jpg") or filename_.endswith(".jpeg"):
mime_type = "image/jpeg"
data_url = f"data:{mime_type};base64,{base64_encoded.decode('utf-8')}"
return data_url
16 changes: 12 additions & 4 deletions jhub_apps/tests/test_api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import dataclasses
import io
import json
from unittest.mock import patch

from jhub_apps.hub_client.hub_client import HubClient
Expand Down Expand Up @@ -34,16 +36,22 @@ def test_api_get_server(get_user, client):
@patch.object(HubClient, "create_server")
def test_api_create_server(create_server, client):
from jhub_apps.service.models import UserOptions

create_server_response = {"user": "aktech"}
create_server.return_value = create_server_response
user_options = mock_user_options()
body = {"servername": "panel-app", "user_options": user_options}
response = client.post("/server/", json=body)
thumbnail = b"contents of thumbnail"
in_memory_file = io.BytesIO(thumbnail)
response = client.post(
"/server/",
data={'data': json.dumps({"servername": "panel-app", "user_options": user_options})},
files={'thumbnail': ('image.jpeg', in_memory_file)}
)
final_user_options = UserOptions(**user_options)
final_user_options.thumbnail = "data:image/jpeg;base64,Y29udGVudHMgb2YgdGh1bWJuYWls"
create_server.assert_called_once_with(
username=MOCK_USER.name,
servername="panel-app",
user_options=UserOptions(**user_options),
user_options=final_user_options,
)
assert response.json() == create_server_response

Expand Down

0 comments on commit a2838f2

Please sign in to comment.