Skip to content

Commit

Permalink
feat: start showing username
Browse files Browse the repository at this point in the history
  • Loading branch information
BenJeau committed Jun 7, 2024
1 parent 6cbe76c commit d3f75a0
Show file tree
Hide file tree
Showing 15 changed files with 809 additions and 678 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions backend/database/migrations/20240106215053_init.up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ CREATE TABLE IF NOT EXISTS "users" (
"roles" TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[],
"password" TEXT,

"last_modified_user_id" TEXT,

FOREIGN KEY ("last_modified_user_id") REFERENCES "users" ("id") ON DELETE SET NULL ON UPDATE CASCADE,

CHECK (LENGTH("email") <= 1000),
UNIQUE ("email", "provider")
);
Expand Down
17 changes: 12 additions & 5 deletions backend/database/src/logic/users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ VALUES ($1, $2, $3, $4, $5, $6);

#[instrument(skip(pool), ret, err)]
pub async fn get_user(pool: &PgPool, user_id: &str) -> Result<Option<User>> {
sqlx::query_as!(User, "SELECT id, created_at, updated_at, auth_id, provider, enabled, email, verified, name, given_name, family_name, locale, picture, roles FROM users WHERE id = $1;", user_id)
sqlx::query_as!(User, "SELECT id, created_at, updated_at, auth_id, provider, enabled, email, verified, name, given_name, family_name, locale, picture, roles, last_modified_user_id FROM users WHERE id = $1;", user_id)
.fetch_optional(pool)
.await
}
Expand All @@ -68,7 +68,7 @@ pub async fn get_user(pool: &PgPool, user_id: &str) -> Result<Option<User>> {
pub async fn get_user_by_auth_id(pool: &PgPool, user_auth_id: &str) -> Result<Option<User>> {
sqlx::query_as!(
User,
"SELECT id, created_at, updated_at, auth_id, provider, enabled, email, verified, name, given_name, family_name, locale, picture, roles FROM users WHERE auth_id = $1;",
"SELECT id, created_at, updated_at, auth_id, provider, enabled, email, verified, name, given_name, family_name, locale, picture, roles, last_modified_user_id FROM users WHERE auth_id = $1;",
user_auth_id
)
.fetch_optional(pool)
Expand Down Expand Up @@ -100,7 +100,12 @@ pub async fn get_user_logs(pool: &PgPool, user_id: &str) -> Result<Vec<DbUserLog
}

#[instrument(skip(pool), ret, err)]
pub async fn update_user(pool: &PgPool, user_id: &str, user: &UpdateUser) -> Result<()> {
pub async fn update_user(
pool: &PgPool,
user_id: &str,
user: &UpdateUser,
request_user_id: &str,
) -> Result<()> {
if user.enabled.is_none() && user.roles.is_none() {
return Ok(());
}
Expand All @@ -114,12 +119,14 @@ pub async fn update_user(pool: &PgPool, user_id: &str, user: &UpdateUser) -> Res
r#"
UPDATE users
SET enabled = COALESCE($2, enabled),
roles = COALESCE($3, roles)
roles = COALESCE($3, roles),
last_modified_user_id = $4
WHERE id = $1;
"#,
user_id,
user.enabled,
roles as _
roles as _,
request_user_id
)
.execute(pool)
.await?;
Expand Down
4 changes: 4 additions & 0 deletions backend/database/src/schemas/users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ pub struct User {
pub picture: Option<Vec<u8>>,
/// Roles of the user, defining their access and what they can do on the platform
pub roles: Vec<String>,
/// User that last modified the user
pub last_modified_user_id: Option<String>,
}

#[derive(FromRow, Clone, Debug)]
Expand All @@ -57,6 +59,7 @@ pub struct UserWithPassword {
pub picture: Option<Vec<u8>>,
pub roles: Vec<String>,
pub password: Option<String>,
pub last_modified_user_id: Option<String>,
}

impl From<UserWithPassword> for User {
Expand All @@ -76,6 +79,7 @@ impl From<UserWithPassword> for User {
locale: value.locale,
picture: value.picture,
roles: value.roles,
last_modified_user_id: value.last_modified_user_id,
}
}
}
Expand Down
11 changes: 8 additions & 3 deletions backend/server/src/routes/users/patch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ use axum::{
extract::{Path, State},
http::StatusCode,
response::IntoResponse,
Json,
Extension, Json,
};
use database::{
logic::users,
schemas::users::{UpdateUser, User},
PgPool,
};
use database::{logic::users, schemas::users::UpdateUser, PgPool};

use crate::Result;

Expand All @@ -28,10 +32,11 @@ use crate::Result;
)]
pub async fn update_user(
State(pool): State<PgPool>,
Extension(user): Extension<User>,
Path(user_id): Path<String>,
Json(update_user): Json<UpdateUser>,
) -> Result<impl IntoResponse> {
users::update_user(&pool, &user_id, &update_user).await?;
users::update_user(&pool, &user_id, &update_user, &user.id).await?;

Ok(StatusCode::NO_CONTENT)
}
16 changes: 11 additions & 5 deletions frontend/src/api/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,20 @@ export const usersQueryOptions = queryOptions({
}),
});

