Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(user): api get user infos #537

Merged
merged 4 commits into from
Feb 13, 2025
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package fr.gouv.dgampa.rapportnav.infrastructure.api.bff.model.v2

data class UserInfos(
val id: Int,
val email: String,
val firstName: String? = null,
val lastName: String? = null,
val serviceId: Int? = null,
val serviceName: String? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package fr.gouv.dgampa.rapportnav.infrastructure.api.bff.v2

import fr.gouv.dgampa.rapportnav.domain.use_cases.service.GetServiceById
import fr.gouv.dgampa.rapportnav.domain.use_cases.user.FindById
import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.model.v2.UserInfos
import org.slf4j.LoggerFactory
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/api/v2/users")
class UserRestController(
private val findById: FindById,
private val getSeviceById: GetServiceById
) {

private val logger = LoggerFactory.getLogger(UserRestController::class.java)

/**
* Retrieves a specific user date by its ID.
*
* This endpoint fetches a user identified by the provided `userId` path variable. If the user exists, it
* returns the user data transformed into the API response format. If the user does not exist or an error occurs,
* it returns null.
*
* @param userId The unique identifier of the user to retrieve.
* @return The user data as a `UserInfos` object, or null if not found or an error occurs.
*/
@GetMapping("{userId}")
fun getUserById(
@PathVariable(name = "userId") userId: Int
): UserInfos? {
try {
val user = this.findById.execute(userId) ?: return null
val service = this.getSeviceById.execute(userId)
return UserInfos(
id = user.id!!,
email = user.email,
firstName = user.firstName,
lastName = user.lastName,
serviceId = service?.id,
serviceName = service?.name,
)
} catch (e: Exception) {
logger.error("Error while getting user information : ", e)
return null
}
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package fr.gouv.gmampa.rapportnav.infrastructure.bff.controllers

import fr.gouv.dgampa.rapportnav.RapportNavApplication
import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.ServiceEntity
import fr.gouv.dgampa.rapportnav.domain.use_cases.service.GetServiceById
import fr.gouv.dgampa.rapportnav.domain.use_cases.user.FindById
import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.v2.UserRestController
import fr.gouv.gmampa.rapportnav.mocks.user.UserMock
import org.junit.jupiter.api.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito.`when`
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.bean.override.mockito.MockitoBean
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get

@AutoConfigureMockMvc(addFilters = false)
@ContextConfiguration(classes = [RapportNavApplication::class])
@WebMvcTest(UserRestController::class)
class UserRestControllerTest {

@Autowired
private lateinit var mockMvc: MockMvc

@MockitoBean
private lateinit var findById: FindById

@MockitoBean
private lateinit var getSeviceById: GetServiceById

@Test
fun `should return a user infor`() {
val user = UserMock.create()
val service = ServiceEntity( id = 1, name = "PAM Jeanne Barret A")

`when`(findById.execute(id = 1)).thenReturn(user)
`when`(getSeviceById.execute(any())).thenReturn(service)

// Act & Assert
mockMvc.perform(
get("/api/v2/users/1")
)
.andExpect(status().isOk)
.andExpect(jsonPath("$.id").value(user.id))
.andExpect(jsonPath("$.serviceName").value(service.name))
}

}
8 changes: 4 additions & 4 deletions frontend/src/v2/features/auth/hooks/use-auth.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { useApolloClient } from '@apollo/client'
import AuthToken from '@features/auth/utils/token'
import { jwtDecode } from 'jwt-decode'
import { jwtDecode, JwtPayload } from 'jwt-decode'
import { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { User } from '../../common/types/user'

type AuthHook = {
isAuthenticated: boolean
logout: () => Promise<void>
isLoggedIn: () => User | undefined
isLoggedIn: () => Token | undefined
navigateAndResetCache: (to: string) => Promise<void>
}
export type Token = JwtPayload & { userId: number }
const authToken = new AuthToken()

const useAuth = (): AuthHook => {
Expand Down Expand Up @@ -41,7 +41,7 @@ const useAuth = (): AuthHook => {
navigate(to)
}

const isLoggedIn = (): User | undefined => {
const isLoggedIn = (): Token | undefined => {
const token = authToken?.get()
return token ? jwtDecode(token) : undefined
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
import { THEME } from '@mtes-mct/monitor-ui'
import React from 'react'
import { Stack } from 'rsuite'
import useGetUserQuery from '../../services/use-user'

const MissionListPageTitle: React.FC = () => {
return <h4>Rapport Nav</h4>
type MissionListPageTitleProps = {
userId?: number
}

const MissionListPageTitle: React.FC<MissionListPageTitleProps> = ({ userId }) => {
const { data: user } = useGetUserQuery(userId)
return (
<Stack direction="row" alignItems="center" style={{ alignItems: 'center', width: '100%' }}>
<Stack.Item>
<h4>Rapport Nav </h4>
</Stack.Item>
<Stack.Item style={{ marginLeft: 2, marginRight: 2 }}>{user && <h4> | </h4>}</Stack.Item>
<Stack.Item>{user && <h4 style={{ color: THEME.color.blueYonder }}> {user?.serviceName}</h4>}</Stack.Item>
</Stack>
)
}

export default MissionListPageTitle
16 changes: 16 additions & 0 deletions frontend/src/v2/features/common/services/use-user.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useQuery } from '@tanstack/react-query'
import axios from '../../../../query-client/axios'
import { User } from '../types/user'

const useGetUserQuery = (userId?: number) => {
const fetchUser = (): Promise<User> => axios.get(`users/${userId}`).then(response => response.data)

const query = useQuery<User>({
queryKey: ['user', userId],
enabled: !!userId,
queryFn: fetchUser
})
return query
}

export default useGetUserQuery
11 changes: 5 additions & 6 deletions frontend/src/v2/features/common/types/user.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { RoleType } from './role-type'

export type User = {
id: string
name: string
id: number
email: string
token: string
roles: RoleType[]
firstName?: string
lastName?: string
serviceId?: number
serviceName?: string
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export function useMissionActionGenericDateObservation(
)

const onSubmit = async (valueToSubmit?: MissionActionData) => {
debugger
if (!valueToSubmit) return
await onChange({ ...action, data: valueToSubmit })
}
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/v2/pages/mission-list-pam-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Icon } from '@mtes-mct/monitor-ui'
import { endOfYear } from 'date-fns/endOfYear'
import { startOfYear } from 'date-fns/startOfYear'
import { FC, useState } from 'react'
import useAuth from '../features/auth/hooks/use-auth.tsx'
import MissionListDateRangeNavigator from '../features/common/components/elements/mission-list-daterange-navigator.tsx'
import MissionListPageContentWrapper from '../features/common/components/layout/mission-list-page-content-wrapper.tsx'
import MissionListPageHeaderWrapper from '../features/common/components/layout/mission-list-page-header-wrapper'
Expand All @@ -28,6 +29,7 @@ const SIDEBAR_ITEMS = [

const MissionListPamPage: FC = () => {
const today = new Date()
const { isLoggedIn } = useAuth()
const [queryParams, setQueryParams] = useState({
startDateTimeUtc: startOfYear(today.toISOString()),
endDateTimeUtc: endOfYear(today).toISOString()
Expand Down Expand Up @@ -86,7 +88,7 @@ const MissionListPamPage: FC = () => {

return (
<MissionListPageWrapper
header={<MissionListPageHeaderWrapper title={<MissionListPageTitle />} />}
header={<MissionListPageHeaderWrapper title={<MissionListPageTitle userId={isLoggedIn()?.userId} />} />}
sidebar={<MissionListPageSidebarWrapper defaultItemKey="list" items={SIDEBAR_ITEMS} />}
footer={<></>}
>
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/v2/pages/mission-list-ulam-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { endOfMonth, startOfMonth } from 'date-fns'
import React, { useState } from 'react'
import { Stack } from 'rsuite'
import useAuth from '../features/auth/hooks/use-auth.tsx'
import MissionListDateRangeNavigator from '../features/common/components/elements/mission-list-daterange-navigator.tsx'
import MissionListPageContentWrapper from '../features/common/components/layout/mission-list-page-content-wrapper.tsx'
import MissionListPageHeaderWrapper from '../features/common/components/layout/mission-list-page-header-wrapper'
Expand All @@ -26,6 +27,7 @@

const MissionListUlamPage: React.FC = () => {
const today = new Date()
const { isLoggedIn } = useAuth()
const [queryParams, setQueryParams] = useState({
startDateTimeUtc: startOfMonth(today).toISOString(),
endDateTimeUtc: endOfMonth(today).toISOString()
Expand All @@ -38,7 +40,7 @@

const { getMissionListItem } = useMissionList()
const { isLoading, data: missions } = useMissionsQuery(queryParams)
const { exportMissionReport, exportIsLoading } = useMissionReportExport()

Check warning on line 43 in frontend/src/v2/pages/mission-list-ulam-page.tsx

View workflow job for this annotation

GitHub Actions / build-and-test-frontend

'exportIsLoading' is assigned a value but never used

const handleUpdateDateTime = (currentDate: Date) => {
const newDateRange = {
Expand All @@ -48,7 +50,7 @@
setQueryParams(newDateRange)
}

const exportAEM = async (missionsToExport: Mission[]) => {

Check warning on line 53 in frontend/src/v2/pages/mission-list-ulam-page.tsx

View workflow job for this annotation

GitHub Actions / build-and-test-frontend

'exportAEM' is assigned a value but never used
await exportMissionReport({
missionIds: (missionsToExport ?? []).map((m: Mission) => m.id),
exportMode: ExportMode.COMBINED_MISSIONS_IN_ONE,
Expand All @@ -56,9 +58,11 @@
})
}

console.log(isLoggedIn())

return (
<MissionListPageWrapper
header={<MissionListPageHeaderWrapper title={<MissionListPageTitle />} />}
header={<MissionListPageHeaderWrapper title={<MissionListPageTitle userId={isLoggedIn()?.userId} />} />}
sidebar={<MissionListPageSidebarWrapper defaultItemKey="list" items={SIDEBAR_ITEMS} />}
footer={<></>}
>
Expand Down
Loading