Skip to content

Commit

Permalink
Bug/prod portal resource display issue (#261)
Browse files Browse the repository at this point in the history
* #260: updated site parser to handle site workers in Maint/ PreMaint

* #260: added maintenance worker table to site detail page"

* #260: replaced mock data with API data

* #257: disabled auto-refresh-token function

* #255: minor wording fixe

* #260: removed console logs

* #260: fixed table site link
  • Loading branch information
yaxue1123 authored May 16, 2023
1 parent e1c618d commit 20baa4b
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 61 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fabric-portal",
"version": "1.4.5",
"version": "1.4.6",
"private": true,
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.32",
Expand Down
2 changes: 1 addition & 1 deletion src/components/Experiment/Slices.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ class Slices extends React.Component {
</div> :
<div className="alert alert-warning mt-3" role="alert">
<p className="mt-2">
We couldn't find your slices. Please create slices in Portal or &nbsp;
We couldn't find any slices belonging to you. Please create slices in Portal or &nbsp;
<a
href={this.jupyterLinkMap[checkPortalType(window.location.href)]}
target="_blank"
Expand Down
31 changes: 29 additions & 2 deletions src/components/Resource/SiteDetailPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const SiteDetailPage = props => {
</div>
}
<div className="mt-4">
<h2>Basic Information</h2>
<h3>Basic Information</h3>
<table className="table table-sm table-striped table-bordered mb-4">
<tbody>
{
Expand Down Expand Up @@ -111,8 +111,35 @@ const SiteDetailPage = props => {
</tbody>
</table>
</div>
{
data.workers.length > 0 && <div className="mt-4">
<h3>Workers in Maintenance</h3>
<table className="table table-sm table-striped table-bordered mb-4">
<tbody>
<tr>
<th>Worker Name</th>
<th>Status</th>
<th>Deadline</th>
<th>Expected End Time</th>
</tr>
{
data.workers.map((worker, index) => {
return (
<tr key={`maintenance-worker-${index}`}>
<td>{Object.keys(worker)[0]}</td>
<td>{statusMapping[Object.values(worker)[0].state].state}</td>
<td>{Object.values(worker)[0].deadline ? utcToLocalTimeParser(Object.values(worker)[0].deadline) : "Unknown"}</td>
<td>{Object.values(worker)[0].expected_end ? utcToLocalTimeParser(Object.values(worker)[0].expected_end) : "Unknown"}</td>
</tr>
)
})
}
</tbody>
</table>
</div>
}
<div className="mt-4">
<h2>Resource Availabilities</h2>
<h3>Resource Availabilities</h3>
<DetailTable
name={ data.name }
resource={ data }
Expand Down
9 changes: 3 additions & 6 deletions src/components/Resource/SummaryTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,9 @@ class SummaryTable extends Component {
content: (resource) => (
<div>
<Link
to={
{
pathname:`/sites/${resource.id}`,
state: { siteData: resource }
}
}>
to={`/sites/${resource.id}`}
state={{ data: resource }}
>
{resource.name}
</Link>
{
Expand Down
8 changes: 4 additions & 4 deletions src/pages/Help.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,16 @@ export default class Help extends React.Component{
<h5 className="card-title">Experiment Issues</h5>
<p>
For technical assistance with your experiments, please search in our <a href={portalData.knowledgeBaseLink} target="_blank" rel="noopener noreferrer">Knowledge Base</a> or
the <a href={portalData.knowledgeBaseForumLink} target="_blank" rel="noopener noreferrer">Forum</a>. If you are unable to fix the problem on your
own, please post a question in the appropriate section of our <a href={portalData.knowledgeBaseForumLink} target="_blank" rel="noopener noreferrer">Forum</a>.
the <a href={portalData.knowledgeBaseForumLink} target="_blank" rel="noopener noreferrer">Forums</a>. If you are unable to fix the problem on your
own, please post a question in the appropriate section of our <a href={portalData.knowledgeBaseForumLink} target="_blank" rel="noopener noreferrer">Forums</a>.
</p>
<a href={portalData.knowledgeBaseLink} target="_blank" rel="noopener noreferrer" className="btn btn-primary mr-2">
<i className="fa fa-sign-in mr-2"></i>
Knowledge Base
</a>
<a href={portalData.knowledgeBaseForumLink} target="_blank" rel="noopener noreferrer" className="btn btn-primary">
<i className="fa fa-sign-in mr-2"></i>
Forum
Forums
</a>
</div>
</div>
Expand Down Expand Up @@ -76,7 +76,7 @@ export default class Help extends React.Component{
<span className="ml-1 badge badge-pill badge-success">beta</span>
</h5>
<p>
Haven't found an answer to your question on the Forum or Knowledge Base? Set up time with members of the FABRIC Team during their Office Hours.
Haven't found an answer to your question on the Forums or Knowledge Base? Set up time with members of the FABRIC Team during their Office Hours.
</p>
<a href={portalData.officeHourBookingLink} target="_blank" rel="noopener noreferrer" className="btn btn-primary mr-2">
<i className="fa fa-sign-in mr-2"></i>
Expand Down
47 changes: 45 additions & 2 deletions src/services/parser/sitesParser.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { object } from "prop-types";

const getSiteColor = (status) => {
if (status === "Active") {
// Color: "primary" for "active" sites
Expand All @@ -13,6 +15,31 @@ const getSiteColor = (status) => {
}
}

const retrieveWorkers = (maintenance) => {
const workers = [];
let status = "PreMaint";
for (const [key, value] of Object.entries(maintenance)) {
workers.push({
[key]: value
});
// if all workers are in PreMaint state, then PreMaint as the site state
// if a Maint worker exists, mark the site as in Maint too
if (value.state === "Maint") {
status = "Maint";
}
}

// status is an object. e.g. { state: "Maint", deadline: null, expected_end: null }
return {
workers: workers,
siteStatus: {
state: status,
deadline: null,
expected_end: null
}
}
}

export default function parseSites(data, acronymToShortName) {
let abqm_elements = JSON.parse(data.model);
const nodes = abqm_elements.nodes;
Expand All @@ -31,7 +58,17 @@ export default function parseSites(data, acronymToShortName) {
site.location = node.Location;
// site.location = JSON.parse(node.Location)["postal"];
/************ retrieve site status in site node. ************/
site.status = JSON.parse(node.MaintenanceInfo)[node.Name];
const maintenance = JSON.parse(node.MaintenanceInfo);
if (maintenance[node.Name]) {
// the site is active or the maintenance is at site level
site.status = maintenance[node.Name];
site.workers = [];
} else {
// the maintenance is at worker level
const parsedMaintInfo = retrieveWorkers(maintenance);
site.status = parsedMaintInfo.siteStatus;
site.workers = parsedMaintInfo.workers;
}
/************ retrieve site capacity in site node. ************/
const siteCapacityTypes = {
"CPU": "cpu",
Expand Down Expand Up @@ -82,7 +119,13 @@ export default function parseSites(data, acronymToShortName) {
}

parsedSites.push(site);
siteColorMapping[site.name] = getSiteColor(site.status.state);

try {
siteColorMapping[site.name] = getSiteColor(site.status.state);
} catch(err) {
console.log(`This site cannot be parsed correctly: ${site}`);
}

siteNames.push(acronymToShortName[site.name]);
siteAcronyms.push(site.name);
}
Expand Down
2 changes: 1 addition & 1 deletion src/services/portalData.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "1.4.4",
"version": "1.4.6",
"defaultFacility": "FABRIC",
"facilityOptions": ["FABRIC"],
"sliverKeyLimit": 10,
Expand Down
1 change: 0 additions & 1 deletion src/utils/clearLocalStorage.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ export default function clearLocalStorage() {
localStorage.removeItem("countdownTimerIntervalId");
localStorage.removeItem("sessionTimeoutIntervalId1");
localStorage.removeItem("sessionTimeoutIntervalId2");
localStorage.removeItem("refreshTokenIntervalId");
}
43 changes: 1 addition & 42 deletions src/utils/manageTokens.js
Original file line number Diff line number Diff line change
@@ -1,58 +1,17 @@
import { createIdToken, refreshToken, revokeToken } from "../services/credentialManagerService.js";
import { default as portalData } from "../services/portalData.json";
import { createIdToken } from "../services/credentialManagerService.js";
import { toast } from "react-toastify";

const autoRevokeTokens = async () => {
try {
await revokeToken(localStorage.getItem("refreshToken"));
} catch (err) {
console.log("Failed to revoke token.");
}
}

export const autoCreateTokens = async (projectId) => {
// clear previous autoRefreshToken interval if there is any
if (localStorage.getItem("refreshTokenIntervalId")) {
clearInterval(localStorage.getItem("refreshTokenIntervalId"));
}

try {
// call credential manager to generate tokens.
// parameters: project and scope, "all" for both by default.
const { data: res } = await createIdToken(projectId, "all");
localStorage.setItem("idToken", res["data"][0].id_token);
localStorage.setItem("refreshToken", res["data"][0].refresh_token);

// Auto refresh token every 55min
const refreshTokenIntervalId = setInterval(() => {
if(localStorage.getItem("refreshTokenIntervalId")) {
clearInterval(localStorage.getItem("refreshTokenIntervalId"));
}
autoRefreshTokens(projectId);
}
, portalData["autoRefreshTokenInterval"]);
localStorage.setItem("refreshTokenIntervalId", refreshTokenIntervalId);
return res["data"][0];
}
catch (err) {
toast.error("Unable to obtain authentication token, the likely reason is you are not a member of any projects.");
}
}

export const autoRefreshTokens = async (projectId) => {
const oldRefreshToken = localStorage.getItem("refreshToken");
try {
const { data: res } = await refreshToken(projectId, "all", oldRefreshToken);
localStorage.setItem("idToken", res["data"][0].id_token);
localStorage.setItem("refreshToken", res["data"][0].refresh_token);
} catch (err) {
toast.error("Failed to refresh necessary tokens to view slice information. Please try again later.");
// if refresh_token isn't working either
// start over by calling create_token when user reloads the page
// 1. call cm revoke_token with old refresh token
autoRevokeTokens();
// 2. clear id_token and refresh_token in local storage
localStorage.removeItem("idToken");
localStorage.removeItem("refreshToken");
}
}

0 comments on commit 20baa4b

Please sign in to comment.