Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix. Make the dashboard load only rules created less than 15 days ago. #481

Merged
merged 2 commits into from
Sep 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 35 additions & 24 deletions src/component-library/pages/Dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { useQuery } from '@tanstack/react-query';
import { SiteHeaderViewModel } from '@/lib/infrastructure/data/view-model/site-header';
import { getSiteHeader } from '@/app/(rucio)/queries';
import { LoadingSpinner } from '@/component-library/atoms/loading/LoadingSpinner';
import { Heading } from '@/component-library/atoms/misc/Heading';
import { WarningField } from '@/component-library/features/fields/WarningField';
import { AccountRoleBadge } from '@/component-library/features/badges/account/AccountRoleBadge';
import { TopRulesWidget } from '@/component-library/pages/Dashboard/widgets/TopRulesWidget';
import { useEffect, useRef, useState } from 'react';
import { RuleViewModel } from '@/lib/infrastructure/data/view-model/rule';
import useStreamReader, { StreamingStatus } from '@/lib/infrastructure/hooks/useStreamReader';
import { RSEAccountUsageViewModel } from '@/lib/infrastructure/data/view-model/rse';
import { TopStorageUsageWidget } from '@/component-library/pages/Dashboard/widgets/TopStorageUsageWidget';
import {useQuery} from '@tanstack/react-query';
import {SiteHeaderViewModel} from '@/lib/infrastructure/data/view-model/site-header';
import {getSiteHeader} from '@/app/(rucio)/queries';
import {LoadingSpinner} from '@/component-library/atoms/loading/LoadingSpinner';
import {Heading} from '@/component-library/atoms/misc/Heading';
import {WarningField} from '@/component-library/features/fields/WarningField';
import {AccountRoleBadge} from '@/component-library/features/badges/account/AccountRoleBadge';
import {TopRulesWidget} from '@/component-library/pages/Dashboard/widgets/TopRulesWidget';
import {useEffect, useRef, useState} from 'react';
import {RuleViewModel} from '@/lib/infrastructure/data/view-model/rule';
import useStreamReader, {StreamingStatus} from '@/lib/infrastructure/hooks/useStreamReader';
import {RSEAccountUsageViewModel} from '@/lib/infrastructure/data/view-model/rse';
import {TopStorageUsageWidget} from '@/component-library/pages/Dashboard/widgets/TopStorageUsageWidget';

