Skip to content
Closed
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
7 changes: 1 addition & 6 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
FROM pypy:3.11-slim

ENV PYTHONUNBUFFERED=1
WORKDIR /app

RUN apt-get update && apt-get install -y \
build-essential pkg-config \
build-essential pkg-config libuv1 \
libffi-dev libssl-dev libsodium-dev make libpq-dev \
&& rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt

COPY app ./app
COPY domain ./domain
COPY migrations ./migrations
COPY pyproject.toml .
COPY settings.yaml .
COPY run_server.sh ./
RUN chmod +x run_server.sh
RUN apt-get update && apt-get install -y libuv1
EXPOSE 8000

RUN useradd -m appuser
Expand Down
2 changes: 1 addition & 1 deletion backend/app/service_analysis/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ async def get_analysis(

return ok(
{
"item": result,
"items": result,
"page": page,
"page_size": page_size,
"total": total_count,
Expand Down
18 changes: 9 additions & 9 deletions backend/app/service_analysis/tests/test_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ async def test_basic_analysis(

# list instance as admin
res_admin = await list_instance(test_client, f"{BASE_URL}/", cookies_admin, 200)
assert len(res_admin["item"]) == 1
assert_instance_fields(res_admin["item"][0], EXPECTED_FIELDS)
assert len(res_admin["items"]) == 1
assert_instance_fields(res_admin["items"][0], EXPECTED_FIELDS)

# list instance as contributor
res_contrib = await list_instance(test_client, f"{BASE_URL}/", cookies_contributor, 200)
assert len(res_contrib["item"]) == 1
assert_instance_fields(res_contrib["item"][0], EXPECTED_FIELDS)
assert len(res_contrib["items"]) == 1
assert_instance_fields(res_contrib["items"][0], EXPECTED_FIELDS)

# delete as admin (should fail)
delete_url = f"{BASE_URL}/{res_admin['item'][0]['id']}/"
Expand Down Expand Up @@ -93,23 +93,23 @@ async def get_page(page, page_size):
# page 1
data = await get_page(1, 10)
assert data["page"] == 1
assert len(data["item"]) == 10
assert len(data["items"]) == 10

# page 2
data = await get_page(2, 10)
assert data["page"] == 2
assert len(data["item"]) == 10
assert len(data["items"]) == 10

# last page
data = await get_page(4, 10)
assert data["page"] == 4
assert len(data["item"]) == 0
assert len(data["items"]) == 0
# too large page
data = await get_page(99, 10)
assert data["item"] == []
assert data["items"] == []

# search by url
res = await test_client.get(f"{BASE_URL}/", query={"search": "repo-1"}, cookies=cookies_admin)
data = await res.json()
assert res.status == 200
assert any("repo-1" in item["url"] for item in data["item"])
assert any("repo-1" in item["url"] for item in data["items"])
2 changes: 1 addition & 1 deletion backend/app/service_job/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ async def list_web_job(

return ok(
{
"item": result,
"items": result,
"page": page,
"page_size": page_size,
"total": total_count,
Expand Down
10 changes: 5 additions & 5 deletions backend/app/service_job/tests/test_api_web_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ async def test_create_list_job(
)
assert res.status == 200
result = await res.json()
assert "item" in result
assert len(result["item"]) == 5
assert "items" in result
assert len(result["items"]) == 5
assert result["total"] >= 15

# List jobs with pagination (page=3, page_size=5)
Expand All @@ -92,8 +92,8 @@ async def test_create_list_job(
)
assert res.status == 200
result = await res.json()
assert "item" in result
assert len(result["item"]) == 5
assert "items" in result
assert len(result["items"]) == 5

# Search for a specific job by name
search_name = "customer-03"
Expand All @@ -104,7 +104,7 @@ async def test_create_list_job(
)
assert res.status == 200
result = await res.json()
assert any(job["name"] == search_name for job in result["item"])
assert any(job["name"] == search_name for job in result["items"])

# get log of current running job
# TODO: test job log
Expand Down
2 changes: 1 addition & 1 deletion backend/app/service_organization/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ async def list_organization(

return ok(
{
"item": result,
"items": result,
"page": page,
"page_size": page_size,
"total": total_count,
Expand Down
14 changes: 7 additions & 7 deletions backend/app/service_organization/tests/test_organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ async def check_page(page, expected_count):
res_data = await res.json()
assert res.status == 200
assert res_data["page"] == page
assert len(res_data["item"]) == expected_count
for item in res_data["item"]:
assert len(res_data["items"]) == expected_count
for item in res_data["items"]:
assert_instance_fields(item, expected_fields)

await check_page(1, 10)
Expand All @@ -58,19 +58,19 @@ async def check_page(page, expected_count):
res_data = await res.json()
assert res.status == 200
assert res_data["total"] == 1
assert "29" in res_data["item"][0]["name"]
assert "29" in res_data["items"][0]["name"]

# search by description
res = await test_client.get(f"{BASE_URL}", query={"search": "Description-1"}, cookies=cookies_org_manager)
res_data = await res.json()
assert res.status == 200
assert res_data["total"] == 11
assert len(res_data["item"]) == 11
for item in res_data["item"]:
assert len(res_data["items"]) == 11
for item in res_data["items"]:
assert_instance_fields(item, expected_fields)

# Update
update_org = res_data["item"][0]
update_org = res_data["items"][0]
updated_org = await update_instance(
test_client,
f"{BASE_URL}/{update_org['id']}/",
Expand Down Expand Up @@ -99,4 +99,4 @@ async def check_page(page, expected_count):
cookies=cookies_org_manager,
)
res_data = await res.json()
assert len(res_data["item"]) == 29
assert len(res_data["items"]) == 29
2 changes: 1 addition & 1 deletion backend/app/service_project/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ async def list_projects(

return ok(
{
"item": result,
"items": result,
"page": page,
"page_size": page_size,
"total": total_count,
Expand Down
24 changes: 12 additions & 12 deletions backend/app/service_project/tests/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,17 +184,17 @@ async def test_project_CRUD_and_search(
res_data = await res.json()
assert res.status == 200
assert res_data["page"] == 1
assert len(res_data["item"]) == 5
for item in res_data["item"]:
assert len(res_data["items"]) == 5
for item in res_data["items"]:
assert_instance_fields(item, expected_fields)

# second page
res = await test_client.get(f"{BASE_URL}", query={"page": 2, "page_size": 5}, cookies=cookies_admin)
res_data = await res.json()
assert res.status == 200
assert res_data["page"] == 2
assert len(res_data["item"]) == 3
for item in res_data["item"]:
assert len(res_data["items"]) == 3
for item in res_data["items"]:
assert_instance_fields(item, expected_fields)

# Search name
Expand All @@ -203,7 +203,7 @@ async def test_project_CRUD_and_search(
res_data = await res.json()
assert res.status == 200
assert res_data["total"] == 2
assert "public project 1" in res_data["item"][0]["name"]
assert "public project 1" in res_data["items"][0]["name"]

# failed due to invalid format filters
res = await test_client.get(
Expand Down Expand Up @@ -233,7 +233,7 @@ async def test_project_CRUD_and_search(
res_data = await res.json()
assert res.status == 200
assert res_data["total"] == 2
assert "public project 1" in res_data["item"][0]["name"]
assert "public project 1" in res_data["items"][0]["name"]

# Search by role
res = await test_client.get(
Expand Down Expand Up @@ -265,8 +265,8 @@ async def test_project_CRUD_and_search(
res_data = await res.json()
assert res.status == 200
assert res_data["total"] == 8
assert len(res_data["item"]) == 5
for item in res_data["item"]:
assert len(res_data["items"]) == 5
for item in res_data["items"]:
assert_instance_fields(item, expected_fields)

# Get by desc sorting (asc by default)
Expand All @@ -283,13 +283,13 @@ async def test_project_CRUD_and_search(
res_data = await res.json()
assert res.status == 200
assert res_data["total"] == 8
assert len(res_data["item"]) == 5
for item in res_data["item"]:
assert len(res_data["items"]) == 5
for item in res_data["items"]:
assert_instance_fields(item, expected_fields)
assert res_data["item"][0]["name"] == "user public project 1" # this one if not sorting will be last
assert res_data["items"][0]["name"] == "user public project 1" # this one if not sorting will be last

# Test RBAC for update/delete
project_id = res_data["item"][0]["id"]
project_id = res_data["items"][0]["id"]
update_data = {"name": "Edited name", "description": "Edited description"}

for cookies in [cookies_contributor, cookies_viewer]:
Expand Down
7 changes: 6 additions & 1 deletion backend/app/utils/executor/ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,12 @@ def _generate_script(self, server: str, job: WebJob, params: dict) -> str:
mkdir -p $NXF_SINGULARITY_CACHEDIR

# === Install pixi ===
which pixi || curl -fsSL https://pixi.sh/install.sh | sh
if [ ! -f "$HOME/.pixi/bin/pixi" ]; then
curl -fsSL https://pixi.sh/install.sh | sh
else
echo "Pixi already installed."
fi

export PATH=$PATH:$HOME/.pixi/bin
# Only append channels if not already present
if ! pixi config get default-channels --global | grep -q bioconda; then
Expand Down
4 changes: 4 additions & 0 deletions backend/perf/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ tar xf pypy3.11-v7.3.20-linux64.tar.bz2
./pypy3.11-v7.3.20-linux64/bin/pypy -mpip install -U pip wheel
./pypy3.11-v7.3.20-linux64/bin/pypy -mpip install -r requirements.txt

# download for bombardier
wget https://github.com/codesenberg/bombardier/releases/download/v2.0.2/bombardier-linux-amd64
chmod +x bombardier-linux-amd64

./bombardier-linux-amd64 -c 2000 -d 10s -l http://river-backend:8000/api/health

# pypy3.11 socketify
./pypy3.11-v7.3.20-linux64/bin/pypy -m socketify app:app --worker 2 --port 8000
Expand Down
3 changes: 2 additions & 1 deletion backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ celery==5.5.3
socketify==0.0.31
psycopg[binary]==3.3.2
psycopg-pool==3.3.0
anyio==4.12.0
anyio==4.12.0
cffi==1.18.0.dev0
2 changes: 1 addition & 1 deletion backend/run_server.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ echo "Starting BlackSheep app..."
pypy -m socketify app.main:app \
--host 0.0.0.0 \
--port 8000 \
--workers "$(nproc --all)"
--workers 2
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export const CredentialContextProvider: React.FC<{ children: ReactNode }> = ({
case CredentialTypeEnum.GITHUB:
if (
existing.type === CredentialTypeEnum.GITHUB
&& existing.username
) {
update = true;
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/features/project/components/Projects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const Projects: React.FC = () => {
const [sorting, setSorting] = useState<MRT_SortingState>([]);
const [pagination, setPagination] = useState({
pageIndex: 0,
pageSize: 5,
pageSize: 10,
});

// Fetch projects
Expand Down
Loading