Skip to content

Commit 266c718

Browse files
authored
When UI is read-only, show Rule config as text (#4239)
* Fixes #4107 - show rule config values even when UI is read-only * Always show the Global rules tab - non-admins will get a read only view of the rules * Fix linting issue * Show "(Not enabled)" text for rule value when appropriate.
1 parent 3d09722 commit 266c718

16 files changed

+231
-35
lines changed

ui/ui-app/configs/config-3scale.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
var ApicurioRegistryConfig = {
22
artifacts: {
3-
url: "https://apicurio-registry-api-rhaf-apicurio-registry.apps.dev-eng-ocp4-mas.dev.3sca.net/apis/registry/v2"
3+
url: "https://apicurio-registry-api-rhaf-apicurio-registry.apps.dev-eng-ocp4-mas.dev.3sca.net/apis/registry/v3"
44
},
55
ui: {
66
contextPath: "/",

ui/ui-app/configs/config-none.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
var ApicurioRegistryConfig = {
22
artifacts: {
3-
url: "http://localhost:8080/apis/registry/v2"
3+
url: "http://localhost:8080/apis/registry/v3"
44
},
55
ui: {
66
contextPath: "/",

ui/ui-app/configs/config-oidc-rbac.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
var ApicurioRegistryConfig = {
22
artifacts: {
3-
url: "http://localhost:8080/apis/registry/v2"
3+
url: "http://localhost:8080/apis/registry/v3"
44
},
55
ui: {
66
contextPath: "/",

ui/ui-app/configs/config-oidc.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
var ApicurioRegistryConfig = {
22
artifacts: {
3-
url: "http://localhost:8080/apis/registry/v2"
3+
url: "http://localhost:8080/apis/registry/v3"
44
},
55
ui: {
66
contextPath: "/",

ui/ui-app/src/app/components/header/RootPageHeader.tsx

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React, { FunctionComponent } from "react";
22
import { Tab, Tabs, TabTitleText } from "@patternfly/react-core";
3-
import { IfAuth } from "@app/components";
43
import { Services } from "@services/services.ts";
54
import { AppNavigation, useAppNavigation } from "@hooks/useAppNavigation.ts";
65

@@ -46,16 +45,14 @@ export const RootPageHeader: FunctionComponent<RootPageHeaderProps> = (props: Ro
4645
<Tab data-testid="access-tab" key={2} eventKey={2} title={<TabTitleText>Access</TabTitleText>} />
4746
);
4847
}
49-
if (Services.getConfigService().featureSettings()) {
48+
if (Services.getConfigService().featureSettings() && Services.getAuthService().isUserAdmin()) {
5049
tabs.push(
5150
<Tab data-testid="settings-tab" key={3} eventKey={3} title={<TabTitleText>Settings</TabTitleText>} />
5251
);
5352
}
5453
return (
5554
<div>
56-
<IfAuth isAdmin={true}>
57-
<Tabs className="root-tabs" activeKey={props.tabKey} onSelect={handleTabClick} children={tabs} />
58-
</IfAuth>
55+
<Tabs className="root-tabs" activeKey={props.tabKey} onSelect={handleTabClick} children={tabs} />
5956
</div>
6057
);
6158

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { FunctionComponent } from "react";
2+
import { If } from "@apicurio/common-ui-components";
3+
import { Label } from "@patternfly/react-core";
4+
5+
6+
/**
7+
* Properties
8+
*/
9+
export type CompatibilityLabelProps = {
10+
value: string;
11+
};
12+
13+
const CONFIG_OPTIONS: any[] = [
14+
{ label: "None", value: "NONE" },
15+
{ label: "Backward", value: "BACKWARD" },
16+
{ label: "Backward transitive", value: "BACKWARD_TRANSITIVE" },
17+
{ label: "Forward", value: "FORWARD" },
18+
{ label: "Forward transitive", value: "FORWARD_TRANSITIVE" },
19+
{ label: "Full", value: "FULL" },
20+
{ label: "Full transitive", value: "FULL_TRANSITIVE" },
21+
];
22+
23+
const valueToLabel = (value: string): string => {
24+
if (value === "UNKNOWN") {
25+
return "";
26+
}
27+
return CONFIG_OPTIONS.filter(item => item.value === value)[0].label;
28+
};
29+
30+
/**
31+
* Component.
32+
*/
33+
export const CompatibilityLabel: FunctionComponent<CompatibilityLabelProps> = (props: CompatibilityLabelProps) => {
34+
35+
return (
36+
<>
37+
<If condition={props.value === "UNKNOWN"}>
38+
<span className="rule-value">(Not enabled)</span>
39+
</If>
40+
<If condition={props.value !== "UNKNOWN"}>
41+
<Label>{ valueToLabel(props.value) }</Label>
42+
</If>
43+
</>
44+
);
45+
46+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { FunctionComponent, useEffect, useState } from "react";
2+
import { Label } from "@patternfly/react-core";
3+
import { If } from "@apicurio/common-ui-components";
4+
5+
6+
/**
7+
* Properties
8+
*/
9+
export type IntegrityLabelProps = {
10+
value: string;
11+
};
12+
13+
export const IntegrityLabel: FunctionComponent<IntegrityLabelProps> = ({ value }: IntegrityLabelProps) => {
14+
const [selectedItems, setSelectedItems] = useState(["FULL"]);
15+
16+
const parseValue = (value: string): string[] => {
17+
if (value) {
18+
return value.split(",").filter(value => value && value.trim().length > 0);
19+
}
20+
return [];
21+
};
22+
23+
useEffect(() => {
24+
setSelectedItems(parseValue(value));
25+
}, [value]);
26+
27+
const valueToLabel = (value: string): string => {
28+
switch (value) {
29+
case "FULL":
30+
return "Full";
31+
case "NONE":
32+
return "None";
33+
case "NO_DUPLICATES":
34+
return "No duplicates";
35+
case "REFS_EXIST":
36+
return "Refs must exist";
37+
case "ALL_REFS_MAPPED":
38+
return "No unmapped refs";
39+
}
40+
return "";
41+
};
42+
43+
return (
44+
<>
45+
<If condition={value === "UNKNOWN"}>
46+
<span className="rule-value">(Not enabled)</span>
47+
</If>
48+
<If condition={value !== "UNKNOWN"}>
49+
{
50+
selectedItems.map((value, idx) =>
51+
<Label key={idx} style={{ marginLeft: "3px" }}>{ valueToLabel(value) }</Label>)
52+
}
53+
</If>
54+
</>
55+
);
56+
};

ui/ui-app/src/app/components/ruleList/IntegritySelect.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ import { MenuToggle, MenuToggleElement, Select, SelectList, SelectOption, Toolti
55
/**
66
* Properties
77
*/
8-
export type IntegrityDropdownProps = {
8+
export type IntegritySelectProps = {
99
value: string;
1010
onSelect: (newValue: string) => void;
1111
};
1212

13-
export const IntegritySelect: FunctionComponent<IntegrityDropdownProps> = ({ value, onSelect }: IntegrityDropdownProps) => {
13+
export const IntegritySelect: FunctionComponent<IntegritySelectProps> = ({ value, onSelect }: IntegritySelectProps) => {
1414
const menuRef = React.useRef<HTMLDivElement>(null);
1515
const [isOpen, setOpen] = useState<boolean>(false);
1616
const [selectedItems, setSelectedItems] = useState(["FULL"]);

ui/ui-app/src/app/components/ruleList/RuleList.css

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
margin-right: 5px;
1515
}
1616

17-
.rule.disabled-state-text .rule-name, .rule.disabled-state-text .rule-description, .rule.disabled-state-text .rule-icon {
17+
.rule.disabled-state-text .rule-name, .rule.disabled-state-text .rule-description, .rule.disabled-state-text .rule-icon, .rule.disabled-state-text .rule-value {
1818
color: rgb(106, 110, 115);
1919
}
2020

@@ -27,4 +27,4 @@
2727
.rule .rule-actions {
2828
display: inline;
2929
white-space: nowrap;
30-
}
30+
}

ui/ui-app/src/app/components/ruleList/RuleList.tsx

+34-17
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
import React, { FunctionComponent } from "react";
22
import "./RuleList.css";
3-
import { Button, Flex, FlexItem, Grid, GridItem, Tooltip } from "@patternfly/react-core";
3+
import { Button, Flex, FlexItem, Grid, GridItem, TextContent, Tooltip } from "@patternfly/react-core";
44
import { CheckIcon, CodeBranchIcon, OkIcon, TrashIcon } from "@patternfly/react-icons";
5-
import { CompatibilitySelect, IfAuth, IfFeature, IntegritySelect, ValiditySelect } from "@app/components";
5+
import {
6+
CompatibilityLabel,
7+
CompatibilitySelect,
8+
IntegrityLabel,
9+
IntegritySelect,
10+
RuleValue,
11+
ValidityLabel,
12+
ValiditySelect
13+
} from "@app/components";
614
import { Rule } from "@models/rule.model.ts";
715

816

@@ -11,6 +19,7 @@ export type RuleListProps = {
1119
onDisableRule: (ruleType: string) => void;
1220
onConfigureRule: (ruleType: string, config: string) => void;
1321
rules: Rule[];
22+
isGlobalRules: boolean;
1423
};
1524

1625
const NAME_COLUMN_WIDTH: string = "250px";
@@ -63,6 +72,12 @@ export const RuleList: FunctionComponent<RuleListProps> = (props: RuleListProps)
6372
};
6473
};
6574

75+
const validityRuleLabel: React.ReactElement = (
76+
<TextContent>
77+
<ValidityLabel value={getRuleConfig("VALIDITY")} />
78+
</TextContent>
79+
);
80+
6681
let validityRuleActions: React.ReactElement = (
6782
<Button variant="secondary"
6883
key="enable-action"
@@ -82,6 +97,13 @@ export const RuleList: FunctionComponent<RuleListProps> = (props: RuleListProps)
8297
</React.Fragment>
8398
);
8499
}
100+
101+
const compatibilityRuleLabel: React.ReactElement = (
102+
<TextContent>
103+
<CompatibilityLabel value={getRuleConfig("COMPATIBILITY")} />
104+
</TextContent>
105+
);
106+
85107
let compatibilityRuleActions: React.ReactElement = (
86108
<Button variant="secondary"
87109
key="enable-action"
@@ -101,6 +123,13 @@ export const RuleList: FunctionComponent<RuleListProps> = (props: RuleListProps)
101123
</React.Fragment>
102124
);
103125
}
126+
127+
const integrityRuleLabel: React.ReactElement = (
128+
<TextContent>
129+
<IntegrityLabel value={getRuleConfig("INTEGRITY")} />
130+
</TextContent>
131+
);
132+
104133
let integrityRuleActions: React.ReactElement = (
105134
<Button variant="secondary"
106135
key="enable-action"
@@ -147,11 +176,7 @@ export const RuleList: FunctionComponent<RuleListProps> = (props: RuleListProps)
147176
</Tooltip>
148177
</FlexItem>
149178
<FlexItem className="rule-actions">
150-
<IfAuth isDeveloper={true}>
151-
<IfFeature feature="readOnly" isNot={true}>
152-
{ validityRuleActions}
153-
</IfFeature>
154-
</IfAuth>
179+
<RuleValue isGlobalRule={props.isGlobalRules} actions={validityRuleActions} label={validityRuleLabel} />
155180
</FlexItem>
156181
</Flex>
157182
</GridItem>
@@ -169,11 +194,7 @@ export const RuleList: FunctionComponent<RuleListProps> = (props: RuleListProps)
169194
</Tooltip>
170195
</FlexItem>
171196
<FlexItem className="rule-actions">
172-
<IfAuth isDeveloper={true}>
173-
<IfFeature feature="readOnly" isNot={true}>
174-
{ compatibilityRuleActions }
175-
</IfFeature>
176-
</IfAuth>
197+
<RuleValue isGlobalRule={props.isGlobalRules} actions={compatibilityRuleActions} label={compatibilityRuleLabel} />
177198
</FlexItem>
178199
</Flex>
179200
</GridItem>
@@ -191,11 +212,7 @@ export const RuleList: FunctionComponent<RuleListProps> = (props: RuleListProps)
191212
</Tooltip>
192213
</FlexItem>
193214
<FlexItem className="rule-actions">
194-
<IfAuth isDeveloper={true}>
195-
<IfFeature feature="readOnly" isNot={true}>
196-
{ integrityRuleActions }
197-
</IfFeature>
198-
</IfAuth>
215+
<RuleValue isGlobalRule={props.isGlobalRules} actions={integrityRuleActions} label={integrityRuleLabel} />
199216
</FlexItem>
200217
</Flex>
201218
</GridItem>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React, { FunctionComponent } from "react";
2+
import { If } from "@apicurio/common-ui-components";
3+
import { Services } from "@services/services.ts";
4+
5+
6+
export type RuleValueProps = {
7+
isGlobalRule: boolean;
8+
actions: React.ReactElement;
9+
label: React.ReactElement;
10+
};
11+
12+
export const RuleValue: FunctionComponent<RuleValueProps> = (props: RuleValueProps) => {
13+
const readOnly: boolean = Services.getConfigService().featureReadOnly();
14+
const userIsAdmin: boolean = Services.getAuthService().isUserAdmin();
15+
const userIsDev: boolean = Services.getAuthService().isUserDeveloper();
16+
17+
const isEditable: boolean = !readOnly && (props.isGlobalRule ? userIsAdmin : userIsDev);
18+
19+
return (
20+
<>
21+
<If condition={isEditable}>
22+
{ props.actions }
23+
</If>
24+
<If condition={!isEditable}>
25+
{ props.label }
26+
</If>
27+
</>
28+
);
29+
30+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { FunctionComponent } from "react";
2+
import { Label } from "@patternfly/react-core";
3+
import { If } from "@apicurio/common-ui-components";
4+
5+
6+
/**
7+
* Properties
8+
*/
9+
export type ValidityLabelProps = {
10+
value: string;
11+
};
12+
13+
const CONFIG_OPTIONS: any[] = [
14+
{ label: "Full", value: "FULL" },
15+
{ label: "Syntax only", value: "SYNTAX_ONLY" },
16+
{ label: "None", value: "NONE" },
17+
];
18+
19+
const valueToLabel = (value: string): string => {
20+
if (value === "UNKNOWN") {
21+
return "";
22+
}
23+
return CONFIG_OPTIONS.filter(item => item.value === value)[0].label;
24+
};
25+
26+
/**
27+
* Component.
28+
*/
29+
export const ValidityLabel: FunctionComponent<ValidityLabelProps> = (props: ValidityLabelProps) => {
30+
31+
return (
32+
<>
33+
<If condition={props.value === "UNKNOWN"}>
34+
<span className="rule-value">(Not enabled)</span>
35+
</If>
36+
<If condition={props.value !== "UNKNOWN"}>
37+
<Label>{ valueToLabel(props.value) }</Label>
38+
</If>
39+
</>
40+
);
41+
42+
};

ui/ui-app/src/app/components/ruleList/ValiditySelect.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { ObjectSelect } from "@apicurio/common-ui-components";
55
/**
66
* Properties
77
*/
8-
export type ValidityDropdownProps = {
8+
export type ValiditySelectProps = {
99
value: string;
1010
onSelect: (newValue: string) => void;
1111
};
@@ -29,7 +29,7 @@ const valueToItem = (value: string): ConfigItem => {
2929
/**
3030
* Component.
3131
*/
32-
export const ValiditySelect: FunctionComponent<ValidityDropdownProps> = (props: ValidityDropdownProps) => {
32+
export const ValiditySelect: FunctionComponent<ValiditySelectProps> = (props: ValiditySelectProps) => {
3333
const [currentValue, setCurrentValue] = useState(valueToItem(props.value));
3434

3535
const onSelect = (item: any): void => {

0 commit comments

Comments
 (0)