Skip to content

Commit

Permalink
Merge branch 'main' into j-s/fix-get-cases
Browse files Browse the repository at this point in the history
  • Loading branch information
kodiakhq[bot] authored Dec 16, 2024
2 parents 2d9c283 + 65767dc commit 0623a65
Show file tree
Hide file tree
Showing 20 changed files with 539 additions and 160 deletions.
15 changes: 15 additions & 0 deletions apps/judicial-system/backend/src/app/messages/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -856,4 +856,19 @@ export const notifications = {
description: 'Texti í pósti til aðila máls þegar ný gögn eru send',
},
}),
courtOfficialAssignedEmail: defineMessages({
subject: {
id: 'judicial.system.backend:notifications.court_official_assigned_email.subject',
defaultMessage: 'Úthlutun máls {courtCaseNumber}',
description:
'Fyrirsögn í pósti til dómara og dómritara þegar máli er úthlutað á þau',
},
body: {
id: 'judicial.system.backend:notifications.court_official_assigned_email.body',
defaultMessage:
'Héraðsdómur hefur skráð þig sem {role, select, DISTRICT_COURT_JUDGE {dómara} DISTRICT_COURT_REGISTRAR {dómritara} other {óþekkt}} í máli {courtCaseNumber}. Hægt er að nálgast gögn málsins á {linkStart}yfirlitssíðu málsins í Réttarvörslugátt{linkEnd}',
description:
'Texti í pósti til dómara og dómritara þegar máli er úthlutað á þau',
},
}),
}
52 changes: 52 additions & 0 deletions apps/judicial-system/backend/src/app/modules/case/case.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,34 @@ export class CaseService {
])
}

private addMessagesForDistrictCourtJudgeAssignedToQueue(
theCase: Case,
user: TUser,
): Promise<void> {
return this.messageService.sendMessagesToQueue([
{
type: MessageType.NOTIFICATION,
user,
caseId: theCase.id,
body: { type: CaseNotificationType.DISTRICT_COURT_JUDGE_ASSIGNED },
},
])
}

private addMessagesForDistrictCourtRegistrarAssignedToQueue(
theCase: Case,
user: TUser,
): Promise<void> {
return this.messageService.sendMessagesToQueue([
{
type: MessageType.NOTIFICATION,
user,
caseId: theCase.id,
body: { type: CaseNotificationType.DISTRICT_COURT_REGISTRAR_ASSIGNED },
},
])
}

private addMessagesForReceivedCaseToQueue(
theCase: Case,
user: TUser,
Expand Down Expand Up @@ -1403,6 +1431,30 @@ export class CaseService {
}
}

if (
isIndictment &&
[CaseState.SUBMITTED, CaseState.RECEIVED].includes(updatedCase.state)
) {
const isJudgeChanged =
updatedCase.judge?.nationalId !== theCase.judge?.nationalId
const isRegistrarChanged =
updatedCase.registrar?.nationalId !== theCase.registrar?.nationalId

if (isJudgeChanged) {
await this.addMessagesForDistrictCourtJudgeAssignedToQueue(
updatedCase,
user,
)
}

if (isRegistrarChanged) {
await this.addMessagesForDistrictCourtRegistrarAssignedToQueue(
updatedCase,
user,
)
}
}

