Adding a support submission button on CLI to assist in support (#3206)

* Adding a support submission button on CLI to assist in identifying issues

* Fixes following rebase

* Rename build_api to BuildApi.js
10.9-maintenance
J Blackman 2023-01-05 06:16:20 +11:00 committed by GitHub
parent 1344baa59c
commit 902f979878
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 111 additions and 21 deletions

View File

@ -77,6 +77,9 @@
"cancel": { "cancel": {
"message": "Cancel" "message": "Cancel"
}, },
"submit": {
"message": "Submit"
},
"autoConnectEnabled": { "autoConnectEnabled": {
"message": "Auto-Connect: Enabled - Configurator automatically tries to connect when new port is detected" "message": "Auto-Connect: Enabled - Configurator automatically tries to connect when new port is detected"
}, },
@ -533,15 +536,24 @@
"message" : "Configurator: <strong>{{configuratorVersion}}</strong>", "message" : "Configurator: <strong>{{configuratorVersion}}</strong>",
"description": "Message that appears in the GUI log panel indicating Configurator version" "description": "Message that appears in the GUI log panel indicating Configurator version"
}, },
"buildServerLoaded": { "buildServerSuccess": {
"message" : "Loaded builds information for $1 from build server." "message" : "Success: $1"
}, },
"buildServerLoadFailed": { "buildServerFailure": {
"message" : "<b>Build server query for $1 releases failed, using cached information. Reason: <code>$2</code></b>" "message" : "<b>Build server failure: $1 <code>$2</code></b>"
}, },
"buildServerUsingCached": { "buildServerUsingCached": {
"message" : "Using cached builds information for $1." "message" : "Using cached builds information for $1."
}, },
"buildServerSupportRequestSubmission": {
"message": "<br>*** Support data submitted *** <br>Id: $1<br><br><br># copy ID and provide to the betaflight team."
},
"supportWarningDialogTitle": {
"message": "Confirm Data Submission"
},
"supportWarningDialogText": {
"message": "Please confirm data submission to the Betaflight team.<br><br>This process will run some commands and submit the output to the build server.<br><br>You will then be provided a unique identifier for your data submission.<br><br>Please ensure you provide this unique identifier to the Betaflight team when using Discord or opening an issues on Github."
},
"releaseCheckLoaded": { "releaseCheckLoaded": {
"message" : "Loaded release information for $1 from GitHub." "message" : "Loaded release information for $1 from GitHub."
}, },
@ -2885,6 +2897,9 @@
"cliLoadFromFileBtn": { "cliLoadFromFileBtn": {
"message": "Load from file" "message": "Load from file"
}, },
"cliSupportRequestBtn": {
"message": "Submit Support Data"
},
"cliConfirmSnippetDialogTitle": { "cliConfirmSnippetDialogTitle": {
"message": "Loaded file <strong>{{fileName}}</strong>. Review the loaded commands" "message": "Loaded file <strong>{{fileName}}</strong>. Review the loaded commands"
}, },

View File

