Skip to content

Commit

Permalink
https://issues.apache.org/jira/browse/MYFACES-4676:
Browse files Browse the repository at this point in the history
Fixes for issue
  • Loading branch information
werpu committed Aug 28, 2024
1 parent fa8730a commit abd4247
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 58 deletions.
14 changes: 7 additions & 7 deletions api/src/client/package-lock.json

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

2 changes: 1 addition & 1 deletion api/src/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"html-webpack-plugin": "^5.5.1",
"jsdom": "^21.1.1",
"jsdom-global": "^3.0.2",
"jsf.js_next_gen": "4.0.2-beta.10",
"jsf.js_next_gen": "4.0.3-beta.1",
"mocha": "^10.2.0",
"npm-check-updates": "^16.10.8",
"nyc": "^15.1.0",
Expand Down
2 changes: 1 addition & 1 deletion api/src/client/typescript/faces/impl/core/Const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export const RESPONSE_TEXT = "responseText";
export const RESPONSE_XML = "responseXML";

/*ajax errors spec 14.4.2*/
export const HTTPERROR = "httpError";
export const HTTP_ERROR = "httpError";
export const EMPTY_RESPONSE = "emptyResponse";
export const MALFORMEDXML = "malformedXML";
export const SERVER_ERROR = "serverError";
Expand Down
35 changes: 20 additions & 15 deletions api/src/client/typescript/faces/impl/xhrCore/ErrorData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
import {
EMPTY_STR, ERROR,
ERROR_MESSAGE,
ERROR_NAME,
ERROR_NAME, HTTP_ERROR,
RESPONSE_TEXT,
RESPONSE_XML, SERVER_ERROR,
SOURCE,
STATUS,
UNKNOWN
} from "../core/Const";
import {Config} from "mona-dish";
import {Config, Optional, XMLQuery} from "mona-dish";