if (
isIndictment &&
![
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const defenderNotificationRule: RolesRule = {
],
} as RolesRule

// Allows district court judges to send notifiications
// Allows district court judges to send notifications
export const districtCourtJudgeNotificationRule: RolesRule = {
role: UserRole.DISTRICT_COURT_JUDGE,
type: RulesType.FIELD_VALUES,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
INDICTMENTS_OVERVIEW_ROUTE,
INVESTIGATION_CASE_POLICE_CONFIRMATION_ROUTE,
RESTRICTION_CASE_OVERVIEW_ROUTE,
ROUTE_HANDLER_ROUTE,
SIGNED_VERDICT_OVERVIEW_ROUTE,
} from '@island.is/judicial-system/consts'
import {
Expand All @@ -46,6 +47,7 @@ import {
RequestSharedWithDefender,
SessionArrangements,
type User,
UserRole,
} from '@island.is/judicial-system/types'

import {
Expand Down Expand Up @@ -689,6 +691,28 @@ export class CaseNotificationService extends BaseNotificationService {
})
}

private sendCourtOfficialAssignedEmailNotificationForIndictmentCase(
theCase: Case,
role: UserRole.DISTRICT_COURT_JUDGE | UserRole.DISTRICT_COURT_REGISTRAR,
): Promise<Recipient> {
const official =
role === UserRole.DISTRICT_COURT_JUDGE ? theCase.judge : theCase.registrar

return this.sendEmail(
this.formatMessage(notifications.courtOfficialAssignedEmail.subject, {
courtCaseNumber: theCase.courtCaseNumber,
}),
this.formatMessage(notifications.courtOfficialAssignedEmail.body, {
courtCaseNumber: theCase.courtCaseNumber,
role,
linkStart: `<a href="${this.config.clientUrl}${ROUTE_HANDLER_ROUTE}/${theCase.id}">`,
linkEnd: '</a>',
}),
official?.name,
official?.email,
)
}

private sendCourtDateEmailNotificationForIndictmentCase(
theCase: Case,
user: User,
Expand Down Expand Up @@ -810,6 +834,25 @@ export class CaseNotificationService extends BaseNotificationService {

return result
}

private async sendDistrictCourtUserAssignedNotifications(
theCase: Case,
userRole: UserRole.DISTRICT_COURT_JUDGE | UserRole.DISTRICT_COURT_REGISTRAR,
): Promise<DeliverResponse> {
const recipient =
await this.sendCourtOfficialAssignedEmailNotificationForIndictmentCase(
theCase,
userRole,
)

return await this.recordNotification(
theCase.id,
userRole === UserRole.DISTRICT_COURT_JUDGE
? CaseNotificationType.DISTRICT_COURT_JUDGE_ASSIGNED
: CaseNotificationType.DISTRICT_COURT_REGISTRAR_ASSIGNED,
[recipient],
)
}
//#endregion

//#region RULING notifications
Expand Down Expand Up @@ -2552,6 +2595,16 @@ export class CaseNotificationService extends BaseNotificationService {
return this.sendReceivedByCourtNotifications(theCase)
case CaseNotificationType.COURT_DATE:
return this.sendCourtDateNotifications(theCase, user)
case CaseNotificationType.DISTRICT_COURT_JUDGE_ASSIGNED:
return this.sendDistrictCourtUserAssignedNotifications(
theCase,
UserRole.DISTRICT_COURT_JUDGE,
)
case CaseNotificationType.DISTRICT_COURT_REGISTRAR_ASSIGNED:
return this.sendDistrictCourtUserAssignedNotifications(
theCase,
UserRole.DISTRICT_COURT_REGISTRAR,
)
case CaseNotificationType.RULING:
return this.sendRulingNotifications(theCase)
case CaseNotificationType.MODIFIED:
Expand Down
112 changes: 112 additions & 0 deletions apps/services/bff/src/app/modules/user/user.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,5 +230,117 @@ describe('UserController', () => {
profile: expiredTokenResponse.userProfile,
})
})

it('should not refresh token when token exists but is not expired', async () => {
// Arrange - Set up login attempt in cache
mockCacheStore.set(
`attempt::${mockConfig.name}::${SID_VALUE}`,
createLoginAttempt(mockConfig),
)

// Initialize session
await server.get('/login')
await server
.get('/callbacks/login')
.set('Cookie', [`${SESSION_COOKIE_NAME}=${SID_VALUE}`])
.query({ code: 'some_code', state: SID_VALUE })

// Set valid (not expired) token in cache
const validTokenResponse = {
...mockCachedTokenResponse,
accessTokenExp: Date.now() + 1000, // Future expiration
}
mockCacheStore.set(
`current::${mockConfig.name}::${SID_VALUE}`,
validTokenResponse,
)

// Act
const res = await server
.get('/user')
.query({ refresh: 'true' })
.set('Cookie', [`${SESSION_COOKIE_NAME}=${SID_VALUE}`])

// Assert
expect(mockTokenRefreshService.refreshToken).not.toHaveBeenCalled()
expect(res.status).toEqual(HttpStatus.OK)
expect(res.body).toEqual({
scopes: validTokenResponse.scopes,
profile: validTokenResponse.userProfile,
})
})

it('should refresh token only when all conditions are met (token exists, is expired, and refresh=true)', async () => {
// Arrange - Set up login attempt in cache
mockCacheStore.set(
`attempt::${mockConfig.name}::${SID_VALUE}`,
createLoginAttempt(mockConfig),
)

const testCases = [
{
exists: true,
expired: true,
refresh: true,
shouldCallRefresh: true,
},
{
exists: true,
expired: true,
refresh: false,
shouldCallRefresh: false,
},
{
exists: true,
expired: false,
refresh: true,
shouldCallRefresh: false,
},
{
exists: false,
expired: true,
refresh: true,
shouldCallRefresh: false,
},
]

for (const testCase of testCases) {
// Reset mocks
jest.clearAllMocks()
mockCacheStore.clear()

if (testCase.exists) {
const tokenResponse = {
...mockCachedTokenResponse,
accessTokenExp: testCase.expired
? Date.now() - 1000 // Expired
: Date.now() + 1000, // Not expired
}
mockCacheStore.set(
`current::${mockConfig.name}::${SID_VALUE}`,
tokenResponse,
)
}

// Act
const res = await server
.get('/user')
.query({ refresh: testCase.refresh.toString() })
.set('Cookie', [`${SESSION_COOKIE_NAME}=${SID_VALUE}`])

// Assert
if (testCase.shouldCallRefresh) {
expect(mockTokenRefreshService.refreshToken).toHaveBeenCalled()
} else {
expect(mockTokenRefreshService.refreshToken).not.toHaveBeenCalled()
}

if (testCase.exists) {
expect(res.status).toEqual(HttpStatus.OK)
} else {
expect(res.status).toEqual(HttpStatus.UNAUTHORIZED)
}
}
})
})
})
7 changes: 6 additions & 1 deletion apps/services/bff/src/app/modules/user/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { BffUser } from '@island.is/shared/types'
import { SESSION_COOKIE_NAME } from '../../constants/cookies'