@ -2,10 +2,10 @@ import GUI from "./gui";
import { i18n } from "./localization"; import { i18n } from "./localization";
import { get as getStorage, set as setStorage } from "./SessionStorage"; import { get as getStorage, set as setStorage } from "./SessionStorage";
export default class ReleaseLoader { export default class BuildApi {
constructor (url) { constructor () {
this._url = url; this._url = 'https://build.betaflight.com';
this._cacheExpirationPeriod = 3600 * 1000; this._cacheExpirationPeriod = 3600 * 1000;
} }
@ -29,8 +29,6 @@ export default class ReleaseLoader {
if (!cachedData || !cachedLastUpdate || dataTimestamp - cachedLastUpdate > this._cacheExpirationPeriod) { if (!cachedData || !cachedLastUpdate || dataTimestamp - cachedLastUpdate > this._cacheExpirationPeriod) {
$.get(url, function (info) { $.get(url, function (info) {
GUI.log(i18n.getMessage('buildServerLoaded', [url]));
// cache loaded info // cache loaded info
const object = {}; const object = {};
object[dataTag] = info; object[dataTag] = info;
@ -38,7 +36,7 @@ export default class ReleaseLoader {
setStorage(object); setStorage(object);
onSuccess(info); onSuccess(info);
}).fail(xhr => { }).fail(xhr => {
GUI.log(i18n.getMessage('buildServerLoadFailed', [url, `HTTP ${xhr.status}`])); GUI.log(i18n.getMessage('buildServerFailure', [url, `HTTP ${xhr.status}`]));
if (onFailure !== undefined) { if (onFailure !== undefined) {
onFailure(); onFailure();
} else { } else {
@ -75,7 +73,41 @@ export default class ReleaseLoader {
GUI.log(i18n.getMessage('buildServerLoaded', [path])); GUI.log(i18n.getMessage('buildServerLoaded', [path]));
onSuccess(data); onSuccess(data);
}).fail(xhr => { }).fail(xhr => {
GUI.log(i18n.getMessage('buildServerLoadFailed', [path, `HTTP ${xhr.status}`])); GUI.log(i18n.getMessage('buildServerFailure', [path, `HTTP ${xhr.status}`]));
if (onFailure !== undefined) {
onFailure();
}
});
}
getSupportCommands(onSuccess, onFailure) {
const url = `${this._url}/api/support/commands`;
$.get(url, function (data) {
onSuccess(data);
}).fail(xhr => {
GUI.log(i18n.getMessage('buildServerFailure', [url, `HTTP ${xhr.status}`]));
if (onFailure !== undefined) {
onFailure();
}
});
}
submitSupportData(data, onSuccess, onFailure) {
const url = `${this._url}/api/support`;
$.ajax({
url: url,
type: "POST",
data: data,
contentType: "text/plain",
dataType: "text",
success: function(response) {
onSuccess(response);
},
}).fail(xhr => {
GUI.log(i18n.getMessage('buildServerFailure', [`HTTP ${xhr.status}`]));
if (onFailure !== undefined) { if (onFailure !== undefined) {
onFailure(); onFailure();
} }
@ -92,11 +124,11 @@ export default class ReleaseLoader {
contentType: "application/json", contentType: "application/json",
dataType: "json", dataType: "json",
success: function(data) { success: function(response) {
onSuccess(data); onSuccess(response);
}, },
}).fail(xhr => { }).fail(xhr => {
GUI.log(i18n.getMessage('buildServerLoadFailed', [url, `HTTP ${xhr.status}`])); GUI.log(i18n.getMessage('buildServerFailure', [url, `HTTP ${xhr.status}`]));
if (onFailure !== undefined) { if (onFailure !== undefined) {
onFailure(); onFailure();
} }
@ -107,10 +139,10 @@ export default class ReleaseLoader {
const url = `${this._url}/api/builds/${key}/status`; const url = `${this._url}/api/builds/${key}/status`;
$.get(url, function (data) { $.get(url, function (data) {
GUI.log(i18n.getMessage('buildServerLoaded', [url])); GUI.log(i18n.getMessage('buildServerSuccess', [url]));
onSuccess(data); onSuccess(data);
}).fail(xhr => { }).fail(xhr => {
GUI.log(i18n.getMessage('buildServerLoadFailed', [url, `HTTP ${xhr.status}`])); GUI.log(i18n.getMessage('buildServerFailure', [url, `HTTP ${xhr.status}`]));
if (onFailure !== undefined) { if (onFailure !== undefined) {
onFailure(); onFailure();
} }

View File

@ -1,6 +1,7 @@
import { i18n } from "../localization"; import { i18n } from "../localization";
import Clipboard from "../Clipboard"; import Clipboard from "../Clipboard";
import GUI from '../gui'; import GUI from '../gui';
import BuildApi from '../BuildApi';
const cli = { const cli = {
lineDelayMs: 15, lineDelayMs: 15,
@ -13,6 +14,7 @@ const cli = {
copyButton: null, copyButton: null,
windowWrapper: null, windowWrapper: null,
}, },
lastArrival: 0,
}; };
function removePromptHash(promptText) { function removePromptHash(promptText) {
@ -89,11 +91,16 @@ cli.initialize = function (callback) {
const enterKeyCode = 13; const enterKeyCode = 13;
function clearHistory() {
self.outputHistory = "";
self.GUI.windowWrapper.empty();
}
function executeCommands(outString) { function executeCommands(outString) {
self.history.add(outString.trim()); self.history.add(outString.trim());
const outputArray = outString.split("\n"); const outputArray = outString.split("\n");
Promise.reduce(outputArray, function(delay, line, index) { return Promise.reduce(outputArray, function(delay, line, index) {
return new Promise(function (resolve) { return new Promise(function (resolve) {
GUI.timeout_add('CLI_send_slowly', function () { GUI.timeout_add('CLI_send_slowly', function () {
let processingDelay = self.lineDelayMs; let processingDelay = self.lineDelayMs;
@ -181,8 +188,7 @@ cli.initialize = function (callback) {
}); });
$('.tab-cli .clear').click(function() { $('.tab-cli .clear').click(function() {
self.outputHistory = ""; clearHistory();
self.GUI.windowWrapper.empty();
}); });
if (Clipboard.available) { if (Clipboard.available) {
@ -247,6 +253,38 @@ cli.initialize = function (callback) {
}); });
}); });
$('.tab-cli .support').click(function() {
function submitSupportData() {
clearHistory();
const api = new BuildApi();
api.getSupportCommands((commands) => {
executeCommands(commands.join('\n')).then(() => {
const delay = setInterval(() => {
const time = new Date().getTime();
if (self.lastArrival < time - 250) {
clearInterval(delay);
const text = self.outputHistory;
api.submitSupportData(text, (key) => {
writeToOutput(i18n.getMessage('buildServerSupportRequestSubmission', [key]));
});
}
}, 250);
});
});
}
const dialogSettings = {
title: i18n.getMessage("supportWarningDialogTitle"),
text: i18n.getMessage("supportWarningDialogText"),
buttonYesText: i18n.getMessage("submit"),
buttonNoText: i18n.getMessage("cancel"),
buttonYesCallback: submitSupportData,
};
GUI.showYesNoDialog(dialogSettings);
});
// Tab key detection must be on keydown, // Tab key detection must be on keydown,
// `keypress`/`keyup` happens too late, as `textarea` will have already lost focus. // `keypress`/`keyup` happens too late, as `textarea` will have already lost focus.
textarea.keydown(function (event) { textarea.keydown(function (event) {
@ -464,6 +502,8 @@ cli.read = function (readInfo) {
} }
this.lastArrival = new Date().getTime();
if (!CONFIGURATOR.cliValid && validateText.indexOf('CLI') !== -1) { if (!CONFIGURATOR.cliValid && validateText.indexOf('CLI') !== -1) {
GUI.log(i18n.getMessage('cliEnter')); GUI.log(i18n.getMessage('cliEnter'));
CONFIGURATOR.cliValid = true; CONFIGURATOR.cliValid = true;

View File

@ -2,11 +2,11 @@ import { i18n } from '../localization';
import GUI from '../gui'; import GUI from '../gui';
import { get as getConfig, set as setConfig } from '../ConfigStorage'; import { get as getConfig, set as setConfig } from '../ConfigStorage';
import { get as getStorage, set as setStorage } from '../SessionStorage'; import { get as getStorage, set as setStorage } from '../SessionStorage';
import ReleaseLoader from '../release_loader'; import BuildApi from '../BuildApi';
const firmware_flasher = { const firmware_flasher = {
targets: null, targets: null,
releaseLoader: new ReleaseLoader('https://build.betaflight.com'), releaseLoader: new BuildApi(),
localFirmwareLoaded: false, localFirmwareLoaded: false,
selectedBoard: undefined, selectedBoard: undefined,
boardNeedsVerification: false, boardNeedsVerification: false,

View File

@ -24,6 +24,9 @@
<div class="btn save_btn"> <div class="btn save_btn">
<a class="copy" href="#" i18n="cliCopyToClipboardBtn"></a> <a class="copy" href="#" i18n="cliCopyToClipboardBtn"></a>
</div> </div>
<div class="btn save_btn">
<a class="support" href="#" i18n="cliSupportRequestBtn"></a>
</div>
</div> </div>
<div class="toolbar_expand_btn" nbrow="2"> <div class="toolbar_expand_btn" nbrow="2">