Skip to content

Commit

Permalink
Merge pull request openshift#7196 from jerolimov/odc-5019
Browse files Browse the repository at this point in the history
Get customized catalog categories in dev catalog from server flags
  • Loading branch information
openshift-merge-robot authored Dec 5, 2020
2 parents b5252cf + 471b670 commit f9bd93e
Show file tree
Hide file tree
Showing 17 changed files with 529 additions and 136 deletions.
19 changes: 8 additions & 11 deletions cmd/bridge/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"os"
"strings"

"github.com/coreos/pkg/flagutil"
"github.com/openshift/console/pkg/auth"
"github.com/openshift/console/pkg/bridge"
"github.com/openshift/console/pkg/knative"
Expand Down Expand Up @@ -61,11 +60,13 @@ func main() {
klog.InitFlags(fs)
defer klog.Flush()

// Define commandline / env / config options
fs.String("config", "", "The YAML config file.")

fListen := fs.String("listen", "http://0.0.0.0:9000", "")

fBaseAddress := fs.String("base-address", "", "Format: <http | https>://domainOrIPAddress[:port]. Example: https://openshift.example.com.")
fBasePath := fs.String("base-path", "/", "")
fConfig := fs.String("config", "", "The YAML config file.")

// See https://github.com/openshift/service-serving-cert-signer
fServiceCAFile := fs.String("service-ca-file", "", "CA bundle for OpenShift services signed with the service signing certificates.")
Expand Down Expand Up @@ -121,22 +122,17 @@ func main() {

fLoadTestFactor := fs.Int("load-test-factor", 0, "DEV ONLY. The factor used to multiply k8s API list responses for load testing purposes.")

if err := fs.Parse(os.Args[1:]); err != nil {
fDevCatalogCategories := fs.String("developer-catalog-categories", "", "Allow catalog categories customization. (JSON as string)")

if err := serverconfig.Parse(fs, os.Args[1:], "BRIDGE"); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}

if err := flagutil.SetFlagsFromEnv(fs, "BRIDGE"); err != nil {
if err := serverconfig.Validate(fs); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}

if *fConfig != "" {
if err := serverconfig.SetFlagsFromConfig(fs, *fConfig); err != nil {
klog.Fatalf("Failed to load config: %v", err)
}
}

baseURL := &url.URL{}
if *fBaseAddress != "" {
baseURL = bridge.ValidateFlagIsURL("base-address", *fBaseAddress)
Expand Down Expand Up @@ -232,6 +228,7 @@ func main() {
ThanosPublicURL: thanosPublicURL,
LoadTestFactor: *fLoadTestFactor,
InactivityTimeout: *fInactivityTimeout,
DevCatalogCategories: *fDevCatalogCategories,
}

// if !in-cluster (dev) we should not pass these values to the frontend
Expand Down
1 change: 1 addition & 0 deletions frontend/@types/console/declarations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ declare interface Window {
GOARCH: string;
GOOS: string;
graphqlBaseURL: string;
developerCatalogCategories: string;
};
windowError?: string;
__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: Function;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import * as React from 'react';
import * as _ from 'lodash';
import * as cx from 'classnames';
import { VerticalTabs, VerticalTabsTab } from '@patternfly/react-catalog-view-extension';
import { CatalogCategories } from '../utils/types';
import { CatalogCategory } from '../utils/types';
import { hasActiveDescendant, isActiveTab } from '../utils/category-utils';

type CatalogCategoriesProp = {
categories: CatalogCategories;
categories: CatalogCategory[];
categorizedIds: Record<string, string[]>;
selectedCategory: string;
onSelectCategory: (category: string) => void;
Expand All @@ -20,12 +20,15 @@ const CatalogCategories: React.FC<CatalogCategoriesProp> = ({
}) => {
const activeTab = _.has(categories, selectedCategory);

const renderTabs = (category, selectedCategoryId) => {
const renderTabs = (
category: CatalogCategory & { numItems?: number },
selectedCategoryID: string,
toplevelCategory: boolean,
) => {
if (!categorizedIds[category.id]) return null;

const { id, label, subcategories, numItems } = category;
const active = id === selectedCategory;
const shown = _.has(categories, id);

const tabClasses = cx('text-capitalize', { 'co-catalog-tab__empty': !numItems });

Expand All @@ -37,11 +40,13 @@ const CatalogCategories: React.FC<CatalogCategoriesProp> = ({
className={tabClasses}
onActivate={() => onSelectCategory(id)}
hasActiveDescendant={hasActiveDescendant(selectedCategory, category)}
shown={shown}
shown={toplevelCategory}
>
{subcategories && (
<VerticalTabs restrictTabs activeTab={isActiveTab(selectedCategoryId, category)}>
{_.map(subcategories, (subcategory) => renderTabs(subcategory, selectedCategoryId))}
<VerticalTabs restrictTabs activeTab={isActiveTab(selectedCategoryID, category)}>
{_.map(subcategories, (subcategory) =>
renderTabs(subcategory, selectedCategoryID, false),
)}
</VerticalTabs>
)}
</VerticalTabsTab>
Expand All @@ -50,7 +55,7 @@ const CatalogCategories: React.FC<CatalogCategoriesProp> = ({

return (
<VerticalTabs restrictTabs activeTab={activeTab}>
{_.map(categories, (category) => renderTabs(category, selectedCategory))}
{_.map(categories, (category) => renderTabs(category, selectedCategory, true))}
</VerticalTabs>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
import { SearchInput } from '@patternfly/react-core';
import { Dropdown } from '@console/internal/components/utils';
import { CatalogSortOrder, CatalogStringMap } from '../utils/types';
import { NO_GROUPING } from '../utils/catalog-utils';
import { NO_GROUPING } from '../utils/category-utils';

type CatalogToolbarProps = {
title: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ import { CatalogItem } from '@console/plugin-sdk';
import { isModalOpen } from '@console/internal/components/modals';
import { useQueryParams } from '@console/shared';

import { categorize, findActiveCategory } from '../utils/category-utils';
import {
setURLParams,
updateURLParams,
NO_GROUPING,
getCatalogTypeCounts,
DEFAULT_CATEGORY,
categorize,
findActiveCategory,
ALL_CATEGORY,
OTHER_CATEGORY,
} from '../utils/catalog-utils';
NO_GROUPING,
} from '../utils/category-utils';
import { setURLParams, updateURLParams, getCatalogTypeCounts } from '../utils/catalog-utils';
import {
filterByAttributes,
filterByCategory,
Expand All @@ -25,7 +24,7 @@ import {
} from '../utils/filter-utils';

import {
CatalogCategories as CategoriesType,
CatalogCategory,
CatalogFilterCounts,
CatalogFilters as FiltersType,
CatalogQueryParams,
Expand All @@ -46,7 +45,7 @@ type CatalogViewProps = {
items: CatalogItem[];
catalogType: string;
catalogTypes: CatalogType[];
categories: CategoriesType;
categories: CatalogCategory[];
filters: FiltersType;
filterGroups: string[];
filterGroupNameMap: CatalogStringMap;
Expand All @@ -67,7 +66,7 @@ const CatalogView: React.FC<CatalogViewProps> = ({
}) => {
const { t } = useTranslation();
const queryParams = useQueryParams();
const activeCategoryId = queryParams.get(CatalogQueryParams.CATEGORY) ?? DEFAULT_CATEGORY;
const activeCategoryId = queryParams.get(CatalogQueryParams.CATEGORY) ?? ALL_CATEGORY;
const activeSearchKeyword = queryParams.get(CatalogQueryParams.KEYWORD) ?? '';
const activeGrouping = queryParams.get(CatalogQueryParams.GROUPING) ?? NO_GROUPING;
const sortOrder =
Expand Down Expand Up @@ -145,15 +144,10 @@ const CatalogView: React.FC<CatalogViewProps> = ({
});
}, []);

const catalogCategories = React.useMemo(() => {
const allCategory = { id: DEFAULT_CATEGORY, label: t('devconsole~All items') };
const catalogCategories = React.useMemo<CatalogCategory[]>(() => {
const allCategory = { id: ALL_CATEGORY, label: t('devconsole~All items') };
const otherCategory = { id: OTHER_CATEGORY, label: t('devconsole~Other') };

return {
all: allCategory,
...categories,
other: otherCategory,
};
return [allCategory, ...categories, otherCategory];
}, [categories, t]);

const categorizedIds = React.useMemo(() => categorize(items, catalogCategories), [
Expand All @@ -164,7 +158,7 @@ const CatalogView: React.FC<CatalogViewProps> = ({
const activeCategory = React.useMemo(
() =>
findActiveCategory(activeCategoryId, catalogCategories) ||
findActiveCategory(DEFAULT_CATEGORY, catalogCategories),
findActiveCategory(ALL_CATEGORY, catalogCategories),
[activeCategoryId, catalogCategories],
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,36 @@
import * as React from 'react';

import { defaultCatalogCategories } from '../utils/default-categories';
import { CatalogCategories } from '../utils/types';
import { CatalogCategory } from '../utils/types';

const useCatalogCategories = (): CatalogCategory[] => {
const categories = React.useMemo<CatalogCategory[]>(() => {
try {
const categoriesString = window.SERVER_FLAGS.developerCatalogCategories;
if (!categoriesString) {
return defaultCatalogCategories;
}

const categoriesArray: CatalogCategory[] = JSON.parse(categoriesString);

if (!Array.isArray(categoriesArray)) {
// eslint-disable-next-line no-console
console.error(
`Unexpected server flag "developerCatalogCategories" format. Expected array, got ${typeof categoriesArray}:`,
categoriesArray,
);
return defaultCatalogCategories;
}

return categoriesArray;
} catch (error) {
// eslint-disable-next-line no-console
console.error('Could not parse server flag "developerCatalogCategories":', error);
return defaultCatalogCategories;
}
}, []);

const useCatalogCategories = (): CatalogCategories => defaultCatalogCategories;
return categories;
};

export default useCatalogCategories;
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,6 @@ import { CatalogItem } from '@console/plugin-sdk';
import * as catalogImg from '@console/internal/imgs/logos/catalog-icon.svg';
import { CatalogType, CatalogTypeCounts } from './types';

export const NO_GROUPING = 'none';

export const DEFAULT_CATEGORY = 'all';

export const OTHER_CATEGORY = 'other';

export const keywordCompare = (filterString: string, item: CatalogItem): boolean => {
if (!filterString) {
return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,33 @@
import * as _ from 'lodash';
import { CatalogItem } from '@console/plugin-sdk';
import { CatalogCategories, CatalogCategory, CatalogSubcategory } from './types';
import { CatalogCategory, CatalogSubcategory } from './types';

export const NO_GROUPING = 'none';
export const ALL_CATEGORY = 'all';
export const OTHER_CATEGORY = 'other';

export const matchSubcategories = (
category: CatalogCategory,
item: CatalogItem,
): (CatalogCategory | CatalogSubcategory)[] => {
if (!category.subcategories) {
if (!category.values) {
if (!category.tags) {
return [];
}

let values = item[category.field];
if (!Array.isArray(values)) {
values = [values];
}

const intersection = [category.values, values].reduce((a, b) => a.filter((c) => b.includes(c)));
const intersection = [category.tags, item.tags || []].reduce((a, b) =>
a.filter((c) => b.includes(c)),
);
if (!_.isEmpty(intersection)) {
return [category];
}

return [];
}

const matchedSubcategories = [];
_.forOwn(category.subcategories, (subCategory) => {
let values = item[category.field];

if (!Array.isArray(values)) {
values = [values];
}

const valuesIntersection = [subCategory.values, values].reduce((a, b) =>
const matchedSubcategories: (CatalogCategory | CatalogSubcategory)[] = [];
_.each(category.subcategories, (subCategory) => {
const valuesIntersection = [subCategory.tags, item.tags || []].reduce((a, b) =>
a.filter((c) => b.includes(c)),
);
if (!_.isEmpty(valuesIntersection)) {
Expand Down Expand Up @@ -63,17 +58,17 @@ export const addItem = (
*/
export const categorize = (
items: CatalogItem[],
categories: CatalogCategories,
categories: CatalogCategory[],
): Record<string, string[]> => {
const categorizedIds = {};

// Categorize each item
_.each(items, (item) => {
let itemCategorized = false;

addItem(categorizedIds, item.uid, categories.all.id); // add each item to all category
addItem(categorizedIds, item.uid, ALL_CATEGORY); // add each item to all category

_.each(categories, (category) => {
_.each(categories, (category: CatalogCategory) => {
const matchedSubcategories = matchSubcategories(category, item);
_.each(matchedSubcategories, (subcategory) => {
addItem(categorizedIds, item.uid, category.id);
Expand All @@ -83,7 +78,7 @@ export const categorize = (
});

if (!itemCategorized) {
addItem(categorizedIds, item.uid, categories.other.id); // add to Other category
addItem(categorizedIds, item.uid, OTHER_CATEGORY); // add to Other category
}
});

Expand All @@ -92,33 +87,33 @@ export const categorize = (

export const findActiveCategory = (
activeId: string,
categories: CatalogCategories,
categories: CatalogCategory[],
): CatalogCategory => {
let activeCategory = null;
_.forOwn(categories, (category) => {
_.each(categories, (category) => {
if (activeCategory) {
return;
}

if (category.id === activeId) {
activeCategory = category;
} else {
} else if (category.subcategories) {
activeCategory = findActiveCategory(activeId, category.subcategories);
}
});
return activeCategory;
};

export const hasActiveDescendant = (activeId: string, category: CatalogCategory): boolean => {
if (_.has(category?.subcategories, activeId)) {
if (_.has(category.subcategories, activeId)) {
return true;
}

return _.some(category?.subcategories, (subcategory) =>
return _.some(category.subcategories, (subcategory) =>
hasActiveDescendant(activeId, subcategory),
);
};

export const isActiveTab = (activeId: string, category: CatalogCategory): boolean => {
return _.has(category?.subcategories, activeId);
return _.has(category.subcategories, activeId);
};
Loading

0 comments on commit f9bd93e

Please sign in to comment.