import { ErrorService } from '../../services/error.service'
import { hasTimestampExpiredInMS } from '../../utils/has-timestamp-expired-in-ms'
import { CachedTokenResponse } from '../auth/auth.types'
import { TokenRefreshService } from '../auth/token-refresh.service'
import { CacheService } from '../cache/cache.service'
Expand Down Expand Up @@ -58,7 +59,11 @@ export class UserService {
false,
)

if (cachedTokenResponse && refresh) {
if (
cachedTokenResponse &&
hasTimestampExpiredInMS(cachedTokenResponse.accessTokenExp) &&
refresh
) {
cachedTokenResponse = await this.tokenRefreshService.refreshToken({
sid,
encryptedRefreshToken: cachedTokenResponse.encryptedRefreshToken,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,7 @@ export const yearSelectContainer = style({
md: { width: '204px' },
}),
})

export const noWrap = style({
flexWrap: 'nowrap',
})
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
GET_ORGANIZATION_PAGE_QUERY,
GET_ORGANIZATION_QUERY,
} from '../../queries'
import { PensionCalculatorTitle } from './PensionCalculatorTitle'
import { PensionCalculatorWrapper } from './PensionCalculatorWrapper'
import { translationStrings } from './translationStrings'
import {
Expand Down Expand Up @@ -554,16 +555,12 @@ const PensionCalculator: CustomScreen<PensionCalculatorProps> = ({
>
<Box paddingY={5}>
<Stack space={3}>
{isNewSystemActive && (
<Text variant={titleVariant} as="h1">
{title} <div>{titlePostfix}</div>
</Text>
)}
{!isNewSystemActive && (
<Text variant={titleVariant} as="h1">
{title} {titlePostfix}
</Text>
)}
<PensionCalculatorTitle
isNewSystemActive={isNewSystemActive}
title={title}
titlePostfix={titlePostfix}
titleVariant={titleVariant}
/>
<Text>{formatMessage(translationStrings.isTurnedOff)}</Text>
</Stack>
</Box>
Expand All @@ -584,16 +581,12 @@ const PensionCalculator: CustomScreen<PensionCalculatorProps> = ({
<Stack space={3}>
<Stack space={3}>
<Box paddingTop={6}>
{isNewSystemActive && (
<Text variant={titleVariant} as="h1">
{title} <div>{titlePostfix}</div>
</Text>
)}
{!isNewSystemActive && (
<Text variant={titleVariant} as="h1">
{title} {titlePostfix}
</Text>
)}
<PensionCalculatorTitle
isNewSystemActive={isNewSystemActive}
title={title}
titlePostfix={titlePostfix}
titleVariant={titleVariant}
/>
</Box>
</Stack>
<Box
Expand Down
Loading

0 comments on commit 0623a65

Please sign in to comment.