Add error service

This commit is contained in:
Kyrylo Gliebov 2018-12-07 20:18:33 +01:00 committed by Kirill Glebov
parent 0ad6b11ebf
commit 6f6c6b7714

View file

@ -26,21 +26,6 @@ let $ = require("jquery");
require("console.css"); require("console.css");
require("bootstrap-slider"); require("bootstrap-slider");
/*
* Get an object field, verifying its presence and type.
*/
let getValidField = function (object, field, type) {
let value;
if (!(field in object)) {
throw Error("\"" + field + "\" field is missing");
}
value = object[field];
if (typeof (value) != typeof (type)) {
throw Error("invalid \"" + field + "\" field type: " + typeof (value));
}
return value;
};
let scrollToBottom = function(id) { let scrollToBottom = function(id) {
const el = document.getElementById(id); const el = document.getElementById(id);
if (el) { if (el) {
@ -48,6 +33,54 @@ let scrollToBottom = function(id) {
} }
}; };
function ErrorList(props) {
let list = [];
if (props.list) {
list = props.list.map((message, key) => { return <ErrorItem key={key} message={message} /> });
}
return (
<React.Fragment>
{list}
</React.Fragment>
);
}
function ErrorItem(props) {
return (
<div className="alert alert-danger alert-dismissable" >
<button type="button" className="close" data-dismiss="alert" aria-hidden="true">
<span className="pficon pficon-close" />
</button>
<span className="pficon pficon-error-circle-o" />
{props.message}
</div>
);
}
let ErrorService = class {
constructor() {
this.addMessage = this.addMessage.bind(this);
this.errors = [];
}
addMessage(message) {
if (typeof message === "object" && message !== null) {
if ("toString" in message) {
message = message.toString();
} else {
message = _("unknown error");
}
}
if (typeof message === "string" || message instanceof String) {
if (this.errors.indexOf(message) === -1) {
this.errors.push(message);
}
}
}
};
/* /*
* An auto-loading buffer of recording's packets. * An auto-loading buffer of recording's packets.
*/ */
@ -55,10 +88,11 @@ let PacketBuffer = class {
/* /*
* Initialize a buffer. * Initialize a buffer.
*/ */
constructor(matchList) { constructor(matchList, reportError) {
this.handleError = this.handleError.bind(this); this.handleError = this.handleError.bind(this);
this.handleStream = this.handleStream.bind(this); this.handleStream = this.handleStream.bind(this);
this.handleDone = this.handleDone.bind(this); this.handleDone = this.handleDone.bind(this);
this.getValidField = this.getValidField.bind(this);
/* RegExp used to parse message's timing field */ /* RegExp used to parse message's timing field */
this.timingRE = new RegExp( this.timingRE = new RegExp(
/* Delay (1) */ /* Delay (1) */
@ -81,6 +115,7 @@ let PacketBuffer = class {
); );
/* List of matches to apply when loading the buffer from Journal */ /* List of matches to apply when loading the buffer from Journal */
this.matchList = matchList; this.matchList = matchList;
this.reportError = reportError;
/* /*
* An array of two-element arrays (tuples) each containing a * An array of two-element arrays (tuples) each containing a
* packet index and a deferred object. The list is kept sorted to * packet index and a deferred object. The list is kept sorted to
@ -119,6 +154,21 @@ let PacketBuffer = class {
this.done = false; this.done = false;
} }
/*
* Get an object field, verifying its presence and type.
*/
getValidField(object, field, type) {
let value;
if (!(field in object)) {
this.reportError("\"" + field + "\" field is missing");
}
value = object[field];
if (typeof (value) != typeof (type)) {
this.reportError("invalid \"" + field + "\" field type: " + typeof (value));
}
return value;
}
/* /*
* Return a promise which is resolved when a packet at a particular * Return a promise which is resolved when a packet at a particular
* index is received by the buffer. The promise is rejected with a * index is received by the buffer. The promise is rejected with a
@ -235,6 +285,7 @@ let PacketBuffer = class {
this.idxDfdList[i][1].reject(error); this.idxDfdList[i][1].reject(error);
} }
this.idxDfdList = []; this.idxDfdList = [];
this.reportError(error);
} }
/* /*
@ -257,7 +308,7 @@ let PacketBuffer = class {
/* Match next timing entry */ /* Match next timing entry */
matches = this.timingRE.exec(timing); matches = this.timingRE.exec(timing);
if (matches === null) { if (matches === null) {
throw Error("invalid timing string"); this.reportError(_("invalid timing string"));
} else if (matches[0] == "") { } else if (matches[0] == "") {
break; break;
} }
@ -297,7 +348,7 @@ let PacketBuffer = class {
/* Add (replacement) input characters */ /* Add (replacement) input characters */
s = in_txt.slice(in_txt_pos, in_txt_pos += x); s = in_txt.slice(in_txt_pos, in_txt_pos += x);
if (s.length != x) { if (s.length != x) {
throw Error("timing entry out of input bounds"); this.reportError(_("timing entry out of input bounds"));
} }
io.push(s); io.push(s);
break; break;
@ -319,7 +370,7 @@ let PacketBuffer = class {
/* Add (replacement) output characters */ /* Add (replacement) output characters */
s = out_txt.slice(out_txt_pos, out_txt_pos += x); s = out_txt.slice(out_txt_pos, out_txt_pos += x);
if (s.length != x) { if (s.length != x) {
throw Error("timing entry out of output bounds"); this.reportError(_("timing entry out of output bounds"));
} }
io.push(s); io.push(s);
break; break;
@ -348,10 +399,10 @@ let PacketBuffer = class {
} }
if (in_txt_pos < in_txt.length) { if (in_txt_pos < in_txt.length) {
throw Error("extra input present"); this.reportError(_("extra input present"));
} }
if (out_txt_pos < out_txt.length) { if (out_txt_pos < out_txt.length) {
throw Error("extra output present"); this.reportError(_("extra output present"));
} }
if (io.length > 0) { if (io.length > 0) {
@ -375,24 +426,24 @@ let PacketBuffer = class {
const string = String(); const string = String();
/* Check version */ /* Check version */
ver = getValidField(message, "ver", string); ver = this.getValidField(message, "ver", string);
matches = ver.match("^(\\d+)\\.(\\d+)$"); matches = ver.match("^(\\d+)\\.(\\d+)$");
if (matches === null || matches[1] > 2) { if (matches === null || matches[1] > 2) {
throw Error("\"ver\" field has invalid value: " + ver); this.reportError("\"ver\" field has invalid value: " + ver);
} }
/* TODO Perhaps check host, rec, user, term, and session fields */ /* TODO Perhaps check host, rec, user, term, and session fields */
/* Extract message ID */ /* Extract message ID */
id = getValidField(message, "id", number); id = this.getValidField(message, "id", number);
if (id <= this.id) { if (id <= this.id) {
throw Error("out of order \"id\" field value: " + id); this.reportError("out of order \"id\" field value: " + id);
} }
/* Extract message time position */ /* Extract message time position */
pos = getValidField(message, "pos", number); pos = this.getValidField(message, "pos", number);
if (pos < this.message_pos) { if (pos < this.message_pos) {
throw Error("out of order \"pos\" field value: " + pos); this.reportError("out of order \"pos\" field value: " + pos);
} }
/* Update last received message ID and time position */ /* Update last received message ID and time position */
@ -401,9 +452,9 @@ let PacketBuffer = class {
/* Parse message data */ /* Parse message data */
this.parseMessageData( this.parseMessageData(
getValidField(message, "timing", string), this.getValidField(message, "timing", string),
getValidField(message, "in_txt", string), this.getValidField(message, "in_txt", string),
getValidField(message, "out_txt", string)); this.getValidField(message, "out_txt", string));
} }
/* /*
@ -573,7 +624,6 @@ export class Player extends React.Component {
containerWidth: 630, containerWidth: 630,
currentTsPost: 0, currentTsPost: 0,
scale: 1, scale: 1,
error: null,
input: "", input: "",
mark: 0, mark: 0,
}; };
@ -581,7 +631,9 @@ export class Player extends React.Component {
this.containerHeight = 290; this.containerHeight = 290;
/* Auto-loading buffer of recording's packets */ /* Auto-loading buffer of recording's packets */
this.buf = new PacketBuffer(this.props.matchList); this.error_service = new ErrorService();
this.reportError = this.error_service.addMessage;
this.buf = new PacketBuffer(this.props.matchList, this.reportError);
/* Current recording time, ms */ /* Current recording time, ms */
this.recTS = 0; this.recTS = 0;
@ -655,7 +707,7 @@ export class Player extends React.Component {
/* Handle packet retrieval error */ /* Handle packet retrieval error */
handleError(error) { handleError(error) {
if (error !== null) { if (error !== null) {
this.setState({error: error}); this.reportError(error);
console.warn(error); console.warn(error);
} }
} }
@ -1046,18 +1098,6 @@ export class Player extends React.Component {
"float": "right", "float": "right",
}; };
let error = "";
if (this.state.error) {
error = (
<div className="alert alert-danger alert-dismissable" >
<button type="button" className="close" data-dismiss="alert" aria-hidden="true">
<span className="pficon pficon-close" />
</button>
<span className="pficon pficon-error-circle-o" />
{this.state.error}.
</div>);
}
// ensure react never reuses this div by keying it with the terminal widget // ensure react never reuses this div by keying it with the terminal widget
return ( return (
<div id="recording-wrap"> <div id="recording-wrap">
@ -1124,8 +1164,8 @@ export class Player extends React.Component {
<div id="input-player-wrap"> <div id="input-player-wrap">
<InputPlayer input={this.state.input} /> <InputPlayer input={this.state.input} />
</div> </div>
<ErrorList list={this.error_service.errors} />
</div> </div>
{error}
</div> </div>
</div> </div>
</div> </div>