Skip to content

Commit

Permalink
Merge pull request #6178 from TheRealJon/CONSOLE-2328
Browse files Browse the repository at this point in the history
Support schema-grouped descriptor arrays on operand details page
  • Loading branch information
openshift-merge-robot committed Jul 31, 2020
2 parents 9958d52 + 9f0b12d commit 64d5d79
Show file tree
Hide file tree
Showing 7 changed files with 297 additions and 102 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,20 @@ export const REGEXP_SELECT_OPTION = new RegExp(`${REGEXP_SELECT_CAPABILITY}(.*)$
export const REGEXP_FIELD_DEPENDENCY_PATH_VALUE = new RegExp(
`^${REGEXP_FIELD_DEPENDENCY_CAPABILITY}([^:]*):(.*)$`,
);

// Matches a path string containing an array reference. Captures
// the segment before the array reference, and the segment after.
// For example:
// path[0].element.property -> [path, element.property]
export const REGEXP_ARRAY_PATH = /^(.*)\[\d+\]\.?(.*)$/;

// Matches a path string with multiple array references.
// e.g.: nested[0].array[0].property
export const REGEXP_NESTED_ARRAY_PATH = /^.*\[\d+\]\.?.*\[\d+\]\.?.*$/;

// Captures the root segment of a path string, and all desscendent segments as
// a single string.
// For example:
// 'this.is.a.really.long.path' -> ['this', 'is.a.really.long.path'])
// 'this' -> ['this']
export const REGEXP_CAPTURE_GROUP_SUBGROUP = /^([^.]*)\.?(.*)$/;
Original file line number Diff line number Diff line change
Expand Up @@ -8,129 +8,193 @@ import { JSONSchema6 } from 'json-schema';
import { SpecDescriptorDetailsItem } from './spec';
import { StatusDescriptorDetailsItem } from './status';
import { withFallback } from '@console/shared/src/components/error/error-boundary';
import { DescriptorGroup, groupDescriptorDetails } from './utils';

export const DescriptorDetailsItem = withFallback<DescriptorDetailsItemProps>(
({ descriptor, model, obj, onError, schema, type }) => {
const propertySchema = getSchemaAtPath(schema, descriptor.path);
const description = descriptor?.description || propertySchema?.description;
const propertySchema = getSchemaAtPath(schema, `${type}.${descriptor.path}`);
const fullPath = [type, ..._.toPath(descriptor.path)];
const label = descriptor.displayName || propertySchema?.title || _.startCase(_.last(fullPath));
const label =
descriptor.displayName ||
propertySchema?.title ||
_.startCase(_.last(descriptor.path.split('.')));
const description = descriptor?.description || propertySchema?.description;
const value = _.get(obj, fullPath, descriptor.value);
const descriptorProps = {
description,
descriptor,
fullPath,
label,
model,
obj,
onError,
value,
};
switch (type) {
case DescriptorType.spec:
return (
<SpecDescriptorDetailsItem
description={description}
descriptor={descriptor}
label={label}
model={model}
obj={obj}
onError={onError}
fullPath={fullPath}
value={value}
/>
);
return <SpecDescriptorDetailsItem {...descriptorProps} />;
case DescriptorType.status:
return (
<StatusDescriptorDetailsItem
description={description}
descriptor={descriptor}
label={label}
model={model}
obj={obj}
onError={onError}
fullPath={fullPath}
value={value}
/>
);
return <StatusDescriptorDetailsItem {...descriptorProps} />;
default:
return null;
}
},
);

const DescriptorDetailsItemArrayGroup: React.FC<DescriptorDetailsItemGroupProps> = ({
group,
groupPath,
model,
obj,
onError,
schema,
type,
}) => {
const { arrayGroupPath, elementDescriptor, descriptor, nested } = group;
const arrayGroupSchema = getSchemaAtPath(schema, `${type}.${arrayGroupPath}`);
const description = descriptor?.description || arrayGroupSchema?.description;
const label =
descriptor?.displayName ||
arrayGroupSchema?.title ||
_.startCase(_.last(arrayGroupPath.split('.')));
const arrayElementDescriptors = nested ?? [elementDescriptor];
const value = _.get(obj, [type, ..._.toPath(arrayGroupPath)], []);
return (
<DetailsItem description={description} label={label} obj={obj} path={`${type}.${groupPath}`}>
<div className="details-item__array">
{value?.length ? (
_.times(value.length, (i) => (
<div className="details-item__value--group">
<dl>
{_.map(arrayElementDescriptors, (primitiveDescriptor: Descriptor) => {
const path = primitiveDescriptor.path.replace(/\d+/, String(i));
return (
<DescriptorDetailsItem
descriptor={{ ...primitiveDescriptor, path }}
key={`${type}.${path}`}
model={model}
obj={obj}
onError={onError}
schema={getSchemaAtPath(schema, path)}
type={type}
/>
);
})}
</dl>
</div>
))
) : (
<span className="text-muted">None</span>
)}
</div>
</DetailsItem>
);
};

const DescriptorDetailsItemGroup: React.FC<DescriptorDetailsItemGroupProps> = ({
descriptors,
groupName,
group,
groupPath,
model,
obj,
onError,
schema,
type,
}) => {
const propertySchema = getSchemaAtPath(schema, groupName) ?? {};
const { root, descendants } = _.groupBy(descriptors, (descriptor) =>
descriptor.path === groupName ? 'root' : 'descendants',
const { descriptor, nested } = group;
const groupSchema = getSchemaAtPath(schema, `${type}.${groupPath}`);
const description = descriptor?.description || groupSchema?.description;
const label = descriptor?.displayName || groupSchema?.title || _.startCase(groupPath);
const arrayGroups = _.pickBy(nested, 'isArrayGroup');
const primitives = _.omitBy(nested, 'isArrayGroup');
const className = _.isEmpty(arrayGroups) || _.isEmpty(primitives) ? 'col-sm-6' : 'col-sm-12';
return (
<div className={className}>
<DetailsItem description={description} label={label} obj={obj} path={`${type}.${groupPath}`}>
<dl className="details-item__value--group olm-descriptors__group">
{!_.isEmpty(primitives) && (
<div>
{_.map(primitives, ({ descriptor: primitiveDescriptor }) => (
<DescriptorDetailsItem
descriptor={primitiveDescriptor}
key={`${type}.${primitiveDescriptor.path}`}
model={model}
obj={obj}
onError={onError}
schema={schema}
type={type}
/>
))}
</div>
)}
{!_.isEmpty(arrayGroups) && (
<div>
{_.map(arrayGroups, (arrayGroup: DescriptorGroup) => (
<DescriptorDetailsItemArrayGroup
group={arrayGroup}
groupPath={arrayGroup.arrayGroupPath}
key={`${type}.${groupPath}.${arrayGroup.arrayGroupPath}`}
model={model}
obj={obj}
onError={onError}
schema={schema}
type={type}
/>
))}
</div>
)}
</dl>
</DetailsItem>
</div>
);
const description = root?.[0]?.description || propertySchema?.description;
const label = root?.[0]?.displayName || propertySchema?.title || _.startCase(groupName);
return descendants?.length > 0 ? (
<DetailsItem
description={description}
label={label}
obj={obj}
path={`${type}.${groupName}`}
valueClassName="details-item__value--group"
>
<dl>
{descendants.map((descriptor) => (
<DescriptorDetailsItem
key={`${type}.${descriptor.path}`}
descriptor={descriptor}
model={model}
obj={obj}
onError={onError}
schema={schema}
type={type}
/>
))}
</dl>
</DetailsItem>
) : null;
};

export const DescriptorDetailsItemList: React.FC<DescriptorDetailsItemListProps> = ({
descriptors,
itemClassName,
model,
obj,
onError,
schema,
type,
}) => {
const groupedDescriptors = (descriptors ?? []).reduce((acc, descriptor) => {
const [key] = _.toPath(descriptor.path);
return {
...acc,
[key]: [...(acc?.[key] ?? []), descriptor],
};
}, {});
const groupedDescriptors = React.useMemo(() => groupDescriptorDetails(descriptors), [
descriptors,
]);
return (
<dl className={`olm-descriptors olm-descriptors--${type}`}>
{_.map(groupedDescriptors, (group: Descriptor[], groupName) => (
<div key={`${type}.${groupName}`} className={itemClassName}>
{group.length > 1 ? (
<DescriptorDetailsItemGroup
descriptors={group}
groupName={groupName}
model={model}
obj={obj}
onError={onError}
schema={schema}
type={type}
/>
) : (
<DescriptorDetailsItem
descriptor={group[0]}
model={model}
obj={obj}
onError={onError}
schema={schema}
type={type}
/>
)}
</div>
))}
{_.map(groupedDescriptors, (group, groupPath) => {
const groupProps = {
group,
groupPath,
};

const commonProps = {
key: `${type}.${groupPath}`,
model,
obj,
onError,
schema,
type,
};

const { isArrayGroup, descriptor, nested } = group;
if (isArrayGroup) {
return (
<div className="col-sm-6">
<DescriptorDetailsItemArrayGroup {...groupProps} {...commonProps} />
</div>
);
}

if (!_.isEmpty(nested)) {
return <DescriptorDetailsItemGroup {...groupProps} {...commonProps} />;
}

return (
<div className="col-sm-6">
<DescriptorDetailsItem descriptor={descriptor} {...commonProps} />
</div>
);
})}
</dl>
);
};
Expand All @@ -145,12 +209,16 @@ export type DescriptorDetailsItemProps = {
};

type DescriptorDetailsItemGroupProps = Omit<DescriptorDetailsItemProps, 'descriptor'> & {
descriptors: Descriptor[];
groupName: string;
group: DescriptorGroup;
groupPath: string;
type: DescriptorType;
};

type DescriptorDetailsItemListProps = Omit<DescriptorDetailsItemGroupProps, 'groupName'> & {
type DescriptorDetailsItemListProps = Omit<
DescriptorDetailsItemGroupProps,
'groupPath' | 'group'
> & {
descriptors: Descriptor[];
itemClassName?: string;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { EndpointList } from './endpoint';
import { configureSizeModal } from './configure-size';
import { configureUpdateStrategyModal } from './configure-update-strategy';
import { DefaultCapability, K8sResourceLinkCapability } from '../common';
import { getPatchPathFromDescriptor } from '../utils';

const PodCount: React.FC<SpecCapabilityProps> = ({
description,
Expand Down Expand Up @@ -146,7 +147,7 @@ const BooleanSwitch: React.FC<SpecCapabilityProps> = ({

if (_.has(obj, `spec.${descriptor.path}`)) {
const patchFor = (val: boolean) => [
{ op: 'add', path: `/spec/${descriptor.path.replace(/\./g, '/')}`, value: val },
{ op: 'add', path: `/spec/${getPatchPathFromDescriptor(descriptor)}`, value: val },
];
return k8sPatch(model, obj, patchFor(checked)).catch((err) => errorCb(err));
}
Expand Down Expand Up @@ -202,7 +203,7 @@ const CheckboxUIComponent: React.FC<SpecCapabilityProps> = ({
const [confirmed, setConfirmed] = React.useState(false);

const patchFor = (val: boolean) => [
{ op: 'add', path: `/spec/${descriptor.path.replace('.', '/')}`, value: val },
{ op: 'add', path: `/spec/${getPatchPathFromDescriptor(descriptor)}`, value: val },
];
const update = () => {
setConfirmed(true);
Expand Down

0 comments on commit 64d5d79

Please sign in to comment.