mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
fix(frontend): Fix marketplace sort by (#11284)
Marketplace sort by functionality was not working on the frontend. This PR fixes it ### Changes 🏗️ - Add type hints for sort by - Fix marketplace sort by drop downs ### Checklist 📋 #### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: <!-- Put your test plan here: --> - [x] tested locally
This commit is contained in:
@@ -170,7 +170,7 @@ class SearchStoreAgentsBlock(Block):
|
||||
category: str | None = SchemaField(
|
||||
description="Filter by category", default=None
|
||||
)
|
||||
sort_by: Literal["rating", "runs", "name", "recent"] = SchemaField(
|
||||
sort_by: Literal["rating", "runs", "name", "updated_at"] = SchemaField(
|
||||
description="How to sort the results", default="rating"
|
||||
)
|
||||
limit: int = SchemaField(
|
||||
@@ -272,24 +272,18 @@ class SearchStoreAgentsBlock(Block):
|
||||
self,
|
||||
query: str | None = None,
|
||||
category: str | None = None,
|
||||
sort_by: str = "rating",
|
||||
sort_by: Literal["rating", "runs", "name", "updated_at"] = "rating",
|
||||
limit: int = 10,
|
||||
) -> SearchAgentsResponse:
|
||||
"""
|
||||
Search for agents in the store using the existing store database function.
|
||||
"""
|
||||
# Map our sort_by to the store's sorted_by parameter
|
||||
sorted_by_map = {
|
||||
"rating": "most_popular",
|
||||
"runs": "most_runs",
|
||||
"name": "alphabetical",
|
||||
"recent": "recently_updated",
|
||||
}
|
||||
|
||||
result = await get_database_manager_async_client().get_store_agents(
|
||||
featured=False,
|
||||
creators=None,
|
||||
sorted_by=sorted_by_map.get(sort_by, "most_popular"),
|
||||
sorted_by=sort_by,
|
||||
search_query=query,
|
||||
category=category,
|
||||
page=1,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from typing import Literal
|
||||
|
||||
import backend.server.v2.store.db
|
||||
from backend.util.cache import cached
|
||||
|
||||
@@ -20,7 +22,7 @@ def clear_all_caches():
|
||||
async def _get_cached_store_agents(
|
||||
featured: bool,
|
||||
creator: str | None,
|
||||
sorted_by: str | None,
|
||||
sorted_by: Literal["rating", "runs", "name", "updated_at"] | None,
|
||||
search_query: str | None,
|
||||
category: str | None,
|
||||
page: int,
|
||||
@@ -52,7 +54,7 @@ async def _get_cached_agent_details(username: str, agent_name: str):
|
||||
async def _get_cached_store_creators(
|
||||
featured: bool,
|
||||
search_query: str | None,
|
||||
sorted_by: str | None,
|
||||
sorted_by: Literal["agent_rating", "agent_runs", "num_agents"] | None,
|
||||
page: int,
|
||||
page_size: int,
|
||||
):
|
||||
|
||||
@@ -2,6 +2,7 @@ import asyncio
|
||||
import logging
|
||||
import typing
|
||||
from datetime import datetime, timezone
|
||||
from typing import Literal
|
||||
|
||||
import fastapi
|
||||
import prisma.enums
|
||||
@@ -41,7 +42,7 @@ DEFAULT_ADMIN_EMAIL = "admin@autogpt.co"
|
||||
async def get_store_agents(
|
||||
featured: bool = False,
|
||||
creators: list[str] | None = None,
|
||||
sorted_by: str | None = None,
|
||||
sorted_by: Literal["rating", "runs", "name", "updated_at"] | None = None,
|
||||
search_query: str | None = None,
|
||||
category: str | None = None,
|
||||
page: int = 1,
|
||||
@@ -63,7 +64,7 @@ async def get_store_agents(
|
||||
ALLOWED_ORDER_BY = {
|
||||
"rating": "rating DESC, rank DESC",
|
||||
"runs": "runs DESC, rank DESC",
|
||||
"name": "agent_name ASC, rank DESC",
|
||||
"name": "agent_name ASC, rank ASC",
|
||||
"updated_at": "updated_at DESC, rank DESC",
|
||||
}
|
||||
|
||||
@@ -421,7 +422,7 @@ async def get_store_agent_by_version_id(
|
||||
async def get_store_creators(
|
||||
featured: bool = False,
|
||||
search_query: str | None = None,
|
||||
sorted_by: str | None = None,
|
||||
sorted_by: Literal["agent_rating", "agent_runs", "num_agents"] | None = None,
|
||||
page: int = 1,
|
||||
page_size: int = 20,
|
||||
) -> backend.server.v2.store.model.CreatorsResponse:
|
||||
|
||||
@@ -392,20 +392,6 @@ async def test_get_store_agents_with_search_and_filters_parameterized():
|
||||
assert isinstance(result.agents, list)
|
||||
|
||||
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_get_store_agents_search_with_invalid_sort_by():
|
||||
"""Test that invalid sorted_by value doesn't cause SQL injection""" # Try to inject SQL via sorted_by parameter
|
||||
malicious_sort = "rating; DROP TABLE Users; --"
|
||||
result = await db.get_store_agents(
|
||||
search_query="test",
|
||||
sorted_by=malicious_sort,
|
||||
)
|
||||
|
||||
# Verify the query executed without error
|
||||
# Invalid sort_by should fall back to default, not cause SQL injection
|
||||
assert isinstance(result.agents, list)
|
||||
|
||||
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_get_store_agents_search_category_array_injection():
|
||||
"""Test that category parameter is safely passed as a parameter"""
|
||||
|
||||
@@ -2,6 +2,7 @@ import logging
|
||||
import tempfile
|
||||
import typing
|
||||
import urllib.parse
|
||||
from typing import Literal
|
||||
|
||||
import autogpt_libs.auth
|
||||
import fastapi
|
||||
@@ -93,7 +94,7 @@ async def update_or_create_profile(
|
||||
async def get_agents(
|
||||
featured: bool = False,
|
||||
creator: str | None = None,
|
||||
sorted_by: str | None = None,
|
||||
sorted_by: Literal["rating", "runs", "name", "updated_at"] | None = None,
|
||||
search_query: str | None = None,
|
||||
category: str | None = None,
|
||||
page: int = 1,
|
||||
@@ -254,7 +255,7 @@ async def create_review(
|
||||
async def get_creators(
|
||||
featured: bool = False,
|
||||
search_query: str | None = None,
|
||||
sorted_by: str | None = None,
|
||||
sorted_by: Literal["agent_rating", "agent_runs", "num_agents"] | None = None,
|
||||
page: int = 1,
|
||||
page_size: int = 20,
|
||||
):
|
||||
|
||||
@@ -7,13 +7,16 @@ import { Separator } from "@/components/__legacy__/ui/separator";
|
||||
import { FeaturedCreators } from "../FeaturedCreators/FeaturedCreators";
|
||||
import { ErrorCard } from "@/components/molecules/ErrorCard/ErrorCard";
|
||||
import { MainMarketplacePageLoading } from "../MainMarketplacePageLoading";
|
||||
import { GetV2ListStoreAgentsParams } from "@/app/api/__generated__/models/getV2ListStoreAgentsParams";
|
||||
|
||||
type MarketplaceSearchSort = GetV2ListStoreAgentsParams["sorted_by"];
|
||||
|
||||
export const MainSearchResultPage = ({
|
||||
searchTerm,
|
||||
sort,
|
||||
}: {
|
||||
searchTerm: string;
|
||||
sort: string;
|
||||
sort: MarketplaceSearchSort;
|
||||
}) => {
|
||||
const {
|
||||
agents,
|
||||
|
||||
@@ -3,12 +3,17 @@ import {
|
||||
useGetV2ListStoreCreators,
|
||||
} from "@/app/api/__generated__/endpoints/store/store";
|
||||
import { CreatorsResponse } from "@/app/api/__generated__/models/creatorsResponse";
|
||||
import { GetV2ListStoreAgentsParams } from "@/app/api/__generated__/models/getV2ListStoreAgentsParams";
|
||||
import { GetV2ListStoreCreatorsParams } from "@/app/api/__generated__/models/getV2ListStoreCreatorsParams";
|
||||
import { StoreAgentsResponse } from "@/app/api/__generated__/models/storeAgentsResponse";
|
||||
import { useState, useMemo } from "react";
|
||||
|
||||
type MarketplaceSearchSort = GetV2ListStoreAgentsParams["sorted_by"];
|
||||
type CreatorSortBy = GetV2ListStoreCreatorsParams["sorted_by"];
|
||||
|
||||
interface useMainSearchResultPageType {
|
||||
searchTerm: string;
|
||||
sort: string;
|
||||
sort: MarketplaceSearchSort;
|
||||
}
|
||||
|
||||
export const useMainSearchResultPage = ({
|
||||
@@ -17,7 +22,9 @@ export const useMainSearchResultPage = ({
|
||||
}: useMainSearchResultPageType) => {
|
||||
const [showAgents, setShowAgents] = useState(true);
|
||||
const [showCreators, setShowCreators] = useState(true);
|
||||
const [clientSortBy, setClientSortBy] = useState<string>(sort);
|
||||
const [clientSortBy, setClientSortBy] = useState<string>(
|
||||
sort ?? "updated_at",
|
||||
);
|
||||
|
||||
const {
|
||||
data: agentsData,
|
||||
@@ -37,12 +44,25 @@ export const useMainSearchResultPage = ({
|
||||
},
|
||||
);
|
||||
|
||||
const creatorsSortBy: CreatorSortBy = useMemo(() => {
|
||||
switch (sort) {
|
||||
case "runs":
|
||||
return "agent_runs";
|
||||
case "rating":
|
||||
return "agent_rating";
|
||||
default:
|
||||
return "num_agents";
|
||||
}
|
||||
}, [sort]);
|
||||
const {
|
||||
data: creatorsData,
|
||||
isLoading: isCreatorsLoading,
|
||||
isError: isCreatorsError,
|
||||
} = useGetV2ListStoreCreators(
|
||||
{ search_query: searchTerm, sorted_by: sort },
|
||||
{
|
||||
search_query: searchTerm,
|
||||
sorted_by: creatorsSortBy,
|
||||
},
|
||||
{
|
||||
query: {
|
||||
select: (x) => {
|
||||
|
||||
@@ -2,8 +2,13 @@
|
||||
|
||||
import { use } from "react";
|
||||
import { MainSearchResultPage } from "../components/MainSearchResultPage/MainSearchResultPage";
|
||||
import { GetV2ListStoreAgentsParams } from "@/app/api/__generated__/models/getV2ListStoreAgentsParams";
|
||||
|
||||
type MarketplaceSearchPageSearchParams = { searchTerm?: string; sort?: string };
|
||||
type MarketplaceSearchSort = GetV2ListStoreAgentsParams["sorted_by"];
|
||||
type MarketplaceSearchPageSearchParams = {
|
||||
searchTerm?: string;
|
||||
sort?: MarketplaceSearchSort;
|
||||
};
|
||||
|
||||
export default function MarketplaceSearchPage({
|
||||
searchParams,
|
||||
@@ -13,7 +18,7 @@ export default function MarketplaceSearchPage({
|
||||
return (
|
||||
<MainSearchResultPage
|
||||
searchTerm={use(searchParams).searchTerm || ""}
|
||||
sort={use(searchParams).sort || "trending"}
|
||||
sort={use(searchParams).sort || "runs"}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2580,7 +2580,13 @@
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"anyOf": [{ "type": "string" }, { "type": "null" }],
|
||||
"anyOf": [
|
||||
{
|
||||
"enum": ["rating", "runs", "name", "updated_at"],
|
||||
"type": "string"
|
||||
},
|
||||
{ "type": "null" }
|
||||
],
|
||||
"title": "Sorted By"
|
||||
}
|
||||
},
|
||||
@@ -2830,7 +2836,13 @@
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"anyOf": [{ "type": "string" }, { "type": "null" }],
|
||||
"anyOf": [
|
||||
{
|
||||
"enum": ["agent_rating", "agent_runs", "num_agents"],
|
||||
"type": "string"
|
||||
},
|
||||
{ "type": "null" }
|
||||
],
|
||||
"title": "Sorted By"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -10,9 +10,10 @@ import {
|
||||
import { ChevronDownIcon } from "@radix-ui/react-icons";
|
||||
|
||||
const sortOptions: SortOption[] = [
|
||||
// { label: "Most Recent", value: "recent" }, // we are not using this for now because we don't have date data from the backend
|
||||
{ label: "Most Runs", value: "runs" },
|
||||
// { label: "Highest Rated", value: "rating" }, // we are not using this for now because we don't have rating data from the backend
|
||||
{ label: "Highest Rated", value: "rating" },
|
||||
{ label: "Name (A-Z)", value: "name" },
|
||||
{ label: "Recently Updated", value: "updated_at" },
|
||||
];
|
||||
|
||||
interface SortOption {
|
||||
|
||||
Reference in New Issue
Block a user