@fleetbase/fleetops-engine
Version:
Fleet & Transport Management Extension for Fleetbase
288 lines (232 loc) • 8.61 kB
JavaScript
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { task } from 'ember-concurrency';
import copyToClipboard from '@fleetbase/ember-core/utils/copy-to-clipboard';
const SENSITIVE_METADATA_KEYS = ['password', 'secret', 'session', 'token'];
export default class ModalsTelematicConnectionDiagnosticsComponent extends Component {
fetch;
modalsManager;
notifications;
result;
startedAt;
completedAt;
constructor(owner, { options = {} }) {
super(...arguments);
this.options = options;
this.setupOptions();
this.runTest.perform();
}
get telematic() {
return this.options.telematic;
}
get providerLabel() {
return this.telematic?.provider_descriptor?.label ?? this.telematic?.provider ?? 'Provider';
}
get providerKey() {
return this.telematic?.provider;
}
get connectionState() {
if (this.runTest.isRunning) {
return 'testing';
}
if (!this.result) {
return 'idle';
}
return this.result.success ? 'success' : 'failed';
}
get connectionStateTitle() {
if (this.connectionState === 'testing') {
return 'Testing provider connection...';
}
if (this.connectionState === 'success') {
return 'Connection verified';
}
if (this.connectionState === 'failed') {
return 'Connection failed';
}
return 'Ready to test';
}
get connectionStateMessage() {
if (this.connectionState === 'testing') {
return 'Waiting for the provider to accept the saved credentials.';
}
if (this.connectionState === 'idle') {
return 'Run a connection test to verify the saved provider credentials.';
}
if (this.isDuplicateConnectionMessage) {
return null;
}
return this.result?.message;
}
get isDuplicateConnectionMessage() {
const message = this.result?.message;
if (!message) {
return false;
}
return ['connection successful', 'connection verified'].includes(message.trim().toLowerCase());
}
get connectionMetadataEntries() {
const metadata = this.result?.metadata ?? {};
return Object.entries(metadata)
.filter(([key, value]) => {
if (value === null || value === undefined || value === '') {
return false;
}
return !this.isSensitiveMetadataKey(key);
})
.map(([key, value]) => ({
label: this.formatMetadataLabel(key),
value: this.formatDiagnosticValue(value),
}));
}
get hasConnectionMetadata() {
return this.connectionMetadataEntries.length > 0;
}
get connectionDiagnosticEntries() {
const entries = [];
const startedAt = this.startedAt;
const completedAt = this.completedAt;
entries.push({
time: this.formatDiagnosticTime(startedAt),
tone: 'muted',
text: `Provider selected: ${this.providerLabel}${this.providerKey ? ` (${this.providerKey})` : ''}`,
});
entries.push({
time: this.formatDiagnosticTime(startedAt),
tone: 'info',
text: 'Sending saved provider connection test request',
});
if (this.connectionState === 'testing') {
entries.push({
time: this.formatDiagnosticTime(),
tone: 'info',
text: 'Awaiting provider response',
});
return entries;
}
if (this.connectionState === 'idle') {
entries.push({
time: this.formatDiagnosticTime(),
tone: 'muted',
text: 'Connection test has not been run yet',
});
return entries;
}
entries.push({
time: this.formatDiagnosticTime(completedAt),
tone: 'info',
text: 'Provider response received',
});
if (this.hasConnectionMetadata) {
entries.push({
time: this.formatDiagnosticTime(completedAt),
tone: 'muted',
text: `Provider metadata: ${this.connectionMetadataEntries.map((entry) => `${entry.label}=${entry.value}`).join(', ')}`,
});
}
if (this.connectionState === 'success') {
entries.push({
time: this.formatDiagnosticTime(completedAt),
tone: 'success',
text: 'Connection verified',
});
return entries;
}
entries.push({
time: this.formatDiagnosticTime(completedAt),
tone: 'danger',
text: `Connection failed${this.result?.message ? `: ${this.formatDiagnosticValue(this.result.message)}` : ''}`,
});
return entries;
}
get connectionDiagnosticText() {
return this.connectionDiagnosticEntries.map((entry) => `[${entry.time}] ${entry.text}`).join('\n');
}
setupOptions() {
this.options.title = this.options.title ?? 'Test Connection';
this.options.acceptButtonText = 'Run Test';
this.options.acceptButtonIcon = 'plug';
this.options.declineButtonText = this.options.declineButtonText ?? 'Close';
this.options.confirm = async (modal) => {
modal.startLoading();
try {
await this.runTest.perform();
} finally {
modal.stopLoading();
}
};
}
*runTest() {
if (!this.telematic?.id) {
return;
}
this.startedAt = new Date();
this.completedAt = null;
this.modalsManager.setOption('acceptButtonDisabled', true);
this.modalsManager.setOption('acceptButtonText', 'Testing...');
try {
const result = yield this.fetch.post(`telematics/${this.telematic.id}/test-connection`);
this.result = result;
this.completedAt = new Date();
yield this.options.onTested?.(result);
} catch (error) {
this.completedAt = new Date();
this.result = {
success: false,
message: error.message || 'Connection test failed',
};
} finally {
this.modalsManager.setOption('acceptButtonDisabled', false);
this.modalsManager.setOption('acceptButtonText', this.result ? 'Test Again' : 'Run Test');
}
}
copyConnectionDiagnostics() {
copyToClipboard(this.connectionDiagnosticText)
.then(() => {
this.notifications.success('Connection diagnostics copied.');
})
.catch(() => {
this.notifications.error('Unable to copy connection diagnostics.');
});
}
formatMetadataLabel(key) {
return key.replace(/_/g, ' ').replace(/\b\w/g, (char) => char.toUpperCase());
}
formatDiagnosticValue(value) {
const sanitizedValue = this.sanitizeDiagnosticValue(value);
if (typeof sanitizedValue === 'object') {
return JSON.stringify(sanitizedValue);
}
return String(sanitizedValue);
}
sanitizeDiagnosticValue(value, seen = new WeakSet()) {
if (!value || typeof value !== 'object') {
return value;
}
if (seen.has(value)) {
return '[circular]';
}
seen.add(value);
if (Array.isArray(value)) {
return value.map((item) => this.sanitizeDiagnosticValue(item, seen));
}
return Object.entries(value).reduce((acc, [key, item]) => {
if (!this.isSensitiveMetadataKey(key)) {
acc[key] = this.sanitizeDiagnosticValue(item, seen);
}
return acc;
}, {});
}
formatDiagnosticTime(date = new Date()) {
if (!(date instanceof Date)) {
return '--:--:--';
}
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false });
}
isSensitiveMetadataKey(key) {
return SENSITIVE_METADATA_KEYS.some((sensitiveKey) => key.toLowerCase().includes(sensitiveKey));
}
}