From b617083159f4f61327bdc2b3839bc246acd8b3ff Mon Sep 17 00:00:00 2001 From: Nir Gazit Date: Tue, 5 Jan 2021 12:46:05 +0200 Subject: [PATCH] Added a table for showing consumer group topic offsets --- plugins/kafka/package.json | 4 +- plugins/kafka/src/Router.tsx | 38 ++++++ plugins/kafka/src/api/KafkaApi.ts | 112 ++++++++++++++++++ .../ExampleComponent => api}/index.ts | 3 +- .../ConsumerGroupOffsets.tsx | 111 +++++++++++++++++ .../useConsumerGroupOffsets.ts | 63 ++++++++++ .../useConsumerGroupsFromEntity.ts | 24 ++++ .../ExampleComponent.test.tsx | 45 ------- .../ExampleComponent/ExampleComponent.tsx | 55 --------- .../ExampleFetchComponent.test.tsx | 40 ------- .../ExampleFetchComponent.tsx | 107 ----------------- .../index.ts => constants.ts} | 3 +- plugins/kafka/src/index.ts | 3 + plugins/kafka/src/plugin.ts | 21 +++- yarn.lock | 12 ++ 15 files changed, 386 insertions(+), 255 deletions(-) create mode 100644 plugins/kafka/src/Router.tsx create mode 100644 plugins/kafka/src/api/KafkaApi.ts rename plugins/kafka/src/{components/ExampleComponent => api}/index.ts (92%) create mode 100644 plugins/kafka/src/components/ConsumerGroupOffsets/ConsumerGroupOffsets.tsx create mode 100644 plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupOffsets.ts create mode 100644 plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsFromEntity.ts delete mode 100644 plugins/kafka/src/components/ExampleComponent/ExampleComponent.test.tsx delete mode 100644 plugins/kafka/src/components/ExampleComponent/ExampleComponent.tsx delete mode 100644 plugins/kafka/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx delete mode 100644 plugins/kafka/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx rename plugins/kafka/src/{components/ExampleFetchComponent/index.ts => constants.ts} (87%) diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index f5dc01ec45..098d9a426c 100644 --- a/plugins/kafka/package.json +++ b/plugins/kafka/package.json @@ -23,9 +23,10 @@ "@backstage/core": "^0.4.1", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", - "@material-ui/icons": "^4.9.1", + "@material-ui/icons": "^4.11.2", "@material-ui/lab": "4.0.0-alpha.45", "kafkajs": "^1.15.0", + "lodash": "^4.17.20", "react": "^16.13.1", "react-dom": "^16.13.1", "react-use": "^15.3.3" @@ -38,6 +39,7 @@ "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", + "@types/lodash": "^4.14.166", "@types/node": "^12.0.0", "cross-fetch": "^3.0.6", "msw": "^0.21.2" diff --git a/plugins/kafka/src/Router.tsx b/plugins/kafka/src/Router.tsx new file mode 100644 index 0000000000..9de9588bd6 --- /dev/null +++ b/plugins/kafka/src/Router.tsx @@ -0,0 +1,38 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '@backstage/catalog-model'; +import { MissingAnnotationEmptyState } from '@backstage/core'; +import React from 'react'; +import { Route, Routes } from 'react-router'; +import { KAFKA_CONSUMER_GROUP_ANNOTATION } from './constants'; +import { KafkaTopicsForConsumer } from './components/ConsumerGroupOffsets/ConsumerGroupOffsets'; + +export const isPluginApplicableToEntity = (entity: Entity) => + Boolean(entity.metadata.annotations?.[KAFKA_CONSUMER_GROUP_ANNOTATION]); + +export const Router = ({ entity }: { entity: Entity }) => { + return !isPluginApplicableToEntity(entity) ? ( + + ) : ( + + } + /> + + ); +}; diff --git a/plugins/kafka/src/api/KafkaApi.ts b/plugins/kafka/src/api/KafkaApi.ts new file mode 100644 index 0000000000..63ac948047 --- /dev/null +++ b/plugins/kafka/src/api/KafkaApi.ts @@ -0,0 +1,112 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ConfigApi, DiscoveryApi, createApiRef } from '@backstage/core'; +import { Kafka, SeekEntry } from 'kafkajs'; + +export const kafkaApiRef = createApiRef({ + id: 'plugin.kafka.service', + description: 'Used by the Kafka plugin to connect to the Kafka cluster', +}); + +export type TopicOffsets = { + topic: string; + partitions: { + id: number; + offset: string; + }[]; +}; + +export class KafkaApi { + static fromConfig(configApi: ConfigApi, discoveryApi: DiscoveryApi) { + const clientId = + configApi.getOptionalString('kafka.clientId') ?? 'kafka-backstage'; + const brokers = configApi.getOptionalStringArray('kafka.brokers') ?? [ + '/kafka/broker', + ]; + + return new KafkaApi(discoveryApi, clientId, brokers); + } + + constructor( + private readonly discoveryApi: DiscoveryApi, + private readonly clientId: string, + private readonly brokers: string[], + ) {} + + async fetchTopicsOffsets(topics: string[]): Promise> { + const admin = await this.getAdmin(); + + await admin.connect(); + try { + return await Promise.all( + topics.map(async topic => + admin + .fetchTopicOffsets(topic) + .then(result => KafkaApi.toTopicOffsets(topic, result)), + ), + ); + } finally { + await admin.disconnect(); + } + } + + async fetchGroupOffsets(groupId: string): Promise> { + const admin = await this.getAdmin(); + + await admin.connect(); + try { + let groupOffsets = [ + { + topic: 'shula', + partitions: await admin.fetchOffsets({ groupId, topic: 'shula' }), + }, + { + topic: 'test', + partitions: await admin.fetchOffsets({ groupId, topic: 'test' }), + }, + ]; + + return groupOffsets.map(topicOffset => + KafkaApi.toTopicOffsets(topicOffset.topic, topicOffset.partitions), + ); + } finally { + await admin.disconnect(); + } + } + + private async getAdmin() { + const kafka = await this.getKafka(); + return kafka.admin(); + } + + private async getKafka() { + const proxyUrl = await this.discoveryApi.getBaseUrl('proxy'); + + return new Kafka({ + clientId: this.clientId, + brokers: this.brokers.map(brokerUrl => proxyUrl + brokerUrl), + }); + } + + private static toTopicOffsets(topic: string, result: Array) { + const partitions = result.map(seekEntry => ({ + id: seekEntry.partition, + offset: seekEntry.offset, + })); + return { topic, partitions }; + } +} diff --git a/plugins/kafka/src/components/ExampleComponent/index.ts b/plugins/kafka/src/api/index.ts similarity index 92% rename from plugins/kafka/src/components/ExampleComponent/index.ts rename to plugins/kafka/src/api/index.ts index 2c6f338ec7..cd78c09cad 100644 --- a/plugins/kafka/src/components/ExampleComponent/index.ts +++ b/plugins/kafka/src/api/index.ts @@ -13,4 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { default } from './ExampleComponent'; + +export { KafkaApi } from './KafkaApi'; diff --git a/plugins/kafka/src/components/ConsumerGroupOffsets/ConsumerGroupOffsets.tsx b/plugins/kafka/src/components/ConsumerGroupOffsets/ConsumerGroupOffsets.tsx new file mode 100644 index 0000000000..0f697e4994 --- /dev/null +++ b/plugins/kafka/src/components/ConsumerGroupOffsets/ConsumerGroupOffsets.tsx @@ -0,0 +1,111 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Table, TableColumn } from '@backstage/core'; +import { Box, Typography } from '@material-ui/core'; +import RetryIcon from '@material-ui/icons/Replay'; +import React from 'react'; +import { useConsumerGroupFromEntity } from './useConsumerGroupsFromEntity'; +import { useConsumerGroupOffsets } from './useConsumerGroupOffsets'; + +export type TopicPartitionInfo = { + topic: string; + partitionId: number; + topicOffset: string; + groupOffset: string; +}; + +const generatedColumns: TableColumn[] = [ + { + title: 'Topic', + field: 'topic', + highlight: true, + render: (row: Partial) => { + return <>{row.topic ?? ''}; + }, + }, + { + title: 'Partition', + field: 'partitionId', + render: (row: Partial) => { + return <>{row.partitionId ?? ''}; + }, + }, + { + title: 'Topic Offset', + field: 'topicOffset', + render: (row: Partial) => { + return <>{row.topicOffset ?? ''}; + }, + }, + { + title: 'Group Offset', + field: 'groupOffset', + render: (row: Partial) => { + return <>{row.groupOffset ?? ''}; + }, + }, +]; + +type Props = { + loading: boolean; + retry: () => void; + consumerGroup: string; + topics?: TopicPartitionInfo[]; +}; + +export const ConsumerGroupOffsets = ({ + loading, + topics, + consumerGroup, + retry, +}: Props) => { + return ( + , + tooltip: 'Refresh Data', + isFreeAction: true, + onClick: () => retry(), + }, + ]} + data={topics ?? []} + title={ + + + Consumed Topics for {consumerGroup} + + + } + columns={generatedColumns} + /> + ); +}; + +export const KafkaTopicsForConsumer = () => { + const consumerGroup = useConsumerGroupFromEntity(); + const [tableProps, { retry }] = useConsumerGroupOffsets(consumerGroup); + + return ( + + ); +}; diff --git a/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupOffsets.ts b/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupOffsets.ts new file mode 100644 index 0000000000..95c6e0892d --- /dev/null +++ b/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupOffsets.ts @@ -0,0 +1,63 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { errorApiRef, useApi } from '@backstage/core'; +import { useAsyncRetry } from 'react-use'; +import { kafkaApiRef } from '../../api/KafkaApi'; +import _ from 'lodash'; + +export function useConsumerGroupOffsets(groupId: string) { + const api = useApi(kafkaApiRef); + const errorApi = useApi(errorApiRef); + + const { loading, value: topics, retry } = useAsyncRetry(async () => { + try { + const groupOnlyOffsets = await api.fetchGroupOffsets(groupId); + const topicOffsets = _.keyBy( + await api.fetchTopicsOffsets( + groupOnlyOffsets.map(value => value.topic), + ), + offsets => offsets.topic, + ); + + return groupOnlyOffsets.flatMap(value => { + let topicPartitionOffsets = _.keyBy( + topicOffsets[value.topic].partitions, + partition => partition.id, + ); + return value.partitions.map(partition => ({ + topic: value.topic, + partitionId: partition.id, + groupOffset: partition.offset, + topicOffset: topicPartitionOffsets[partition.id].offset, + })); + }); + } catch (e) { + errorApi.post(e); + throw e; + } + }, [api, errorApi, groupId]); + + return [ + { + loading, + topics, + }, + { + retry, + }, + ] as const; +} diff --git a/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsFromEntity.ts b/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsFromEntity.ts new file mode 100644 index 0000000000..8bad96b61c --- /dev/null +++ b/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsFromEntity.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { useEntity } from '@backstage/plugin-catalog'; +import { KAFKA_CONSUMER_GROUP_ANNOTATION } from '../../constants'; + +export const useConsumerGroupFromEntity = () => { + const { entity } = useEntity(); + + return entity.metadata.annotations?.[KAFKA_CONSUMER_GROUP_ANNOTATION] ?? ''; +}; diff --git a/plugins/kafka/src/components/ExampleComponent/ExampleComponent.test.tsx b/plugins/kafka/src/components/ExampleComponent/ExampleComponent.test.tsx deleted file mode 100644 index 680714eeda..0000000000 --- a/plugins/kafka/src/components/ExampleComponent/ExampleComponent.test.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import React from 'react'; -import { render } from '@testing-library/react'; -import ExampleComponent from './ExampleComponent'; -import { ThemeProvider } from '@material-ui/core'; -import { lightTheme } from '@backstage/theme'; -import { rest } from 'msw'; -import { setupServer } from 'msw/node'; -import { msw } from '@backstage/test-utils'; - -describe('ExampleComponent', () => { - const server = setupServer(); - // Enable sane handlers for network requests - msw.setupDefaultHandlers(server); - - // setup mock response - beforeEach(() => { - server.use( - rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.json({}))), - ); - }); - - it('should render', () => { - const rendered = render( - - - , - ); - expect(rendered.getByText('Welcome to kafka!')).toBeInTheDocument(); - }); -}); diff --git a/plugins/kafka/src/components/ExampleComponent/ExampleComponent.tsx b/plugins/kafka/src/components/ExampleComponent/ExampleComponent.tsx deleted file mode 100644 index cfa36acd7b..0000000000 --- a/plugins/kafka/src/components/ExampleComponent/ExampleComponent.tsx +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import React from 'react'; -import { Typography, Grid } from '@material-ui/core'; -import { - InfoCard, - Header, - Page, - Content, - ContentHeader, - HeaderLabel, - SupportButton, -} from '@backstage/core'; -import ExampleFetchComponent from '../ExampleFetchComponent'; - -const ExampleComponent = () => ( - -
- - -
- - - A description of your plugin goes here. - - - - - - All content should be wrapped in a card like this. - - - - - - - - -
-); - -export default ExampleComponent; diff --git a/plugins/kafka/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx b/plugins/kafka/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx deleted file mode 100644 index 0a73ed0fc1..0000000000 --- a/plugins/kafka/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import React from 'react'; -import { render } from '@testing-library/react'; -import ExampleFetchComponent from './ExampleFetchComponent'; -import { rest } from 'msw'; -import { setupServer } from 'msw/node'; -import { msw } from '@backstage/test-utils'; - -describe('ExampleFetchComponent', () => { - const server = setupServer(); - // Enable sane handlers for network requests - msw.setupDefaultHandlers(server); - - // setup mock response - beforeEach(() => { - server.use( - rest.get('https://randomuser.me/*', (_, res, ctx) => - res(ctx.status(200), ctx.delay(2000), ctx.json({})), - ), - ); - }); - it('should render', async () => { - const rendered = render(); - expect(await rendered.findByTestId('progress')).toBeInTheDocument(); - }); -}); diff --git a/plugins/kafka/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx b/plugins/kafka/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx deleted file mode 100644 index 8a4aef11b8..0000000000 --- a/plugins/kafka/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import React from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import { Table, TableColumn, Progress } from '@backstage/core'; -import Alert from '@material-ui/lab/Alert'; -import { useAsync } from 'react-use'; - -const useStyles = makeStyles({ - avatar: { - height: 32, - width: 32, - borderRadius: '50%', - }, -}); - -type User = { - gender: string; // "male" - name: { - title: string; // "Mr", - first: string; // "Duane", - last: string; // "Reed" - }; - location: object; // {street: {number: 5060, name: "Hickory Creek Dr"}, city: "Albany", state: "New South Wales",…} - email: string; // "duane.reed@example.com" - login: object; // {uuid: "4b785022-9a23-4ab9-8a23-cb3fb43969a9", username: "blackdog796", password: "patch",…} - dob: object; // {date: "1983-06-22T12:30:23.016Z", age: 37} - registered: object; // {date: "2006-06-13T18:48:28.037Z", age: 14} - phone: string; // "07-2154-5651" - cell: string; // "0405-592-879" - id: { - name: string; // "TFN", - value: string; // "796260432" - }; - picture: { medium: string }; // {medium: "https://randomuser.me/api/portraits/men/95.jpg",…} - nat: string; // "AU" -}; - -type DenseTableProps = { - users: User[]; -}; - -export const DenseTable = ({ users }: DenseTableProps) => { - const classes = useStyles(); - - const columns: TableColumn[] = [ - { title: 'Avatar', field: 'avatar' }, - { title: 'Name', field: 'name' }, - { title: 'Email', field: 'email' }, - { title: 'Nationality', field: 'nationality' }, - ]; - - const data = users.map(user => { - return { - avatar: ( - {user.name.first} - ), - name: `${user.name.first} ${user.name.last}`, - email: user.email, - nationality: user.nat, - }; - }); - - return ( -
- ); -}; - -const ExampleFetchComponent = () => { - const { value, loading, error } = useAsync(async (): Promise => { - const response = await fetch('https://randomuser.me/api/?results=20'); - const data = await response.json(); - return data.results; - }, []); - - if (loading) { - return ; - } else if (error) { - return {error.message}; - } - - return ; -}; - -export default ExampleFetchComponent; diff --git a/plugins/kafka/src/components/ExampleFetchComponent/index.ts b/plugins/kafka/src/constants.ts similarity index 87% rename from plugins/kafka/src/components/ExampleFetchComponent/index.ts rename to plugins/kafka/src/constants.ts index e0d021564d..652911a91f 100644 --- a/plugins/kafka/src/components/ExampleFetchComponent/index.ts +++ b/plugins/kafka/src/constants.ts @@ -13,4 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { default } from './ExampleFetchComponent'; +export const KAFKA_CONSUMER_GROUP_ANNOTATION = + 'kafka.apache.org/consumer-groups'; diff --git a/plugins/kafka/src/index.ts b/plugins/kafka/src/index.ts index 224e293890..80670acb81 100644 --- a/plugins/kafka/src/index.ts +++ b/plugins/kafka/src/index.ts @@ -14,3 +14,6 @@ * limitations under the License. */ export { plugin } from './plugin'; +export { KAFKA_CONSUMER_GROUP_ANNOTATION } from './constants'; +export { Router, isPluginApplicableToEntity } from './Router'; +export * from './api'; diff --git a/plugins/kafka/src/plugin.ts b/plugins/kafka/src/plugin.ts index f0bc22481e..a666defa44 100644 --- a/plugins/kafka/src/plugin.ts +++ b/plugins/kafka/src/plugin.ts @@ -13,8 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { createPlugin, createRouteRef } from '@backstage/core'; -import ExampleComponent from './components/ExampleComponent'; +import { + configApiRef, + createApiFactory, + createPlugin, + createRouteRef, + discoveryApiRef, +} from '@backstage/core'; +import { KafkaApi, kafkaApiRef } from './api/KafkaApi'; export const rootRouteRef = createRouteRef({ path: '/kafka', @@ -23,7 +29,12 @@ export const rootRouteRef = createRouteRef({ export const plugin = createPlugin({ id: 'kafka', - register({ router }) { - router.addRoute(rootRouteRef, ExampleComponent); - }, + apis: [ + createApiFactory({ + api: kafkaApiRef, + deps: { discoveryApi: discoveryApiRef, configApi: configApiRef }, + factory: ({ configApi, discoveryApi }) => + KafkaApi.fromConfig(configApi, discoveryApi), + }), + ], }); diff --git a/yarn.lock b/yarn.lock index bb8a845850..0ad51ca447 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3734,6 +3734,13 @@ react-is "^16.8.0" react-transition-group "^4.4.0" +"@material-ui/icons@^4.11.2": + version "4.11.2" + resolved "https://registry.npmjs.org/@material-ui/icons/-/icons-4.11.2.tgz#b3a7353266519cd743b6461ae9fdfcb1b25eb4c5" + integrity sha512-fQNsKX2TxBmqIGJCSi3tGTO/gZ+eJgWmMJkgDiOfyNaunNaxcklJQFaFogYcFl0qFuaEz1qaXYXboa/bUXVSOQ== + dependencies: + "@babel/runtime" "^7.4.4" + "@material-ui/icons@^4.9.1": version "4.9.1" resolved "https://registry.npmjs.org/@material-ui/icons/-/icons-4.9.1.tgz#fdeadf8cb3d89208945b33dbc50c7c616d0bd665" @@ -5866,6 +5873,11 @@ resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.165.tgz#74d55d947452e2de0742bad65270433b63a8c30f" integrity sha512-tjSSOTHhI5mCHTy/OOXYIhi2Wt1qcbHmuXD1Ha7q70CgI/I71afO4XtLb/cVexki1oVYchpul/TOuu3Arcdxrg== +"@types/lodash@^4.14.166": + version "4.14.166" + resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.166.tgz#07e7f2699a149219dbc3c35574f126ec8737688f" + integrity sha512-A3YT/c1oTlyvvW/GQqG86EyqWNrT/tisOIh2mW3YCgcx71TNjiTZA3zYZWA5BCmtsOTXjhliy4c4yEkErw6njA== + "@types/long@^4.0.0": version "4.0.1" resolved "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9"