diff --git a/frontend/packages/console-shared/src/components/index.ts b/frontend/packages/console-shared/src/components/index.ts new file mode 100644 index 00000000000..04bca77e0de --- /dev/null +++ b/frontend/packages/console-shared/src/components/index.ts @@ -0,0 +1 @@ +export * from './utils'; diff --git a/frontend/packages/console-shared/src/components/utils/index.ts b/frontend/packages/console-shared/src/components/utils/index.ts new file mode 100644 index 00000000000..b7a9b77869f --- /dev/null +++ b/frontend/packages/console-shared/src/components/utils/index.ts @@ -0,0 +1 @@ +export * from './with-resources'; diff --git a/frontend/packages/console-shared/src/components/utils/with-resources.tsx b/frontend/packages/console-shared/src/components/utils/with-resources.tsx new file mode 100644 index 00000000000..8fce426da3d --- /dev/null +++ b/frontend/packages/console-shared/src/components/utils/with-resources.tsx @@ -0,0 +1,129 @@ +import * as React from 'react'; +import * as _ from 'lodash-es'; +import { connect } from 'react-redux'; +import { Map as ImmutableMap } from 'immutable'; + +import { Firehose, inject } from '@console/internal/components/utils'; +import { K8sKind, K8sResourceKindReference } from '@console/internal/module/k8s'; + +type ResourceToPropsResult = { + value: { + [key: string]: any; + }; +}; + +type ResourcesProps = { + loaderComponent?: React.ComponentType; + onError?(): void; + resourceMap: any; + resources?: any; + resourceToProps?(): ResourceToPropsResult; + children: React.ReactNode; +}; + +type ResourcesState = { + childrenProps: any; + errors: any[]; + loaded: boolean; +}; + +function checkErrors(resources, onError): void { + if (resources.length > 0) { + if (onError) { + onError(resources); + } + resources.forEach(resource => { + const errorMessage = _.get( + resource.error, + 'json.message', + `Error occured while loading ${resource.resourceConfig.resource.kind}` + ); + const errorCode = _.get(resource.error, 'json.code', ''); + const error = errorCode ? `${errorCode}: ${errorMessage}` : errorMessage; + console.warn(error); // eslint-disable-line + }); + } +} + +function getResourcesState({ resourceMap, resources = {}, resourceToProps = null }): ResourcesState { + const childrenProps = {}; + const errors = []; + let loaded = true; + + Object.keys(resourceMap).forEach(resourceKey => { + const resourceConfig = resourceMap[resourceKey]; + const configResource = resourceConfig.resource; + const resource = _.get(resources, resourceKey); + + if (resource) { + if (resource.loaded) { + childrenProps[resourceKey] = resource.data; + } else if (resourceConfig.required) { + loaded = false; + } + + if (!resourceConfig.ignoreErrors && resource.loadError) { + childrenProps[resourceKey] = configResource.isList ? [] : {}; + errors.push({ error: resource.loadError, resourceConfig }); + } + } else { + // unknown resources (CRD not created in opeshift, etc..) + childrenProps[resourceKey] = configResource.isList ? [] : {}; + } + }); + + return { + childrenProps: { + ...childrenProps, + ...(resourceToProps && loaded ? resourceToProps(childrenProps) : {}), + }, + errors, + loaded, + }; +} + +const Resources = (props: ResourcesProps) => { + const [{ errors, loaded, childrenProps }, setResourcesState] = React.useState(getResourcesState(props)); + const { onError, loaderComponent, children } = props; + + React.useEffect(() => checkErrors(errors, onError), [errors]); + React.useEffect(() => setResourcesState(getResourcesState(props)), [props]); + + const LoaderComponent: React.ComponentType = loaderComponent; + + if (!loaded) { + return LoaderComponent ? : null; + } + + return {inject(children, childrenProps)}; +}; + +const mapStateToProps = ({ k8s }, { resourceMap }) => { + const resources = Object.keys(resourceMap).map(k => resourceMap[k].resource); + return { + k8sModels: resources.reduce( + (models, { kind }) => models.set(kind, k8s.getIn(['RESOURCES', 'models', kind])), + ImmutableMap() + ), + }; +}; + +export type WithResourcesProps = { + k8sModels: ImmutableMap; +} & ResourcesProps; + +export const WithResources = connect(mapStateToProps)(({ resourceMap, k8sModels, ...rest }: WithResourcesProps) => { + const kindExists = Object.keys(resourceMap).some(key => !!k8sModels.get(resourceMap[key].resource.kind)); + + const resourceComponent = ; + + // firehose renders null if kind does not exist + // We can have more queries for the same kind so lets set resource.prop to key to make sure its unique + return kindExists ? ( + ({ ...resourceMap[k].resource, prop: k }))}> + {resourceComponent} + + ) : ( + resourceComponent + ); +}); diff --git a/frontend/packages/console-shared/src/index.ts b/frontend/packages/console-shared/src/index.ts index ff8b4c56321..07635cbbc8e 100644 --- a/frontend/packages/console-shared/src/index.ts +++ b/frontend/packages/console-shared/src/index.ts @@ -1 +1 @@ -export default {}; +export * from './components';