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

Feature 433. Functional create rule + Feature 436. Dashboard #479

Merged
merged 100 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
9804be6
Add create rule gateway and endpoint
MytsV Sep 17, 2024
0699a75
Add a unit test for the create rule endpoint
MytsV Sep 17, 2024
7e3ec11
Improve mock request validation and create a unit tests for create ru…
MytsV Sep 17, 2024
acb3e41
Decompose common streaming into table logic
MytsV Sep 17, 2024
e4c527e
Partially implement first view of the rule creation
MytsV Sep 20, 2024
1e9d9c4
Finish selected DIDs table view in the first view of rule creation
MytsV Sep 20, 2024
0f9c1eb
Reformat the code
MytsV Sep 20, 2024
fea83b1
Fix create rule pagination
MytsV Sep 20, 2024
621a0e5
Make the RSE usage and quota info successfully load for the RSEs with…
MytsV Sep 20, 2024
09411ff
Make the text-formatters able to process negative values
MytsV Sep 20, 2024
579fcb1
Make the table streaming accept fetch options
MytsV Sep 20, 2024
da8172c
Add msw mocking for POST requests
MytsV Sep 20, 2024
7208ed3
Add a needs approval field to the create rule parameters
MytsV Sep 20, 2024
a71d613
Fix setting initial selection
MytsV Sep 20, 2024
7eb37b2
Remove unused stuff from DID stage view
MytsV Sep 20, 2024
7068802
Reformat the code
MytsV Sep 20, 2024
762738e
Handle different error formats in stream worker
MytsV Sep 20, 2024
631769b
Fix resolution of parameters from request body
MytsV Sep 20, 2024
bcb1cba
Connect add did endpoint with nextjs
MytsV Sep 22, 2024
722f619
Make the add did endpoint not pass unneeded body parameters
MytsV Sep 22, 2024
45fb481
Cover the add did controller with unit tests
MytsV Sep 22, 2024
57f03a7
Connect the attach dids endpoint and cover it with tests
MytsV Sep 22, 2024
bb4e564
Refactor the add dids
MytsV Sep 22, 2024
8582040
Reformat the add and attach dids infrastructure code
MytsV Sep 22, 2024
e604bdb
Fix set did status endpoint
MytsV Sep 22, 2024
76ab832
Add set did status infrastructure
MytsV Sep 22, 2024
527e807
Cover set did status controller with tests
MytsV Sep 22, 2024
44ae5f7
Fix mock path in attach dids test
MytsV Sep 22, 2024
99d455a
Fix isOpen not being shown when the value is false
MytsV Sep 22, 2024
bdc3481
Finish the RSEs stage of rule creation
MytsV Sep 22, 2024
3364f35
Improve table icons size and ability to shrink
MytsV Sep 22, 2024
582df40
Improve checkbox click area
MytsV Sep 22, 2024
4e61670
Handle resizing columns in tab view
MytsV Sep 22, 2024
7576274
Improve navigation buttons in create rule coloring
MytsV Sep 22, 2024
6fea486
Improve dark mode warning field
MytsV Sep 22, 2024
4f39ef9
Fix dark mode checkbox label
MytsV Sep 22, 2024
6123528
More correctly handle the grid resizing
MytsV Sep 22, 2024
49082f9
Improve order of RSEs columns
MytsV Sep 22, 2024
b358744
Start streaming in create rule RSEs when the page is loaded
MytsV Sep 22, 2024
f1cb9a4
Handle DIDs deselection in list view
MytsV Sep 22, 2024
39bbaad
Fix flickering no data overlay issue
MytsV Sep 22, 2024
96fe6d5
Make previous elements in timeline clickable
MytsV Sep 22, 2024
252e771
Refactor the create rule view
MytsV Sep 22, 2024
265515a
Make Regular Table extend ag grid properties
MytsV Sep 22, 2024
027b516
Fix selection not getting updated on search
MytsV Sep 22, 2024
258d978
Reformat the code
MytsV Sep 22, 2024
0405cd5
Refactor the crate rule steps views
MytsV Sep 22, 2024
d132b88
Refactor the checkbox into a separate component
MytsV Sep 22, 2024
f73f300
Refactor the rse search panel and fix grammar of the warning field
MytsV Sep 22, 2024
71d1c3d
Fix light mode warning field
MytsV Sep 22, 2024
b87377d
Handle loading local storage data in create rule
MytsV Sep 22, 2024
658d35b
Fix warning field opacity
MytsV Sep 22, 2024
5bfa825
Fix create rule layout
MytsV Sep 22, 2024
f71b035
Disable docs in page stories
MytsV Sep 22, 2024
b328ec1
Set fixed RSEs in the create rule story
MytsV Sep 22, 2024
e76f1a6
Implement basic create rule stage options view
MytsV Sep 22, 2024
cba6310
Make whitespace wrap a default property of fields
MytsV Sep 23, 2024
574b3c9
Implement validation and error messages for create rule options
MytsV Sep 23, 2024
6574402
Refactor the options stage
MytsV Sep 23, 2024
29e677a
Ues a view model for the create rule dids with an open parameter
MytsV Sep 23, 2024
f54eac1
Make heading size configurable
MytsV Sep 23, 2024
5395438
Finish tables for the create rule summary
MytsV Sep 23, 2024
a736ddc
Decompose the list DID meta
MytsV Sep 24, 2024
5e56043
Add key value views to the create rule summary
MytsV Sep 24, 2024
71bd918
Add headings to separate create rule summary sections
MytsV Sep 24, 2024
4f91147
Fix resize flickering in all tables
MytsV Sep 24, 2024
c3ec3ae
Add padding to key value views in create rule summary
MytsV Sep 24, 2024
f2cc776
Implement create rule additional flow and partially cover it with tests
MytsV Sep 24, 2024
4ca058d
Reformat the key value feature code
MytsV Sep 24, 2024
5d51831
Add additional test case for the rule creation
MytsV Sep 24, 2024
405ceba
Implement submission step
MytsV Sep 25, 2024
864b1c9
Correctly handle optional values in options
MytsV Sep 25, 2024
246bac8
Finish the top rules widget for the dashboard
MytsV Sep 25, 2024
185374e
Reformat the create rule summary code
MytsV Sep 25, 2024
2649c74
Include scope in the response from the list rule endpoint
MytsV Sep 25, 2024
a4560f7
Convert milliseconds in the rule gateway to seconds
MytsV Sep 25, 2024
5a027bd
Fix loading indicator and no data indicator in the top rules widget
MytsV Sep 25, 2024
e45d1a4
Add collapsible component and fix recharts issue by version update
MytsV Sep 25, 2024
0e04733
Implement a rse usage widget and decompose the legend component
MytsV Sep 26, 2024
93dd655
Integrate the rse usage widget into the dashboard
MytsV Sep 26, 2024
87e2739
Fix padding inside dashboard widgets
MytsV Sep 26, 2024
f2d33f2
Pass custom activity on rule creation to rucio server
MytsV Sep 26, 2024
d59daca
Add asterix to required parameters in create rule options
MytsV Sep 26, 2024
9a6bfc0
Add collapsible search in the create rule DIDs
MytsV Sep 26, 2024
9e1d47f
Fix selection cells in create rule views
MytsV Sep 26, 2024
bed0fe6
Remove highlighting from the DIDs create rule info field
MytsV Sep 26, 2024
44d94d2
Implement lifetime handling with expiry date
MytsV Sep 26, 2024
2a04b6e
Fix padding in custom tooltip
MytsV Sep 26, 2024
215c405
Make advanced options collapsible
MytsV Sep 26, 2024
03a589d
Add a UI option to create samples
MytsV Sep 27, 2024
5802148
Add requested size to the DID summary table
MytsV Sep 27, 2024
493c766
Remove unused old components which failed the build
MytsV Sep 27, 2024
39fde27
Implement DID sampling in the rule creation and cover it with tests
MytsV Sep 27, 2024
e50f17b
Integrate sampling feature with the UI
MytsV Sep 27, 2024
de81372
Include information about sampling in the summary
MytsV Sep 27, 2024
6b3c3ff
Fix generation of the names for sampled DIDs
MytsV Sep 27, 2024
ff046a7
Change the button on the options step to submit
MytsV Sep 27, 2024
ef342e5
Fix rules widget on hover color
MytsV Sep 27, 2024
a38954f
Add stroke to the rse usage widget
MytsV Sep 27, 2024
d1d68aa
Fix legend positioning and padding
MytsV Sep 27, 2024
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
791 changes: 776 additions & 15 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@next/font": "13.1.6",
"@preact/signals": "^1.2.2",
"@primer/css": "^20.8.2",
"@radix-ui/react-collapsible": "^1.1.0",
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-toast": "^1.2.1",
"@sinclair/typebox": "^0.28.14",
Expand Down Expand Up @@ -60,12 +61,14 @@
"react-dom": "18.2.0",
"react-icons": "^4.8.0",
"react-modal": "^3.16.1",
"recharts": "^2.13.0-alpha.5",
"reflect-metadata": "^0.1.13",
"swr": "^2.0.3",
"tailwind-merge": "^1.14.0",
"tailwindcss": "^3.2.7",
"typescript": "4.9.5",
"util": "^0.12.5"
"util": "^0.12.5",
"zod": "^3.23.8"
},
"devDependencies": {
"@babel/core": "^7.20.12",
Expand Down
2 changes: 2 additions & 0 deletions public/streamWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ async function getResponseError(response) {
if (response.status === 404) {
error.type = 'not_found';
}
} else if (jsonResponse.error) {
error.message = jsonResponse.error;
}
} catch (e) {
}
Expand Down
23 changes: 2 additions & 21 deletions src/app/(rucio)/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,5 @@
'use client';
import { fixtureOngoingrules, fixtureUsedquota } from 'test/fixtures/widget-fixtures';
import { Dashboard as DashboardStory } from '@/component-library/pages/legacy/Dashboard/Dashboard';
import { Role } from '@/lib/core/entity/account';
import { SiteHeaderViewModel } from '@/lib/infrastructure/data/view-model/site-header';
import { useEffect, useState } from 'react';
import { getSiteHeader } from '../queries';
import { Loading } from '@/component-library/pages/legacy/Helpers/Loading';
import { User } from '@/lib/core/entity/auth-models';
import { Dashboard } from '@/component-library/pages/Dashboard/Dashboard';
export default function Page() {
const [siteHeader, setSiteHeader] = useState<SiteHeaderViewModel>({ status: 'pending' } as SiteHeaderViewModel);
useEffect(() => {
getSiteHeader().then((vm: SiteHeaderViewModel) => setSiteHeader(vm));
}, []);
if (siteHeader.status === 'pending') return <Loading title="Dashboard" />;
return (
<DashboardStory
accountname={(siteHeader.activeAccount as User).rucioAccount}
accountrole={(siteHeader.activeAccount as User).role === 'admin' ? Role.ADMIN : Role.USER}
inputOngoingrules={Array.from({ length: 20 }, (v, k) => fixtureOngoingrules())}
inputUsedquota={Array.from({ length: 20 }, (v, k) => fixtureUsedquota())}
/>
);
return <Dashboard />;
}
11 changes: 1 addition & 10 deletions src/app/(rucio)/queries.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
import { SiteHeaderViewModel } from '@/lib/infrastructure/data/view-model/site-header';

