Skip to content

Commit 430496b

Browse files
committed
fix: handle private validation URL redirect and string error messages
When make_validation_url_private=true, the backend returns HTTP 401 with action=REDIRECT and errors as a string array. The frontend was only handling object-format errors ({message}) and not the redirect action, causing a false 'Failed to validate document' message. Adds handleValidationRedirect() and fixes getValidationErrorMessage() to handle both string and object error formats. Signed-off-by: Vitor Mattos <[email protected]>
1 parent 17a8bb3 commit 430496b

2 files changed

Lines changed: 84 additions & 5 deletions

File tree

src/tests/views/Validation.spec.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type ValidationVm = {
3838
handleValidationSuccess: (data: Record<string, any>) => void
3939
handleSigningComplete: (file: Record<string, any> | null) => void
4040
refreshAfterAsyncSigning: () => Promise<void>
41+
validateByUUID: (uuid: string, options?: { suppressLoading?: boolean }) => Promise<void>
4142
$nextTick: () => Promise<void>
4243
}
4344

@@ -986,6 +987,53 @@ describe('Validation.vue - Business Logic', () => {
986987
})
987988
})
988989

990+
describe('validation API error handling', () => {
991+
const VALID_UUID = '550e8400-e29b-41d4-a716-446655440000'
992+
993+
it('redirects to login when validation URL is private', async () => {
994+
const hrefSpy = vi.spyOn(window.location, 'href', 'set')
995+
vi.mocked(axios.get).mockRejectedValueOnce({
996+
response: {
997+
status: 401,
998+
data: {
999+
ocs: {
1000+
data: {
1001+
action: 1000,
1002+
redirect: '/index.php/login?redirect_url=%2Fapps%2Flibresign%2Fvalidation%2F550e8400-e29b-41d4-a716-446655440000',
1003+
errors: ['You are not logged in. Please log in.'],
1004+
},
1005+
},
1006+
},
1007+
},
1008+
})
1009+
1010+
await wrapper.vm.validateByUUID(VALID_UUID)
1011+
1012+
expect(hrefSpy).toHaveBeenCalledWith('/index.php/login?redirect_url=%2Fapps%2Flibresign%2Fvalidation%2F550e8400-e29b-41d4-a716-446655440000')
1013+
expect(wrapper.vm.validationErrorMessage).toBe(null)
1014+
hrefSpy.mockRestore()
1015+
})
1016+
1017+
it('shows string-based backend errors instead of generic fallback', async () => {
1018+
vi.mocked(axios.get).mockRejectedValueOnce({
1019+
response: {
1020+
status: 401,
1021+
data: {
1022+
ocs: {
1023+
data: {
1024+
errors: ['You are not logged in. Please log in.'],
1025+
},
1026+
},
1027+
},
1028+
},
1029+
})
1030+
1031+
await wrapper.vm.validateByUUID(VALID_UUID)
1032+
1033+
expect(wrapper.vm.validationErrorMessage).toBe('You are not logged in. Please log in.')
1034+
})
1035+
})
1036+
9891037
describe('status contract guards', () => {
9901038
const createLoadedValidationDocument = (patch: Record<string, unknown> = {}) => ({
9911039
id: 100,

src/views/Validation.vue

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ import {
137137
MODIFICATION_VIOLATION,
138138
toValidationDocument,
139139
} from '../services/validationDocument'
140+
import { ACTION_CODES } from '../helpers/ActionMapping'
140141
import { normalizeRouteRecord } from '../services/routeNormalization.js'
141142
import logger from '../logger.js'
142143
import { useFilesStore } from '../store/files.js'
@@ -196,13 +197,17 @@ type StatusPresentation = {
196197
type ErrorMessageEntry = {
197198
message?: string
198199
}
200+
type ValidationErrorEntry = ErrorMessageEntry | string
201+
type ValidationErrorPayload = {
202+
errors?: ValidationErrorEntry[]
203+
action?: number
204+
redirect?: string
205+
}
199206
type ValidationErrorResponse = {
200207
status?: number
201208
data?: {
202209
ocs?: {
203-
data?: {
204-
errors?: ErrorMessageEntry[]
205-
}
210+
data?: ValidationErrorPayload
206211
}
207212
}
208213
}
@@ -223,12 +228,29 @@ function isSignedDocumentStatus(status: unknown): boolean {
223228
}
224229
225230
function getValidationErrorMessage(response: ValidationErrorResponse | undefined, fallback: string): string {
226-
if (response?.data?.ocs?.data?.errors?.length) {
227-
return response.data.ocs.data.errors[0]?.message || fallback
231+
const errors = response?.data?.ocs?.data?.errors
232+
if (errors?.length) {
233+
const [firstError] = errors
234+
if (typeof firstError === 'string' && firstError.length > 0) {
235+
return firstError
236+
}
237+
if (typeof firstError?.message === 'string' && firstError.message.length > 0) {
238+
return firstError.message
239+
}
228240
}
229241
return fallback
230242
}
231243
244+
function handleValidationRedirect(response: ValidationErrorResponse | undefined): boolean {
245+
const action = response?.data?.ocs?.data?.action
246+
const redirect = response?.data?.ocs?.data?.redirect
247+
if (action !== ACTION_CODES.REDIRECT || typeof redirect !== 'string' || redirect.length === 0) {
248+
return false
249+
}
250+
window.location.href = redirect
251+
return true
252+
}
253+
232254
const signStore = useSignStore()
233255
const sidebarStore = useSidebarStore()
234256
const filesStore = useFilesStore()
@@ -333,6 +355,9 @@ async function upload(file: File) {
333355
handleValidationSuccess(data.ocs.data)
334356
})
335357
.catch((error: { response?: ValidationErrorResponse }) => {
358+
if (handleValidationRedirect(error.response)) {
359+
return
360+
}
336361
const errorMsg = getValidationErrorMessage(error.response, t('libresign', 'Failed to validate document'))
337362
setValidationError(errorMsg)
338363
})
@@ -397,6 +422,9 @@ async function validateByUUID(uuid: string, { suppressLoading = false }: { suppr
397422
})
398423
.catch((error: { response?: ValidationErrorResponse }) => {
399424
const response = error.response
425+
if (handleValidationRedirect(response)) {
426+
return
427+
}
400428
if (response?.status === 404) {
401429
setValidationError(t('libresign', 'Document not found'))
402430
} else {
@@ -419,6 +447,9 @@ async function validateByNodeID(nodeId: string, { suppressLoading = false }: { s
419447
})
420448
.catch((error: { response?: ValidationErrorResponse }) => {
421449
const response = error.response
450+
if (handleValidationRedirect(response)) {
451+
return
452+
}
422453
if (response?.status === 404) {
423454
setValidationError(t('libresign', 'Document not found'))
424455
} else {

0 commit comments

Comments
 (0)