Skip to content

Commit

Permalink
Improved error types
Browse files Browse the repository at this point in the history
  • Loading branch information
TwitchBronBron committed Dec 3, 2024
1 parent 885502b commit b9d8c1d
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 13 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"publish-coverage": "nyc report --reporter=text-lcov | coveralls"
},
"dependencies": {
"@types/request": "^2.47.0",
"chalk": "^2.4.2",
"dateformat": "^3.0.3",
"dayjs": "^1.11.0",
Expand All @@ -42,7 +43,6 @@
"@types/mocha": "^9.0.0",
"@types/node": "^16.11.3",
"@types/q": "^1.5.8",
"@types/request": "^2.47.0",
"@types/sinon": "^10.0.4",
"@types/xml2js": "^0.4.5",
"@typescript-eslint/eslint-plugin": "5.1.0",
Expand Down
37 changes: 33 additions & 4 deletions src/Errors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { HttpResponse, RokuMessages } from './RokuDeploy';
import type * as requestType from 'request';

export class InvalidDeviceResponseCodeError extends Error {
constructor(message: string, public results?: any) {
Expand Down Expand Up @@ -56,17 +57,45 @@ export class MissingRequiredOptionError extends Error {
}
}

/**
* This error is thrown when a Roku device refuses to accept connections because it requires the user to check for updates (even if no updates are actually available).
*/
export class UpdateCheckRequiredError extends Error {

static MESSAGE = `Your device needs to check for updates before accepting connections. Please navigate to System Settings and check for updates and then try again.\n\nhttps://support.roku.com/article/208755668.`;

constructor(response: HttpResponse) {
constructor(
public response: HttpResponse,
public requestOptions: requestType.OptionsWithUrl,
public cause?: Error
) {
super();
this.message = UpdateCheckRequiredError.MESSAGE;
//this exact structure helps `roku-debug` detect this error by finding this status code and then showing a nice popup
this.results = { response: { ...response ?? {}, statusCode: 500 } };
Object.setPrototypeOf(this, UpdateCheckRequiredError.prototype);
}
}

export function isUpdateCheckRequiredError(e: any): e is UpdateCheckRequiredError {
return e?.constructor?.name === 'UpdateCheckRequiredError';
}

/**
* This error is thrown when a Roku device ends the connection unexpectedly, causing an 'ECONNRESET' error. Typically this happens when the device needs to check for updates (even if no updates are available), but it can also happen for other reasons.
*/
export class ConnectionResetError extends Error {

static MESSAGE = `The Roku device ended the connection unexpectedly and may need to check for updates before accepting connections. Please navigate to System Settings and check for updates and then try again.\n\nhttps://support.roku.com/article/208755668.`;

constructor(error: Error, requestOptions: requestType.OptionsWithUrl) {
super();
this.message = ConnectionResetError.MESSAGE;
this.cause = error;
Object.setPrototypeOf(this, ConnectionResetError.prototype);
}

public cause?: Error;
}

results: any;
export function isConnectionResetError(e: any): e is ConnectionResetError {
return e?.constructor?.name === 'ConnectionResetError';
}
29 changes: 21 additions & 8 deletions src/RokuDeploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,20 +462,33 @@ export class RokuDeploy {
//try to "replace" the channel first since that usually works.
let response: HttpResponse;
try {
response = await this.doPostRequest(requestOptions);
} catch (replaceError: any) {
//fail if this is a compile error
if (this.isCompileError(replaceError.message) && options.failOnCompileError) {
throw new errors.CompileError('Compile error', replaceError, replaceError.results);
} else {
requestOptions.formData.mysubmit = 'Install';
try {
response = await this.doPostRequest(requestOptions);
} catch (replaceError: any) {
//fail if this is a compile error
if (this.isCompileError(replaceError.message) && options.failOnCompileError) {
throw new errors.CompileError('Compile error', replaceError, replaceError.results);
} else {
requestOptions.formData.mysubmit = 'Install';
response = await this.doPostRequest(requestOptions);
}
}
} catch (e: any) {
//if this is a 577 error, we have high confidence that the device needs to do an update check
if (e?.results?.response?.statusCode === 577) {
throw new errors.UpdateCheckRequiredError(response, requestOptions, e);

//a reset connection could be cause by several things, but most likely it's due to the device needing to check for updates
} else if (e.code === 'ECONNRESET') {
throw new errors.ConnectionResetError(e, requestOptions);
} else {
throw e;
}
}

//if we got a non-error status code, but the body includes a message about needing to update, throw a special error
if (this.isUpdateCheckRequiredResponse(response.body)) {
throw new errors.UpdateCheckRequiredError(response);
throw new errors.UpdateCheckRequiredError(response, requestOptions);
}

if (options.failOnCompileError) {
Expand Down

0 comments on commit b9d8c1d

Please sign in to comment.