export const userQueryOptions = (userId: string) =>
queryOptions({
export function userQueryOptions<T>(userId: T) {
return queryOptions({
queryKey: ["users", userId],
queryFn: async ({ signal }) =>
await fetcher.get<User>(`/users/${userId}`, {
queryFn: async ({ signal }) => {
if (userId == undefined) {
return null;
}

return await fetcher.get<User>(`/users/${userId}`, {
signal,
}),
});
},
});
}

export const userLogsQueryOptions = (userId: string) =>
queryOptions({
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/i18n/en_CA.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"none": "None",
"creator": "Creator",
"updater": "Updater",
"requester": "Requester",
"add.entry": "Add entry",
"add.new.api.token": "Add new API token",
"add.new.secret": "Add new secret",
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/i18n/fr_CA.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"none": "Aucun",
"creator": "Créateur",
"updater": "Mise à jour par",
"requester": "Demandeur",
"add.entry": "Ajouter une entrée",
"add.new.api.token": "Créer une nouvelle clé d'API",
"add.new.secret": "Ajouter un nouveau secret",
Expand Down
65 changes: 45 additions & 20 deletions frontend/src/routes/history/$id.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
import { createFileRoute } from "@tanstack/react-router";
import { useSuspenseQuery } from "@tanstack/react-query";
import { ExternalLink } from "lucide-react";
import { Link, createFileRoute } from "@tanstack/react-router";
import {
UseSuspenseQueryResult,
useSuspenseQuery,
} from "@tanstack/react-query";
import { ExternalLink, UserIcon } from "lucide-react";
import dayjs from "dayjs";

import {
ReqestSSEData,
requestDataQueryOptions,
requestQueryOptions,
} from "@/api/requests";
import { SectionPanelHeader, RequestDataView, Trans } from "@/components";
import {
SectionPanelHeader,
RequestDataView,
Trans,
FullBadge,
} from "@/components";
import { Button } from "@/components/ui/button";
import config from "@/lib/config";
import { Badge } from "@/components/ui/badge";
import { DataCacheAction, SourceError } from "@/types/backendTypes";
import { DataCacheAction, SourceError, User } from "@/types/backendTypes";
import { beforeLoadAuthenticated } from "@/lib/auth";
import { userQueryOptions } from "@/api/users";

const HistoryComponent: React.FC = () => {
const { id } = Route.useParams();

const request = useSuspenseQuery(requestQueryOptions(id));
const requestData = useSuspenseQuery(requestDataQueryOptions(id));
const user = useSuspenseQuery(
userQueryOptions(request.data.userId),
) as UseSuspenseQueryResult<User, Error>;

return (
<>
Expand All @@ -31,21 +43,33 @@ const HistoryComponent: React.FC = () => {
}
description={dayjs.utc(request.data.createdAt).format("lll")}
extra={
<a
href={`${config.opentel_url}/trace/${request.data.traceId}`}
target="_blank"
rel="noreferrer noopener"
>
<Button
variant="secondary"
className="gap-2"
size="sm"
type="button"
<>
<Link to="/users/$id" params={{ id: request.data.userId }}>
<FullBadge
value={user.data.name}
Icon={UserIcon}
label="requester"
valueBadgeProps={{
variant: "secondary",
}}
/>
</Link>
<a
href={`${config.opentel_url}/trace/${request.data.traceId}`}
target="_blank"
rel="noreferrer noopener"
>
<ExternalLink size={16} />
<Trans id="view.related.traces" />
</Button>
</a>
<Button
variant="secondary"
className="gap-2"
size="sm"
type="button"
>
<ExternalLink size={16} />
<Trans id="view.related.traces" />
</Button>
</a>
</>
}
/>
<div className="overflow-y-auto">
Expand Down Expand Up @@ -88,9 +112,10 @@ export const Route = createFileRoute("/history/$id")({
component: HistoryComponent,
beforeLoad: beforeLoadAuthenticated(),
loader: async ({ context: { queryClient }, params: { id } }) => {
await Promise.all([
const [request] = await Promise.all([
queryClient.ensureQueryData(requestQueryOptions(id)),
queryClient.ensureQueryData(requestDataQueryOptions(id)),
]);
await queryClient.ensureQueryData(userQueryOptions(request.userId));
},
});
Loading

0 comments on commit d3f75a0

Please sign in to comment.