const AccountHeading = () => {
const querySiteHeader = async () => {
Expand All @@ -34,7 +34,7 @@ const AccountHeading = () => {
retry: false,
});

if (isHeaderFetching) return <LoadingSpinner />;
if (isHeaderFetching) return <LoadingSpinner/>;

if (headerError || !header?.activeAccount) {
return (
Expand All @@ -46,16 +46,16 @@ const AccountHeading = () => {

return (
<div className="flex space-x-2 items-center">
<Heading text={`Hello, ${header.activeAccount.rucioAccount}!`} />
<AccountRoleBadge className="text-xl" value={header.activeAccount.role} />
<Heading text={`Hello, ${header.activeAccount.rucioAccount}!`}/>
<AccountRoleBadge className="text-xl" value={header.activeAccount.role}/>
</div>
);
};

const UsageView = () => {
const usageBuffer = useRef<RSEAccountUsageViewModel[] | undefined>([]);
const [usages, setUsages] = useState<RSEAccountUsageViewModel[]>();
const { start, stop, error, status } = useStreamReader<RSEAccountUsageViewModel>();
const {start, stop, error, status} = useStreamReader<RSEAccountUsageViewModel>();

useEffect(() => {
// TODO: handle error view models
Expand All @@ -80,18 +80,29 @@ const UsageView = () => {

const isLoading = (!usages && !error) || status === StreamingStatus.RUNNING;

return <TopStorageUsageWidget usages={usages} isLoading={isLoading} errorMessage={error?.message} />;
return <TopStorageUsageWidget usages={usages} isLoading={isLoading} errorMessage={error?.message}/>;
};

const RulesView = () => {
const rulesBuffer = useRef<RuleViewModel[] | undefined>([]);
const [rules, setRules] = useState<RuleViewModel[]>();
const { start, stop, error, status } = useStreamReader<RuleViewModel>();
const {start, stop, error, status} = useStreamReader<RuleViewModel>();

const getCreatedAfterDate = () => {
// Only the rules that were created less than 15 days ago should get loaded
const now = new Date();
const fifteenDaysAgo = new Date(now.setDate(now.getDate() - 15));
return fifteenDaysAgo.toISOString();
};

useEffect(() => {
// TODO: handle error view models
const params = new URLSearchParams({
scope: '*',
created_after: getCreatedAfterDate(),
});
start({
url: '/api/feature/list-rules?scope=*',
url: '/api/feature/list-rules?' + params.toString(),
onData: data => {
if (!rulesBuffer.current) {
rulesBuffer.current = data;
Expand All @@ -111,17 +122,17 @@ const RulesView = () => {

const isLoading = (!rules && !error) || status === StreamingStatus.RUNNING;

return <TopRulesWidget rules={rules} isLoading={isLoading} errorMessage={error?.message} />;
return <TopRulesWidget rules={rules} isLoading={isLoading} errorMessage={error?.message}/>;
};

export const Dashboard = () => {
return (
<div className="flex flex-col space-y-3 w-full">
<div className="h-14">
<AccountHeading />
<AccountHeading/>
</div>
<RulesView />
<UsageView />
<RulesView/>
<UsageView/>
</div>
);
};
4 changes: 2 additions & 2 deletions src/lib/core/use-case/list-rules-usecase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export default class ListRulesUseCase
}

async makeGatewayRequest(requestModel: AuthenticatedRequestModel<ListRulesRequest>): Promise<ListRulesDTO> {
const { rucioAuthToken, scope, account } = requestModel;
const dto: ListRulesDTO = await this.gateway.listRules(rucioAuthToken, { scope, account });
const { rucioAuthToken, scope, account, created_after } = requestModel;
const dto: ListRulesDTO = await this.gateway.listRules(rucioAuthToken, { scope, account, created_after });
return dto;
}

Expand Down
1 change: 1 addition & 0 deletions src/lib/core/usecase-models/list-rules-usecase-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Rule } from '@/lib/core/entity/rucio';
export interface ListRulesRequest {
account?: string;
scope?: string;
created_after?: Date;
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/lib/infrastructure/controller/list-rules-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import USECASE_FACTORY from '@/lib/infrastructure/ioc/ioc-symbols-usecase-factor
export type ListRulesControllerParameters = TAuthenticatedControllerParameters & {
scope?: string;
account?: string;
created_after?: string;
};

@injectable()
Expand All @@ -22,6 +23,7 @@ class ListRulesController extends BaseController<ListRulesControllerParameters,
rucioAuthToken: parameters.rucioAuthToken,
account: parameters.account,
scope: parameters.scope,
created_after: parameters.created_after ? new Date(parameters.created_after) : undefined,
};
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {RuleDTO} from '@/lib/core/dto/rule-dto';
import {BaseStreamableDTO} from '@/lib/sdk/dto';
import {BaseStreamableEndpoint} from '@/lib/sdk/gateway-endpoints';
import {HTTPRequest} from '@/lib/sdk/http';
import {Response} from 'node-fetch';
import {convertToRuleDTO, ListRulesFilter, TRucioRule} from '../rule-gateway-utils';
import { RuleDTO } from '@/lib/core/dto/rule-dto';
import { BaseStreamableDTO } from '@/lib/sdk/dto';
import { BaseStreamableEndpoint } from '@/lib/sdk/gateway-endpoints';
import { HTTPRequest } from '@/lib/sdk/http';
import { Response } from 'node-fetch';
import { convertToRuleDTO, formatFilterDate, ListRulesFilter, TRucioRule } from '../rule-gateway-utils';

const DEFAULT_PARAMETER = '*';

Expand All @@ -19,17 +19,23 @@ export default class ListRulesEndpoint extends BaseStreamableEndpoint<BaseStream
await super.initialize();
const rucioHost = await this.envConfigGateway.rucioHost();
const endpoint = `${rucioHost}/rules/`;

const params: any = {
account: this.filter?.account ?? DEFAULT_PARAMETER,
scope: this.filter?.scope ?? DEFAULT_PARAMETER,
};
if (this.filter?.created_after) {
params.created_after = formatFilterDate(this.filter.created_after);
}

const request: HTTPRequest = {
method: 'GET',
url: endpoint,
headers: {
'X-Rucio-Auth-Token': this.rucioAuthToken,
'Content-Type': 'application/x-json-stream',
},
params: {
account: this.filter?.account ?? DEFAULT_PARAMETER,
scope: this.filter?.scope ?? DEFAULT_PARAMETER,
}
params: params,
};
this.request = request;
this.initialized = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { RuleDTO, RuleReplicaLockStateDTO } from '@/lib/core/dto/rule-dto';
import { LockState, RuleState } from '@/lib/core/entity/rucio';
import { DateISO, LockState, RuleState } from '@/lib/core/entity/rucio';

export type TRucioRule = {
error: null | string;
Expand Down Expand Up @@ -139,4 +139,21 @@ export function getEmptyRuleReplicaLockDTO(): RuleReplicaLockStateDTO {
export type ListRulesFilter = {
account?: string;
scope?: string;
created_after?: Date;
};

export const formatFilterDate = (date: Date) => {
const daysOfWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

const dayOfWeek = daysOfWeek[date.getUTCDay()];
const day = String(date.getUTCDate()).padStart(2, '0');
const month = months[date.getUTCMonth()];
const year = date.getUTCFullYear();

const hours = String(date.getUTCHours()).padStart(2, '0');
const minutes = String(date.getUTCMinutes()).padStart(2, '0');
const seconds = String(date.getUTCSeconds()).padStart(2, '0');

return `${dayOfWeek}, ${day} ${month} ${year} ${hours}:${minutes}:${seconds} UTC`;
};
4 changes: 3 additions & 1 deletion src/pages/api/feature/list-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ async function listRules(req: NextApiRequest, res: NextApiResponse, rucioAuthTok

const account = sessionUser.rucioAccount;

const { scope } = req.query as { scope?: string };
// TODO: check if created_after is an actual date
const { scope, created_after } = req.query as { scope?: string; created_after?: string };

const controllerParameters: ListRulesControllerParameters = {
response: res,
rucioAuthToken: rucioAuthToken,
account: account,
scope: scope,
created_after: created_after,
};

const controller = appContainer.get<BaseController<ListRulesControllerParameters, ListRulesRequest>>(CONTROLLERS.LIST_RULES);
Expand Down
Loading