Skip to content

Commit

Permalink
Update extensions and catalog provider implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
rohitkrai03 committed Nov 9, 2020
1 parent f6af0b2 commit d812809
Show file tree
Hide file tree
Showing 22 changed files with 656 additions and 666 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export const useResolvedExtensions = <E extends Extension>(
return resolvedExtensions;
};

type ResolvedExtension<E extends Extension, P = ExtensionProperties<E>> = UpdateExtensionProperties<
LoadedExtension<E>,
ResolvedCodeRefProperties<P>
>;
export type ResolvedExtension<
E extends Extension,
P = ExtensionProperties<E>
> = UpdateExtensionProperties<LoadedExtension<E>, ResolvedCodeRefProperties<P>>;
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { executeReferencedFunction } from '../coderef-utils';
import { codeRefSymbol } from '../coderef-resolver';
import { executeReferencedFunction, getExecutableCodeRef } from '../coderef-utils';

describe('executeReferencedFunction', () => {
it('executes the referenced function with given args and returns its result', async () => {
Expand Down Expand Up @@ -37,3 +38,12 @@ describe('executeReferencedFunction', () => {
expect(result).toBe(null);
});
});

describe('getExecutableCodeRef', () => {
it('should add codeRefSymbol to the codeRef to make it executable', () => {
const codeRef = jest.fn(() => Promise.resolve({}));
expect(codeRef[codeRefSymbol]).toBeFalsy();
const executableCodeRef = getExecutableCodeRef(codeRef);
expect(executableCodeRef[codeRefSymbol]).toBe(true);
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable no-console */

import { CodeRef } from '../types';
import { codeRefSymbol } from './coderef-resolver';

/**
* Execute function referenced by the `CodeRef` with given arguments.
Expand All @@ -21,3 +22,15 @@ export const executeReferencedFunction = async <T extends (...args: any[]) => an
return null;
}
};

/**
* Convert any codeRef to an executable codeRef that can be executed by useResolvedExtension.
*
* Adds codeRefSymbol to the codeRef.
*
* @param ref codeRef that needs to be converted to an executable codeRef.
*/
export const getExecutableCodeRef = (ref: CodeRef): CodeRef => {
ref[codeRefSymbol] = true;
return ref;
};
5 changes: 0 additions & 5 deletions frontend/packages/console-plugin-sdk/src/typings/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,6 @@ export type LazyLoader<T extends {} = {}> = () => Promise<React.ComponentType<Pa
*/
export type CodeRef<T> = () => Promise<T>;

/**
* React Lazy loader component
*/
export type ReactLazyLoader = () => Promise<{ default: React.ComponentType<any> }>;

/**
* From Console application perspective, a plugin is a list of extensions
* enhanced with additional data.
Expand Down
93 changes: 50 additions & 43 deletions frontend/packages/console-plugin-sdk/src/typings/dev-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,39 @@ namespace ExtensionProperties {
normalize: (data: K8sResourceKind[]) => K8sResourceKind[];
}

export interface CatalogItemProvider {
export interface CatalogItemType {
/** Type for the catalog item. */
type: string;
/** Title fpr the catalog item. */
title: string;
/** Catalog ID for which this provider contributes to. Idea here is to be able to support multiple catalogs. */
catalog: string;
/** Fetch items and normalize it for the catalog. Value is a react effect hook. */
provider: CodeRef<() => CatalogItemProviderResult>;
/** Description for the catalog item. */
description?: string;
/** Description for the type specific catalog. */
catalogDescription?: string;
/** Description for the catalog item type. */
typeDescription?: string;
/** Custom filters specific to the catalog item */
filters?: CodeRef<CatalogFilters>;
filters?: CatalogFilter[];
/** Custom groupings specific to the catalog item */
groupings?: CodeRef<CatalogGroupings>;
groupings?: CatalogGrouping[];
}

export interface CatalogItemProvider {
/** Type ID for the catalog item type. */
type: string;
/** Fetch items and normalize it for the catalog. Value is a react effect hook. */
provider: CodeRef<CatalogExtensionHook<CatalogItem[]>>;
}
}

export interface DevCatalogModel extends Extension<ExtensionProperties.DevCatalogModel> {
type: 'DevCatalogModel';
}

export interface CatalogItemType extends Extension<ExtensionProperties.CatalogItemType> {
type: 'Catalog/ItemType';
}

export interface CatalogItemProvider extends Extension<ExtensionProperties.CatalogItemProvider> {
type: 'Catalog/ItemProvider';
}
Expand All @@ -37,32 +48,36 @@ export const isDevCatalogModel = (e: Extension): e is DevCatalogModel => {
return e.type === 'DevCatalogModel';
};

export const isCatalogItemType = (e: Extension): e is CatalogItemType => {
return e.type === 'Catalog/ItemType';
};

export const isCatalogItemProvider = (e: Extension): e is CatalogItemProvider => {
return e.type === 'Catalog/ItemProvider';
};

export type CatalogItemProviderResult = [CatalogItem[], boolean, any];
export type CatalogExtensionHookResult<T> = [T, boolean, any];

type Metadata = {
uid?: string;
name?: string;
namespace?: string;
creationTimestamp?: string;
export type CatalogExtensionHookOptions = {
namespace: string;
};

export type CatalogExtensionHook<T> = (
options: CatalogExtensionHookOptions,
) => CatalogExtensionHookResult<T>;

export type CatalogItem = {
type?: string;
name?: string;
uid: string;
type: string;
name: string;
provider?: string;
description?: string;
tags?: string[];
obj?: {
metadata?: Metadata;
csv?: {
kind?: string;
spec: { displayName: string };
metadata?: Metadata;
};
creationTimestamp?: string;
supportUrl?: string;
documentationUrl?: string;
attributes?: {
[key: string]: string;
};
cta: {
label: string;
Expand All @@ -74,34 +89,26 @@ export type CatalogItem = {
};
details?: {
properties?: CatalogItemDetailsProperty[];
descriptions?: CatalogItemDetailsProperty[];
descriptions?: CatalogItemDetailsDescription[];
};
};

export type CatalogItemDetailsProperty = {
type: CatalogItemDetailsPropertyVariant;
title?: string;
label?: string;
value: string | (() => Promise<string>);
label: string;
value: string | React.ReactNode;
};

export enum CatalogItemDetailsPropertyVariant {
TEXT = 'TEXT',
LINK = 'LINK',
EXTERNAL_LINK = 'EXTERNAL_LINK',
MARKDOWN = 'MARKDOWN',
ASYNC_MARKDOWN = 'ASYNC_MARKDOWN',
TIMESTAMP = 'TIMESTAMP',
}

export type CatalogItemFilterProperties = {
[key: string]: string;
export type CatalogItemDetailsDescription = {
label?: string;
value: string | React.ReactNode;
};

export type CatalogItemGroupingProperties = {
[key: string]: string;
export type CatalogFilter = {
label: string;
attribute: string;
};

export type CatalogFilters = { [key: string]: CatalogItemFilterProperties[] };

export type CatalogGroupings = { [key: string]: CatalogItemGroupingProperties[] };
export type CatalogGrouping = {
label: string;
attribute: string;
};
Original file line number Diff line number Diff line change
@@ -1,16 +1,44 @@
import * as React from 'react';
import { LoadingBox } from '@console/internal/components/utils';
import { PageHeading, skeletonCatalog, StatusBox } from '@console/internal/components/utils';
import { CatalogService } from './service/CatalogServiceProvider';

type CatalogControllerProps = CatalogService;

const CatalogController: React.FC<CatalogControllerProps> = ({ items, loaded, type }) => {
if (!loaded) return <LoadingBox />;
const CatalogController: React.FC<CatalogControllerProps> = ({
type,
items,
loaded,
loadError,
catalogExtensions,
}) => {
let title = 'Developer Catalog';
let description =
'Add shared applications, services, event sources, or source-to-image builders to your project from the Developer catalog. Cluster administrators can customize the content made available in the catalog.';

if (type && catalogExtensions.length > 0) {
title = catalogExtensions[0].properties.title;
description = catalogExtensions[0].properties.catalogDescription;
}

return (
<>
<h1>Catalog Type - {type}</h1>
<h2>Loaded Catalog Items - {items.length}</h2>
<div className="co-m-page__body">
<div className="co-catalog">
<PageHeading title={title} />
<p className="co-catalog-page__description">{description}</p>
<div className="co-catalog__body">
<StatusBox
skeleton={skeletonCatalog}
data={items}
loaded={loaded}
loadError={loadError}
label="Catalog items"
>
<h2>Loaded Catalog Items - {items.length}</h2>
</StatusBox>
</div>
</div>
</div>
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
import * as React from 'react';
import Helmet from 'react-helmet';
import { RouteComponentProps } from 'react-router';
import { getQueryArgument } from '@console/internal/components/utils';
import NamespacedPage from '../NamespacedPage';
import CreateProjectListPage from '../projects/CreateProjectListPage';
import NamespacedPage, { NamespacedPageVariants } from '../NamespacedPage';
import CatalogServiceProvider from './service/CatalogServiceProvider';
import CatalogController from './CatalogController';

const CatalogPage = () => {
type CatalogPageProps = RouteComponentProps<{
ns?: string;
}>;

const CatalogPage: React.FC<CatalogPageProps> = ({ match }) => {
const catalogType = getQueryArgument('catalogType');
const namespace = match.params.ns;

return (
<NamespacedPage hideApplications>
<CatalogServiceProvider type={catalogType}>
{(service) => <CatalogController {...service} />}
</CatalogServiceProvider>
<NamespacedPage variant={NamespacedPageVariants.light} hideApplications>
<Helmet>
<title>Developer Catalog</title>
</Helmet>
{namespace ? (
<CatalogServiceProvider namespace={namespace} catalogType={catalogType}>
{(service) => <CatalogController {...service} />}
</CatalogServiceProvider>
) : (
<CreateProjectListPage title="Developer Catalog">
Select a project to view the Developer Catalog
</CreateProjectListPage>
)}
</NamespacedPage>
);
};
Expand Down

0 comments on commit d812809

Please sign in to comment.