import {EventData} from "./EventData";
import {ExtLang} from "../util/Lang";
Expand All @@ -49,8 +49,8 @@ export enum ErrorType {
export class ErrorData extends EventData implements IErrorData {

type: string = "error";
source: HTMLElement;
sourceId: string;
source: string;

errorName: string;
errorMessage: string;

Expand All @@ -62,19 +62,24 @@ export class ErrorData extends EventData implements IErrorData {

serverErrorName: string;
serverErrorMessage: string;
message: string;
description: string;

constructor(source: string, errorName: string, errorMessage: string, responseText: string = null, responseXML: any = null, responseCode: string = "200", status: string = "", type = ErrorType.CLIENT_ERROR) {
constructor(source: string, errorName: string, errorMessage: string, responseText: string = null, responseXML: Document = null, responseCode: number = -1, statusOverride: string = null, type = ErrorType.CLIENT_ERROR) {
super();
this.source = document.getElementById(source);
this.sourceId = source;
this.source = source;
this.type = ERROR;
this.errorName = errorName;

//tck requires that the type is prefixed to the message itself (jsdoc also) in case of a server error
this.message = this.errorMessage = (type == SERVER_ERROR) ? type + ": " + errorMessage : errorMessage;
this.responseCode = responseCode;
this.errorMessage = errorMessage;
this.responseCode = `${responseCode}`;
this.responseText = responseText;
this.status = status;
this.responseXML = responseXML;

this.status = statusOverride;

this.description = `Status: ${this.status}\nResponse Code: ${this.responseCode}\nError Message: ${this.errorMessage}`;

this.typeDetails = type;

if (type == ErrorType.SERVER_ERROR) {
Expand All @@ -87,8 +92,8 @@ export class ErrorData extends EventData implements IErrorData {
return new ErrorData((e as any)?.source ?? "client", e?.name ?? EMPTY_STR, e?.message ?? EMPTY_STR, e?.stack ?? EMPTY_STR);
}

static fromHttpConnection(source: any, name: string, message: string, responseText, responseCode: number, status: string = EMPTY_STR): ErrorData {
return new ErrorData(source, name, message, responseText, responseCode, `${responseCode}`, status, ErrorType.HTTP_ERROR);
static fromHttpConnection(source: any, name: string, message: string, responseText: string, responseXML: Document, responseCode: number, status: string = EMPTY_STR): ErrorData {
return new ErrorData(source, name, message, responseText, responseXML, responseCode, status, ErrorType.HTTP_ERROR);
}

static fromGeneric(context: Config, errorCode: number, errorType: ErrorType = ErrorType.SERVER_ERROR): ErrorData {
Expand All @@ -100,10 +105,10 @@ export class ErrorData extends EventData implements IErrorData {
let errorMessage = getMsg(context, ERROR_MESSAGE);
let status = getMsg(context, STATUS);
let responseText = getMsg(context, RESPONSE_TEXT);
let responseXML = getMsg(context, RESPONSE_XML);
let responseXML: Document = context.getIf(RESPONSE_XML).value;


return new ErrorData(source, errorName, errorMessage, responseText, responseXML, errorCode + EMPTY_STR, status, errorType);
return new ErrorData(source, errorName, errorMessage, responseText, responseXML, errorCode, status, errorType);
}

private static getMsg(context, param) {
Expand Down
97 changes: 72 additions & 25 deletions api/src/client/typescript/faces/impl/xhrCore/XhrRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

import {AsyncRunnable, IAsyncRunnable} from "../util/AsyncRunnable";
import {Config, DQ} from "mona-dish";
import {Config, DQ, XMLQuery} from "mona-dish";
import {Implementation} from "../AjaxImpl";

import {XhrFormData} from "./XhrFormData";
Expand All @@ -34,14 +34,23 @@ import {
MALFORMEDXML,
NO_TIMEOUT,
ON_ERROR,
ON_EVENT, P_EXECUTE,
ON_EVENT,
P_EXECUTE,
REQ_ACCEPT,
REQ_TYPE_GET,
REQ_TYPE_POST, SOURCE,
REQ_TYPE_POST,
SOURCE,
STATE_EVT_TIMEOUT,
STD_ACCEPT,
URL_ENCODED,
VAL_AJAX, IDENT_NONE, CTX_PARAM_SRC_FRM_ID, CTX_PARAM_SRC_CTL_ID, CTX_PARAM_PPS
VAL_AJAX,
IDENT_NONE,
CTX_PARAM_SRC_FRM_ID,
CTX_PARAM_SRC_CTL_ID,
CTX_PARAM_PPS,
P_AJAX_SOURCE,
RESPONSE_TEXT,
RESPONSE_XML, STATUS, EMPTY_RESPONSE, HTTP_ERROR, UNKNOWN, SERVER_ERROR, EMPTY_STR
} from "../core/Const";
import {
resolveFinalUrl,
Expand Down Expand Up @@ -130,7 +139,6 @@ export class XhrRequest extends AsyncRunnable<XMLHttpRequest> {
executes, partialIdsArray
);


this.contentType = formData.isMultipartRequest ? "undefined" : this.contentType;

// next step the pass through parameters are merged in for post params
Expand Down Expand Up @@ -283,7 +291,7 @@ export class XhrRequest extends AsyncRunnable<XMLHttpRequest> {
// reject means clear queue, in this case we abort entirely the processing
// does not happen yet, we have to probably rethink this strategy in the future
// when we introduce cancel functionality
this.handleGenericError(reject);
this.handleHttpError(reject);
}

/**
Expand All @@ -297,7 +305,7 @@ export class XhrRequest extends AsyncRunnable<XMLHttpRequest> {
// timeout also means we we probably should clear the queue,
// the state is unsafe for the next requests
this.sendEvent(STATE_EVT_TIMEOUT);
this.handleGenericError(resolve);
this.handleHttpError(resolve);
}

/**
Expand All @@ -311,36 +319,75 @@ export class XhrRequest extends AsyncRunnable<XMLHttpRequest> {
private onResponseReceived(resolve: Consumer<any>) {

this.sendEvent(COMPLETE);
/*
* second on error path
*/
if ((this.xhrObject?.status ?? 0) >= 300 || !this?.xhrObject?.responseXML) {
// all errors from the server are resolved without interfering in the queue
this.handleGenericError(resolve);
return;

//request error resolution as per spec:
if(!this.processRequestErrors(resolve)) {
$faces().ajax.response(this.xhrObject, this.responseContext.value ?? {});
}
}

$faces().ajax.response(this.xhrObject, this.responseContext.value ?? {});
private processRequestErrors(resolve: Consumer<any>): boolean {
const responseXML = new XMLQuery(this.xhrObject?.responseXML);
const responseCode = this.xhrObject?.status ?? -1;
if(responseXML.isXMLParserError()) {
// invalid response
const errorName = "Invalid Response";
const errorMessage = "The response xml is invalid";

this.handleGenericResponseError(errorName, errorMessage, MALFORMEDXML, resolve);
return true;
} else if(responseXML.isAbsent()) {
// empty response
const errorName = "Empty Response";
const errorMessage = "The response has provided no data";

this.handleGenericResponseError(errorName, errorMessage, EMPTY_RESPONSE, resolve);
return true;
} else if (responseCode >= 300 || responseCode < 200) {
// other server errors
// all errors from the server are resolved without interfering in the queue
this.handleHttpError(resolve);
return true;
}
//additional errors are application errors and must be handled within the response
return false;
}
private handleGenericResponseError(errorName: string, errorMessage: string, responseStatus: string, resolve: (s?: any) => void) {
const errorData: ErrorData = new ErrorData(
this.internalContext.getIf(CTX_PARAM_SRC_CTL_ID).value,
errorName, errorMessage,
this.xhrObject?.responseText ?? "",
this.xhrObject?.responseXML ?? null,
this.xhrObject.status,
responseStatus
);
this.finalizeError(errorData, resolve);
}

private handleGenericError(resolveOrReject: Function) {
private handleHttpError(resolveOrReject: Function, errorMessage: string = "Generic HTTP Serror") {
this.stopProgress = true;
const errorData = {
type: ERROR,
status: MALFORMEDXML,
responseCode: this.xhrObject?.status ?? 400,
responseText: this.xhrObject?.responseText ?? "Error",
source: this.internalContext.getIf(CTX_PARAM_SRC_CTL_ID).value
};

const errorData = new ErrorData(
this.internalContext.getIf(CTX_PARAM_SRC_CTL_ID).value,
HTTP_ERROR, errorMessage,
this.xhrObject?.responseText ?? "",
this.xhrObject?.responseXML ?? null,
this.xhrObject?.status ?? -1,
HTTP_ERROR
)
this.finalizeError(errorData, resolveOrReject);
}

private finalizeError(errorData: ErrorData, resolveOrReject: Function) {
try {
this.handleError(errorData, true);
} finally {
// we issue a resolveOrReject in this case to allow the system to recover
// reject would clean up the queue
// resolve would trigger the next element in the queue to be processed
resolveOrReject(errorData);
this.stopProgress = true;
}
// non blocking non clearing
}

/**
Expand Down Expand Up @@ -399,7 +446,7 @@ export class XhrRequest extends AsyncRunnable<XMLHttpRequest> {
}

private handleError(exception, responseFormatError: boolean = false) {
const errorData = (responseFormatError) ? ErrorData.fromHttpConnection(exception.source, exception.type, exception.status, exception.responseText, exception.responseCode, exception.status) : ErrorData.fromClient(exception);
const errorData = (responseFormatError) ? ErrorData.fromHttpConnection(exception.source, exception.type, exception.message ?? EMPTY_STR, exception.responseText, exception.responseXML, exception.responseCode, exception.status) : ErrorData.fromClient(exception);
const eventHandler = resolveHandlerFunc(this.requestContext, this.responseContext, ON_ERROR);

Implementation.sendError(errorData, eventHandler);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ describe('tests the addOnEvent and addOnError handling', function () {
expect(onErrorCalled1).to.eq(1);
expect(onErrorCalled2).to.eq(1);
expect(errorTitle).to.eq('Erro21');
expect(errorMessage).to.eq('serverError: Error2 Text');
expect(errorMessage).to.eq('Error2 Text');
} finally {
console.error = oldErr;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -501,9 +501,9 @@ describe('Tests after core when it hits response', function () {
},
onerror: (error: any) => {
expect(error.type).to.eq("error");
expect(error.status).to.eq(EMPTY_STR);
expect(!!error.message).to.eq(true);
expect(!!error.source?.id).to.eq(true);
expect(error.status).to.eq(null);
expect(!!error.errorMessage).to.eq(true);
expect(!!error.source).to.eq(true);
expect(!!error.responseCode).to.eq(true);
expect(!!error.responseText).to.eq(true);
expect(!error.responseXML).to.eq(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,9 +334,9 @@ describe('Tests after core when it hits response', function () {
pass2: "pass2",
onerror: (error: any) => {
expect(error.type).to.eq("error");
expect(error.status).to.eq(EMPTY_STR);
expect(!!error.message).to.eq(true);
expect(!!error.source.id).to.eq(true);
expect(error.status).to.eq(null);
expect(!!error.errorMessage).to.eq(true);
expect(!!error.source).to.eq(true);
expect(!!error.responseCode).to.eq(true);
expect(!!error.responseText).to.eq(true);
expect(!error.responseXML).to.eq(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -739,8 +739,8 @@ describe('Tests of the various aspects of the response protocol functionality',
let errorCalled = 0;
faces.ajax.addOnError((error) => {
expect(error.errorName).to.eq("jakarta.faces.application.ViewExpiredException");
expect(error.errorMessage).to.eq("serverError: View \"/testhmtl.xhtml\" could not be restored.");
expect(error.source.id).to.eq("form1x:button");
expect(error.errorMessage).to.eq("View \"/testhmtl.xhtml\" could not be restored.");
expect(error.source).to.eq("form1x:button");
errorCalled++;
});

Expand Down

0 comments on commit abd4247

Please sign in to comment.