Skip to content

Commit

Permalink
Major features | Update package.json version to relate it with pixxel…
Browse files Browse the repository at this point in the history
…hq/yamcs

1. Add mechanism to authenticate the plugin against a username/password protected yamcs server.
2. Modify parameter loading from the MDb, accounting for commandHistory parameters.
  • Loading branch information
swarup-n committed Jan 25, 2024
1 parent 19513c3 commit a58aba5
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 21 deletions.
4 changes: 2 additions & 2 deletions src/actions/exportToCSV/ExportToCSVAction.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
import { OBJECT_TYPES } from "../../const.js";
import {idToQualifiedName} from "../../utils.js";
import { customFetch, idToQualifiedName } from "../../utils.js";
import { saveAs } from 'file-saver';

const SUPPORTED_TYPES = [OBJECT_TYPES.TELEMETRY_OBJECT_TYPE, OBJECT_TYPES.AGGREGATE_TELEMETRY_TYPE];
Expand Down Expand Up @@ -115,7 +115,7 @@ export default class ExportToCSVAction {
url += `&parameters=${parameterIdsString}`;
url += `&delimiter=COMMA`;

const response = await fetch(url);
const response = await customFetch(url);
if (!response.ok) {
return response.json();
} else {
Expand Down
3 changes: 2 additions & 1 deletion src/const.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ export const OBJECT_TYPES = {
OPERATOR_STATUS_TYPE: 'yamcs.operatorStatus',
POLL_QUESTION_TYPE: 'yamcs.pollQuestion',
ALARMS_TYPE: 'yamcs.alarms',
GLOBAL_STATUS_TYPE: 'yamcs.globalStatus'
GLOBAL_STATUS_TYPE: 'yamcs.globalStatus',
COMMAND_HISTORY_TYPE: 'yamcs.cmdHist'
};
export const MDB_TYPE = 'yamcs.mdbchanges';

Expand Down
3 changes: 2 additions & 1 deletion src/providers/fault-mgmt-providers/fault-action-provider.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { customFetch } from '../../utils.js';
import { FAULT_MANAGEMENT_ALARMS, FAULT_MANAGEMENT_DEFAULT_SHELVE_DURATION } from './fault-mgmt-constants.js';

export default class FaultActionProvider {
Expand Down Expand Up @@ -55,6 +56,6 @@ export default class FaultActionProvider {
}

_sendRequest(url, options) {
return fetch(url, options);
return customFetch(url, options);
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { customFetch } from '../../utils.js';
import { FAULT_MANAGEMENT_ALARMS, FAULT_MANAGEMENT_TYPE } from './fault-mgmt-constants.js';

export default class HistoricalFaultProvider {
Expand All @@ -15,7 +16,7 @@ export default class HistoricalFaultProvider {
async request() {
let url = `${this.url}api/processors/${this.instance}/${this.processor}/${FAULT_MANAGEMENT_ALARMS}`;

const res = await fetch(url);
const res = await customFetch(url);
const faultsData = await res.json();

return faultsData.alarms?.map(this.faultModelConverter);
Expand Down
5 changes: 3 additions & 2 deletions src/providers/latest-telemetry-provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import {
getValue,
idToQualifiedName,
qualifiedNameFromParameterId,
qualifiedNameToId
qualifiedNameToId,
customFetch
} from '../utils.js';

const BATCH_DEBOUNCE_MS = 100;
Expand Down Expand Up @@ -98,7 +99,7 @@ export default class LatestTelemetryProvider {
fromCache: true
};

const response = await fetch(this.#buildUrl(), {
const response = await customFetch(this.#buildUrl(), {
method: 'POST',
body: JSON.stringify(requestBody)
});
Expand Down
23 changes: 17 additions & 6 deletions src/providers/object-provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ export default class YamcsObjectProvider {
}

async #loadTelemetryDictionary() {
const operation = 'parameters?details=yes&limit=1000';
const operation = 'parameters?details=yes';
const parameterUrl = this.url + 'api/mdb/' + this.instance + '/' + operation;
const url = this.#getMdbUrl('space-systems');
const spaceSystems = await accumulateResults(url, {}, 'spaceSystems', []);
Expand Down Expand Up @@ -370,15 +370,18 @@ export default class YamcsObjectProvider {
}

const isAggregate = this.#isAggregate(parameter);
const isCommandHistory = this.#isCommandHistory(parameter);
let aggregateHasMembers = false;

if (this.limitOverrides[qualifiedName] !== undefined) {
obj.configuration.limits = this.limitOverrides[qualifiedName];
} else if (parameter.type.defaultAlarm) {
obj.configuration.limits = this.#convertToLimits(parameter.type.defaultAlarm);
if(!isCommandHistory) {
if (this.limitOverrides[qualifiedName] !== undefined) {
obj.configuration.limits = this.limitOverrides[qualifiedName];
} else if (parameter.type.defaultAlarm) {
obj.configuration.limits = this.#convertToLimits(parameter.type.defaultAlarm);
}
}

if (!isAggregate) {
if (!isAggregate && !isCommandHistory) {
const key = 'value';
const telemetryValue = {
key,
Expand Down Expand Up @@ -476,6 +479,10 @@ export default class YamcsObjectProvider {
return parameter?.type?.engType === 'aggregate';
}

#isCommandHistory(parameter) {
return parameter?.dataSource === 'COMMAND' || parameter?.dataSource === 'COMMAND_HISTORY';
}

#isImage(obj) {
return (obj.type === OBJECT_TYPES.IMAGE_OBJECT_TYPE);
}
Expand Down Expand Up @@ -542,6 +549,10 @@ export default class YamcsObjectProvider {
}
}

if(this.#isCommandHistory(parameter)) {
return OBJECT_TYPES.COMMAND_HISTORY_TYPE;
}

if (this.#isAggregate(parameter) && this.#aggregateHasMembers(parameter)) {
return OBJECT_TYPES.AGGREGATE_TELEMETRY_TYPE;
}
Expand Down
7 changes: 6 additions & 1 deletion src/providers/realtime-provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,14 @@ export default class RealtimeProvider {
return;
}

let wsUrl = `${this.url}`;
this.lastSubscriptionId = 1;
this.connected = false;

this.postConnect();
}

postConnect(access_token) {
let wsUrl = `${this.url}`;
this.socket = new WebSocket(wsUrl);

this.socket.onopen = () => {
Expand Down
3 changes: 2 additions & 1 deletion src/providers/user/operator-status-telemetry.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
import {
customFetch,
idToQualifiedName
} from '../../utils.js';

Expand Down Expand Up @@ -50,7 +51,7 @@ export default class OperatorStatusTelemetry {
let success = false;

try {
const result = await fetch(setParameterUrl, {
const result = await customFetch(setParameterUrl, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
Expand Down
3 changes: 2 additions & 1 deletion src/providers/user/poll-question-telemetry.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
import {
customFetch,
idToQualifiedName
} from '../../utils.js';

Expand Down Expand Up @@ -54,7 +55,7 @@ export default class PollQuestionTelemetry {
let success = false;

try {
const result = await fetch(setParameterUrl, {
const result = await customFetch(setParameterUrl, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
Expand Down
3 changes: 2 additions & 1 deletion src/providers/user/user-provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import createYamcsUser from './createYamcsUser.js';
import { EventEmitter } from 'eventemitter3';
import { customFetch } from '../../utils.js';

export default class UserProvider extends EventEmitter {
constructor(openmct, {userEndpoint, roleStatus, latestTelemetryProvider, realtimeTelemetryProvider, pollQuestionParameter, pollQuestionTelemetry}) {
Expand Down Expand Up @@ -187,7 +188,7 @@ export default class UserProvider extends EventEmitter {

async #getUserInfo() {
try {
const res = await fetch(this.userEndpoint);
const res = await customFetch(this.userEndpoint);
const info = await res.json();

this.user = new this.YamcsUser(info);
Expand Down
98 changes: 94 additions & 4 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,8 @@ async function accumulateResults(url, options, property, soFar, totalLimit, toke

let newUrl = formatUrl(url, token);

const fetchResult = await fetch(newUrl, options);
const fetchResult = await customFetch(newUrl, options);

const result = await fetchResult.json();

if (property in result) {
Expand All @@ -200,7 +201,8 @@ async function accumulateResults(url, options, property, soFar, totalLimit, toke
}

async function requestLimitOverrides(url) {
const response = await fetch(encodeURI(url));
const response = await customFetch(encodeURI(url));

const json = await response.json();

return json?.overrides ?? [];
Expand Down Expand Up @@ -295,7 +297,7 @@ function getHistoryYieldRequest(signal) {

while (url) {
url = yield;
yield fetch(url, { signal})
yield customFetch(url, { signal})
.then(res => res.json());
}
}
Expand Down Expand Up @@ -362,6 +364,93 @@ function flattenObjectArray(array, baseObj = {}) {
}, baseObj);
}

async function getAccessToken(forceRefresh=false) {
const tokenUrl = "http://" + process.env.YAMCS_ENDPOINT + "/auth/token";
const params = new URLSearchParams();

const expiration_time = getCookie("expiration_time");

if (!forceRefresh && expiration_time != null && Date.now() > expiration_time) {
params.append("refresh_token", getCookie("refresh_token"));
params.append("grant_type", "refresh_token");

} else {
params.append("username", process.env.YAMCS_USERNAME);
params.append("password", process.env.YAMCS_PASSWORD);
params.append("grant_type", "password");
}

fetch(tokenUrl, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: params.toString(),
})
.then(response => {
if (!response.ok) {
throw new Error(`Failed to retrieve API token: ${response.status}`);
}

return response.json();
})
.then(data => {
const access_token = data.access_token;
const refresh_token = data.refresh_token;

const lastLoginTime = new Date(data.user.lastLoginTime).getTime();
const expires_in = parseInt(data.expires_in) * 1000;

document.cookie = `access_token=${access_token}; path=/; SameSite=Strict;`;
document.cookie = `refresh_token=${refresh_token}; path=/; SameSite=Strict;`;
document.cookie = `expiration_time=${lastLoginTime + expires_in}; path=/; SameSite=Strict;`;
});
}

function getCookie(name) {
const cookies = document.cookie.split(';');
for (const cookie of cookies) {
const [cookieName, cookieValue] = cookie.trim().split('=');

if (cookieName === name) {
return decodeURIComponent(cookieValue);
}
}
return null; // Return null if the cookie is not found
}

async function customFetch(url, requestOptions = {}) {
const access_token = getCookie('access_token');

// Extend the provided requestOptions object with headers
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${access_token}`
};

// Merge the headers with the provided requestOptions headers
requestOptions.headers = {
...requestOptions.headers,
...headers
};

const response = fetch(url, requestOptions)
.then(response => {
if (!response.ok) {
if (response.status === 401){
getAccessToken(true);
return customFetch(url, requestOptions);
}
else {
throw new Error(`Failed to execute API: ${response}`);
}
}
return response;
});

return response;
}

export {
buildStalenessResponseObject,
getLimitFromAlarmRange,
Expand All @@ -373,5 +462,6 @@ export {
accumulateResults,
addLimitInformation,
yieldResults,
getLimitOverrides
getLimitOverrides,
customFetch,
};

0 comments on commit a58aba5

Please sign in to comment.