Skip to content

Commit

Permalink
Query batch (#141)
Browse files Browse the repository at this point in the history
* Apparently Edit Part was removed somehow??

* Make card heights consistent

* Query owners in batch

* paginate in case of >100 users
  • Loading branch information
chennisden authored Jun 17, 2024
1 parent 07537ca commit dd8ccc6
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 37 deletions.
4 changes: 4 additions & 0 deletions frontend/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ form button {
width: 100%;
}

div.card {
height: 100%;
}

p.card-text {
flex-grow: 1;
max-height: 100%;
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AuthenticationProvider } from "hooks/auth";
import { ThemeProvider } from "hooks/theme";
import About from "pages/About";
import ChangeEmail from "pages/ChangeEmail";
import EditPartForm from "pages/EditPartForm";
import EditRobotForm from "pages/EditRobotForm";
import Forgot from "pages/Forgot";
import Home from "pages/Home";
Expand Down Expand Up @@ -66,6 +67,7 @@ const App = () => {
<Route path="/test-images" element={<TestImages />} />
<Route path="/parts/" element={<Parts />} />
<Route path="/part/:id" element={<PartDetails />} />
<Route path="/edit-part/:id" element={<EditPartForm />} />
<Route path="robots/your" element={<YourRobots />} />
<Route path="/parts/your" element={<YourParts />} />
<Route path="/404" element={<NotFound />} />
Expand Down
15 changes: 15 additions & 0 deletions frontend/src/hooks/api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ export class api {
const response = await this.api.get(`/users/${userId}`);
return response.data.username;
}

public async getRobots(): Promise<Robot[]> {
try {
const response = await this.api.get("/robots/");
Expand All @@ -256,6 +257,20 @@ export class api {
}
}
}

public async getUserBatch(userIds: string[]): Promise<Map<string, string>> {
const params = new URLSearchParams();
userIds.forEach((id) => params.append("user_ids", id));
const response = await this.api.get("/users/batch/", {
params,
});
const map = new Map();
for (const index in response.data) {
map.set(response.data[index].user_id, response.data[index].username);
}
return map;
}

public async getYourRobots(): Promise<Robot[]> {
try {
const response = await this.api.get("/robots/your/");
Expand Down
18 changes: 1 addition & 17 deletions frontend/src/pages/Parts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
} from "react-bootstrap";
import Markdown from "react-markdown";
import { useNavigate } from "react-router-dom";
import { isFulfilled } from "utils/isfullfiled";

const Parts = () => {
const auth = useAuthentication();
Expand All @@ -32,22 +31,7 @@ const Parts = () => {
partsQuery.forEach((part) => {
ids.add(part.owner);
});
const idMap = await Promise.allSettled(
Array.from(ids).map(async (id) => {
try {
return [id, await auth_api.getUserById(id)];
} catch (err) {
return null;
}
}),
);
setIdMap(
new Map(
idMap
.filter(isFulfilled)
.map((result) => result.value as [string, string]),
),
);
setIdMap(await auth_api.getUserBatch(Array.from(ids)));
} catch (err) {
if (err instanceof Error) {
setError(err.message);
Expand Down
14 changes: 1 addition & 13 deletions frontend/src/pages/Robots.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
} from "react-bootstrap";
import Markdown from "react-markdown";
import { useNavigate } from "react-router-dom";
import { isFulfilled } from "utils/isfullfiled";

const Robots = () => {
const auth = useAuthentication();
Expand All @@ -29,18 +28,7 @@ const Robots = () => {
robotsQuery.forEach((robot) => {
ids.add(robot.owner);
});
const idMap = await Promise.allSettled(
Array.from(ids).map(async (id) => {
return [id, await auth_api.getUserById(id)];
}),
);
setIdMap(
new Map(
idMap
.filter(isFulfilled)
.map((result) => result.value as [string, string]),
),
);
setIdMap(await auth_api.getUserBatch(Array.from(ids)));
} catch (err) {
if (err instanceof Error) {
setError(err.message);
Expand Down
10 changes: 10 additions & 0 deletions store/app/crud/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ async def get_user(self, user_id: str) -> User | None:
return None
return User.model_validate(user_dict["Item"])

async def get_user_batch(self, user_ids: list[str]) -> list[User]:
users: list[User] = []
chunk_size = 100
for i in range(0, len(user_ids), chunk_size):
chunk = user_ids[i : i + chunk_size]
keys = [{"user_id": user_id} for user_id in chunk]
response = await self.db.batch_get_item(RequestItems={"Users": {"Keys": keys}})
users.extend(User.model_validate(user) for user in response["Responses"]["Users"])
return users

async def get_user_from_email(self, email: str) -> User | None:
table = await self.db.Table("Users")
user_dict = await table.query(
Expand Down
33 changes: 26 additions & 7 deletions store/app/routers/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from email.utils import parseaddr as parse_email_address
from typing import Annotated

from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
from fastapi import APIRouter, Depends, HTTPException, Query, Request, Response, status
from fastapi.security.utils import get_authorization_scheme_param
from pydantic.main import BaseModel

Expand Down Expand Up @@ -310,15 +310,34 @@ async def logout_user_endpoint(
return True


@users_router.get("/{user_id}", response_model=UserInfoResponse)
async def get_user_info_by_id_endpoint(user_id: str, crud: Annotated[Crud, Depends(Crud.get)]) -> UserInfoResponse:
class PublicUserInfoResponse(BaseModel):
username: str
user_id: str


@users_router.get("/batch", response_model=list[PublicUserInfoResponse])
async def get_users_batch_endpoint(
crud: Annotated[Crud, Depends(Crud.get)],
user_ids: list[str] = Query(...),
) -> list[PublicUserInfoResponse]:
user_objs = await crud.get_user_batch(user_ids)
return [
PublicUserInfoResponse(
username=user_obj.username,
user_id=user_obj.user_id,
)
for user_obj in user_objs
]


@users_router.get("/{user_id}", response_model=PublicUserInfoResponse)
async def get_user_info_by_id_endpoint(
user_id: str, crud: Annotated[Crud, Depends(Crud.get)]
) -> PublicUserInfoResponse:
user_obj = await crud.get_user(user_id)
if user_obj is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
return UserInfoResponse(
email=user_obj.email,
return PublicUserInfoResponse(
username=user_obj.username,
user_id=user_obj.user_id,
verified=user_obj.verified,
admin=user_obj.admin,
)

0 comments on commit dd8ccc6

Please sign in to comment.