export async function getSiteHeader(): Promise<SiteHeaderViewModel> {
const req: any = {
const res = await fetch('/api/feature/get-site-header', {
method: 'GET',
url: new URL(`${process.env.NEXT_PUBLIC_WEBUI_HOST}/api/feature/get-site-header`),
headers: {
'Content-Type': 'application/json',
},
params: {},
};

const res = await fetch(req.url, {
method: 'GET',
headers: new Headers({
'Content-Type': 'application/json',
} as HeadersInit),
});

return await res.json();
Expand Down
2 changes: 1 addition & 1 deletion src/app/(rucio)/rse/list/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ export default function Page() {

// TODO: fetch initial data

return <ListRSE firstExpression={firstExpression ?? undefined} />;
return <ListRSE initialExpression={firstExpression ?? undefined} />;
}
143 changes: 41 additions & 102 deletions src/app/(rucio)/rule/create/page.tsx
Original file line number Diff line number Diff line change
@@ -1,119 +1,58 @@
'use client';
import { CreateRule as CreateRuleStory } from '@/component-library/pages/legacy/Rule/CreateRule';
import { RSEAccountUsageLimitViewModel } from '@/lib/infrastructure/data/view-model/rse';
import {
CreateRulesViewModel,
generateEmptyCreateRulesViewModel,
TypedDIDValidationQuery,
TypedDIDValidationResponse,
} from '@/lib/infrastructure/data/view-model/create-rule';
import useComDOM from '@/lib/infrastructure/hooks/useComDOM';
import { RuleSummaryViewModel } from '@/lib/infrastructure/data/view-model/rule';
import { BatchResponse } from '@/lib/infrastructure/web-worker/comdom-wrapper';
import { ListDIDsViewModel } from '@/lib/infrastructure/data/view-model/list-did';
import { useEffect, useState } from 'react';
import { AccountInfo } from '@/lib/core/entity/rucio';
import { generateEmptyAccountInfoViewModel } from '@/lib/infrastructure/data/view-model/account';
import { TCreateRuleFeatureRequestParams } from '@/pages/api/feature/create-rule';

export default function CreateRule() {
const onSubmit = async (query: TCreateRuleFeatureRequestParams) => {
let viewModel: CreateRulesViewModel = generateEmptyCreateRulesViewModel();
viewModel.status = 'pending';

try {
const response = await fetch(`${process.env.NEXT_PUBLIC_WEBUI_HOST}/api/feature/create-rule`, {
method: 'POST',
headers: new Headers({
'Content-Type': 'application/json',
} as HeadersInit),
body: JSON.stringify(query),
});

if (response.ok) {
const data: CreateRulesViewModel = await response.json();
viewModel = data;
} else {
throw new Error(`Error creating rule. HTTP Status Code: ${response.status}`);
import { CreateRule } from '@/component-library/pages/Rule/create/CreateRule';
import { parameters } from '../../../../../.storybook/preview';
import { CreateRuleParameters } from '@/lib/infrastructure/data/view-model/rule';

const PARAMS_KEY = 'create_rule_parameters';
const ACTIVE_KEY = 'create_rule_active';

export default function Page() {
const getSavedParameters = () => {
const initialParametersString = localStorage.getItem(PARAMS_KEY);
// TODO: check with zod
if (initialParametersString) {
try {
return JSON.parse(initialParametersString);
} catch (e) {
return undefined;
}
} catch (error: any) {
viewModel.status = 'error';
viewModel.message = error.message;
}

return viewModel;
return undefined;
};

const didValidation = (query: TypedDIDValidationQuery) => {
// if the DID contains the string "error", it will be added to the error list
var localErrorDIDs: TypedDIDValidationResponse = { ErrorList: [] };
query.DIDList.map((DID: string, index: number) => {
if (DID.includes('error')) {
localErrorDIDs.ErrorList.push({ DID: DID, ErrorCodes: [421], Message: 'This DID is invalid' });
}
});
// if the error list is empty, the promise will resolve, otherwise it will reject
if (localErrorDIDs.ErrorList.length === 0) {
return Promise.resolve(localErrorDIDs);
} else {
return Promise.reject(localErrorDIDs);
const getSavedIndex = () => {
const initialActiveString = localStorage.getItem(ACTIVE_KEY);

if (initialActiveString) {
const numberValue = parseInt(initialActiveString);
return isNaN(numberValue) ? undefined : numberValue;
}
};

const DIDSearchComDOM = useComDOM<ListDIDsViewModel>('create-rule-page-did-query', [], false, Infinity, 50, true);
const setSavedParameters = (parameters: CreateRuleParameters) => {
localStorage.setItem(PARAMS_KEY, JSON.stringify(parameters));
};

const processRSEAccountUsageLimitViewModelBatchResponse = (batch: BatchResponse<RSEAccountUsageLimitViewModel>) => {
// The feature replaces Infinity with -1 for the bytes_limit and bytes_remaining fields
// This is done because Infinity is not a valid JSON value
// This code replaces -1 with Infinity for the bytes_limit and bytes_remaining fields
batch.data.forEach((rseAccountUsageLimitViewModel: RSEAccountUsageLimitViewModel) => {
if (rseAccountUsageLimitViewModel.status === 'success') {
if (rseAccountUsageLimitViewModel.bytes_limit === -1) {
rseAccountUsageLimitViewModel.bytes_limit = Infinity;
}
if (rseAccountUsageLimitViewModel.bytes_remaining === -1) {
rseAccountUsageLimitViewModel.bytes_remaining = Infinity;
}
}
});
return batch;
const setSavedIndex = (activeIndex: number) => {
localStorage.setItem(ACTIVE_KEY, activeIndex.toString());
};

const RSEComDOM = useComDOM<RSEAccountUsageLimitViewModel>(
'create-rule-page-rse-query',
[],
false,
Infinity,
50,
true,
processRSEAccountUsageLimitViewModelBatchResponse,
);
const removeSavedParameters = () => {
localStorage.removeItem(PARAMS_KEY);
};

const [accountInfo, setAccountInfo] = useState<AccountInfo>(generateEmptyAccountInfoViewModel());
useEffect(() => {
fetch(`${process.env.NEXT_PUBLIC_WEBUI_HOST}/api/feature/account-info`)
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error(`Error fetching account info. Are you logged in? HTTP Status Code: ${response.status}`);
}
})
.then((data: AccountInfo) => {
setAccountInfo(data);
})
.catch(error => {
console.error(error);
});
}, []);
const removeSavedIndex = () => {
localStorage.removeItem(ACTIVE_KEY);
};

return (
<CreateRuleStory
accountInfo={accountInfo}
onSubmit={onSubmit}
didValidation={didValidation}
didListComDOM={DIDSearchComDOM}
rseListComDOM={RSEComDOM}
<CreateRule
getSavedIndex={getSavedIndex}
getSavedParameters={getSavedParameters}
setSavedIndex={setSavedIndex}
setSavedParameters={setSavedParameters}
removeSavedParameters={removeSavedParameters}
removeSavedIndex={removeSavedIndex}
/>
);
}
6 changes: 4 additions & 2 deletions src/component-library/atoms/form/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@ const buttonVariants = cva(
{
variants: {
variant: {
default: 'bg-brand-600 text-neutral-100 hover:bg-brand-700 dark:hover:bg-brand-500',
default: 'bg-opacity-80 bg-brand-600 text-neutral-100 hover:bg-brand-700 dark:hover:bg-brand-500',
success: 'bg-base-success-600 text-neutral-100 hover:bg-base-success-700 dark:hover:bg-base-success-500',
error: 'bg-base-error-600 text-neutral-100 hover:bg-base-error-700 dark:hover:bg-base-error-500',
neutral:
'bg-neutral-300 text-neutral-900 dark:bg-opacity-90 dark:bg-neutral-700 dark:text-neutral-100 hover:bg-neutral-200 dark:hover:bg-neutral-800',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
icon: 'h-6 w-6 rounded',
},
},
defaultVariants: {
Expand Down
45 changes: 22 additions & 23 deletions src/component-library/atoms/form/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@ import * as React from 'react';

import { cn } from '@/component-library/utils';

const commonClasses = cn(
'flex w-full rounded-md',
'border border-neutral-900 dark:border-neutral-100 border-opacity-10 dark:border-opacity-10',
'rounded-md',
'bg-neutral-100 dark:bg-neutral-800',
'px-3 py-2',
'text-neutral-900 dark:text-neutral-100',
'placeholder:text-neutral-500',
'focus:ring-0 focus:shadow-brand',
'focus:outline-none focus:border-1 focus:border-brand-500',
'dark:focus:border-1 dark:focus:border-brand-500',
);

export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
onEnterKey?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
}
Expand All @@ -17,29 +30,15 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(({ className, type,
}
};

return (
<input
type={type}
className={cn(
'flex w-full rounded-md',
'h-10',
'border border-neutral-900 dark:border-neutral-100 border-opacity-10 dark:border-opacity-10',
'rounded-md',
'bg-neutral-100 dark:bg-neutral-800',
'px-3 py-2',
'text-neutral-900 dark:text-neutral-100',
'placeholder:text-neutral-500',
'focus:ring-0 focus:shadow-brand',
'focus:outline-none focus:border-1 focus:border-brand-500',
'dark:focus:border-1 dark:focus:border-brand-500',
className,
)}
ref={ref}
onKeyDown={handleKeyDown}
{...props}
/>
);
return <input type={type} className={cn(commonClasses, 'h-10', className)} ref={ref} onKeyDown={handleKeyDown} {...props} />;
});
Input.displayName = 'Input';

export { Input };
export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}

const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(({ className, onKeyDown, ...props }, ref) => {
return <textarea className={cn('h-40', commonClasses, className)} ref={ref} {...props} />;
});
Textarea.displayName = 'Textarea';

export { Input, Textarea };
21 changes: 21 additions & 0 deletions src/component-library/atoms/misc/CircleWithText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { cn } from '@/component-library/utils';

const CircleWithText = ({ text, className, onClick }: { text: string; onClick?: () => void; className?: string }) => {
const classes = cn(
'flex items-center justify-center',
'w-12 h-12 rounded-full',
'text-neutral-100',
'border border-neutral-900 dark:border-neutral-100 border-opacity-10 dark:border-opacity-10',
className,
onClick ? 'cursor-pointer' : '',
);

return (
<div className={classes} onClick={onClick}>
<span className="text-lg">{text}</span>
</div>
);
};

export default CircleWithText;
19 changes: 13 additions & 6 deletions src/component-library/atoms/misc/Divider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,22 @@ export const Divider = ({
thickness = 'border',
color = 'border-neutral-100 dark:border-neutral-700',
margin = 'my-2',
className = '',
}) => {
const isHorizontal = orientation === 'horizontal';

const classNames = cn(color, thickness, margin, {
'w-full': isHorizontal,
'h-full': !isHorizontal,
'border-t': isHorizontal,
'border-l': !isHorizontal,
});
const classNames = cn(
color,
thickness,
margin,
{
'w-full': isHorizontal,
'h-full': !isHorizontal,
'border-t': isHorizontal,
'border-l': !isHorizontal,
},
className,
);

return <div className={classNames}></div>;
};
Loading
Loading