diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 495f1ff78d..ada20f6931 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,12 +4,13 @@ # The last matching pattern takes precedence. # https://help.github.com/articles/about-codeowners/ -* @backstage/maintainers -/docs/features/techdocs @backstage/techdocs-core -/plugins/cost-insights @backstage/silver-lining -/plugins/cloudbuild @trivago/ebarrios -/plugins/search @backstage/techdocs-core -/plugins/techdocs @backstage/techdocs-core -/plugins/techdocs-backend @backstage/techdocs-core -/packages/techdocs-common @backstage/techdocs-core -/.changeset/cost-insights-* @backstage/silver-lining +* @backstage/maintainers +/.changeset/cost-insights-* @backstage/silver-lining +/docs/features/techdocs @backstage/techdocs-core +/packages/techdocs-common @backstage/techdocs-core +/plugins/cloudbuild @trivago/ebarrios +/plugins/cost-insights @backstage/silver-lining +/plugins/kafka @fiverr/platform +/plugins/search @backstage/techdocs-core +/plugins/techdocs @backstage/techdocs-core +/plugins/techdocs-backend @backstage/techdocs-core diff --git a/packages/app/package.json b/packages/app/package.json index 63710a5c28..1488ee5505 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -18,17 +18,18 @@ "@backstage/plugin-github-actions": "^0.2.5", "@backstage/plugin-gitops-profiles": "^0.2.2", "@backstage/plugin-graphiql": "^0.2.2", - "@backstage/plugin-org": "^0.3.2", "@backstage/plugin-jenkins": "^0.3.4", + "@backstage/plugin-kafka": "^0.1.1", "@backstage/plugin-kubernetes": "^0.3.3", "@backstage/plugin-lighthouse": "^0.2.6", "@backstage/plugin-newrelic": "^0.2.2", + "@backstage/plugin-org": "^0.3.2", "@backstage/plugin-pagerduty": "0.2.3", "@backstage/plugin-register-component": "^0.2.5", "@backstage/plugin-rollbar": "^0.2.7", "@backstage/plugin-scaffolder": "^0.3.4", - "@backstage/plugin-sentry": "^0.3.1", "@backstage/plugin-search": "^0.2.4", + "@backstage/plugin-sentry": "^0.3.1", "@backstage/plugin-tech-radar": "^0.3.1", "@backstage/plugin-techdocs": "^0.5.0", "@backstage/plugin-user-settings": "^0.2.3", diff --git a/packages/app/src/plugins.ts b/packages/app/src/plugins.ts index 6a4924cb0e..f07be07ec1 100644 --- a/packages/app/src/plugins.ts +++ b/packages/app/src/plugins.ts @@ -43,3 +43,4 @@ export { plugin as PagerDuty } from '@backstage/plugin-pagerduty'; export { plugin as Buildkite } from '@roadiehq/backstage-plugin-buildkite'; export { plugin as Search } from '@backstage/plugin-search'; export { plugin as Org } from '@backstage/plugin-org'; +export { plugin as Kafka } from '@backstage/plugin-kafka'; diff --git a/plugins/kafka/.eslintrc.js b/plugins/kafka/.eslintrc.js new file mode 100644 index 0000000000..13573efa9c --- /dev/null +++ b/plugins/kafka/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint')], +}; diff --git a/plugins/kafka/README.md b/plugins/kafka/README.md new file mode 100644 index 0000000000..35a62758f3 --- /dev/null +++ b/plugins/kafka/README.md @@ -0,0 +1,13 @@ +# kafka + +Welcome to the kafka plugin! + +_This plugin was created through the Backstage CLI_ + +## Getting started + +Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/kafka](http://localhost:3000/kafka). + +You can also serve the plugin in isolation by running `yarn start` in the plugin directory. +This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. +It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory. diff --git a/plugins/kafka/dev/index.tsx b/plugins/kafka/dev/index.tsx new file mode 100644 index 0000000000..8890c7e3c4 --- /dev/null +++ b/plugins/kafka/dev/index.tsx @@ -0,0 +1,21 @@ +/* + * 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 { createDevApp } from '@backstage/dev-utils'; +import { plugin } from '../src/plugin'; + +createDevApp() + .registerPlugin(plugin) + .render(); diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json new file mode 100644 index 0000000000..f5dc01ec45 --- /dev/null +++ b/plugins/kafka/package.json @@ -0,0 +1,48 @@ +{ + "name": "@backstage/plugin-kafka", + "version": "0.1.1", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "scripts": { + "build": "backstage-cli plugin:build", + "start": "backstage-cli plugin:serve", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "diff": "backstage-cli plugin:diff", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "dependencies": { + "@backstage/core": "^0.4.1", + "@backstage/theme": "^0.2.2", + "@material-ui/core": "^4.11.0", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.45", + "kafkajs": "^1.15.0", + "react": "^16.13.1", + "react-dom": "^16.13.1", + "react-use": "^15.3.3" + }, + "devDependencies": { + "@backstage/cli": "^0.4.2", + "@backstage/dev-utils": "^0.1.6", + "@backstage/test-utils": "^0.1.5", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^10.4.1", + "@testing-library/user-event": "^12.0.7", + "@types/jest": "^26.0.7", + "@types/node": "^12.0.0", + "cross-fetch": "^3.0.6", + "msw": "^0.21.2" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/kafka/src/components/ExampleComponent/ExampleComponent.test.tsx b/plugins/kafka/src/components/ExampleComponent/ExampleComponent.test.tsx new file mode 100644 index 0000000000..680714eeda --- /dev/null +++ b/plugins/kafka/src/components/ExampleComponent/ExampleComponent.test.tsx @@ -0,0 +1,45 @@ +/* + * 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 new file mode 100644 index 0000000000..cfa36acd7b --- /dev/null +++ b/plugins/kafka/src/components/ExampleComponent/ExampleComponent.tsx @@ -0,0 +1,55 @@ +/* + * 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/ExampleComponent/index.ts b/plugins/kafka/src/components/ExampleComponent/index.ts new file mode 100644 index 0000000000..2c6f338ec7 --- /dev/null +++ b/plugins/kafka/src/components/ExampleComponent/index.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ +export { default } from './ExampleComponent'; diff --git a/plugins/kafka/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx b/plugins/kafka/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx new file mode 100644 index 0000000000..0a73ed0fc1 --- /dev/null +++ b/plugins/kafka/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx @@ -0,0 +1,40 @@ +/* + * 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 new file mode 100644 index 0000000000..8a4aef11b8 --- /dev/null +++ b/plugins/kafka/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx @@ -0,0 +1,107 @@ +/* + * 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/components/ExampleFetchComponent/index.ts new file mode 100644 index 0000000000..e0d021564d --- /dev/null +++ b/plugins/kafka/src/components/ExampleFetchComponent/index.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ +export { default } from './ExampleFetchComponent'; diff --git a/plugins/kafka/src/index.ts b/plugins/kafka/src/index.ts new file mode 100644 index 0000000000..224e293890 --- /dev/null +++ b/plugins/kafka/src/index.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ +export { plugin } from './plugin'; diff --git a/plugins/kafka/src/plugin.test.ts b/plugins/kafka/src/plugin.test.ts new file mode 100644 index 0000000000..8d13aa1727 --- /dev/null +++ b/plugins/kafka/src/plugin.test.ts @@ -0,0 +1,22 @@ +/* + * 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 { plugin } from './plugin'; + +describe('kafka', () => { + it('should export plugin', () => { + expect(plugin).toBeDefined(); + }); +}); diff --git a/plugins/kafka/src/plugin.ts b/plugins/kafka/src/plugin.ts new file mode 100644 index 0000000000..f0bc22481e --- /dev/null +++ b/plugins/kafka/src/plugin.ts @@ -0,0 +1,29 @@ +/* + * 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 { createPlugin, createRouteRef } from '@backstage/core'; +import ExampleComponent from './components/ExampleComponent'; + +export const rootRouteRef = createRouteRef({ + path: '/kafka', + title: 'kafka', +}); + +export const plugin = createPlugin({ + id: 'kafka', + register({ router }) { + router.addRoute(rootRouteRef, ExampleComponent); + }, +}); diff --git a/plugins/kafka/src/setupTests.ts b/plugins/kafka/src/setupTests.ts new file mode 100644 index 0000000000..43b8421558 --- /dev/null +++ b/plugins/kafka/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * 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 '@testing-library/jest-dom'; +import 'cross-fetch/polyfill'; diff --git a/yarn.lock b/yarn.lock index 0923d3ed70..bb8a845850 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1644,9 +1644,11 @@ to-fast-properties "^2.0.0" "@backstage/catalog-model@^0.2.0": - version "0.6.0" + version "0.2.0" + resolved "https://registry.npmjs.org/@backstage/catalog-model/-/catalog-model-0.2.0.tgz#e3fe2a4ddeb6a9b6ec480c80cb2b9c39cb245576" + integrity sha512-Y1ocdRpBlxK/VrJQjHlQd0bgADECd1B2NRjwd8ss46ibT5hwLvMOfD80+Fa7oPLu0ktJrH4lq0pNIIJIml48zA== dependencies: - "@backstage/config" "^0.1.2" + "@backstage/config" "^0.1.1" "@types/json-schema" "^7.0.5" "@types/yup" "^0.29.8" json-schema "^0.2.5" @@ -1655,9 +1657,11 @@ yup "^0.29.3" "@backstage/catalog-model@^0.3.0": - version "0.6.0" + version "0.3.1" + resolved "https://registry.npmjs.org/@backstage/catalog-model/-/catalog-model-0.3.1.tgz#45d08e2f333c9c566b2bf2629fd707fe989bb404" + integrity sha512-9XhV7c4rmVW+Yzj2PiwTQ7DsegWGB3C4ELsDRExuEVZONdqNcC02cyJtrt3fT5F31ZS3tHkB9bMUymFOBLqUSA== dependencies: - "@backstage/config" "^0.1.2" + "@backstage/config" "^0.1.1" "@types/json-schema" "^7.0.5" "@types/yup" "^0.29.8" json-schema "^0.2.5" @@ -1666,16 +1670,17 @@ yup "^0.29.3" "@backstage/core@^0.3.0": - version "0.4.1" + version "0.3.2" + resolved "https://registry.npmjs.org/@backstage/core/-/core-0.3.2.tgz#a8209126d5076cf4a8b9bd632fe4e5e2edb62916" + integrity sha512-i5d+Wh8js4qEWoAsPY5L7HVSWpumr1OhfF2dUCGYdyW6AMqVJPca6+n6zp1Rg2CO+J9norp44XAVVCbyhtUpig== dependencies: - "@backstage/config" "^0.1.2" - "@backstage/core-api" "^0.2.6" - "@backstage/theme" "^0.2.2" + "@backstage/config" "^0.1.1" + "@backstage/core-api" "^0.2.1" + "@backstage/theme" "^0.2.1" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" "@material-ui/lab" "4.0.0-alpha.45" "@types/dagre" "^0.7.44" - "@types/prop-types" "^15.7.3" "@types/react" "^16.9" "@types/react-sparklines" "^1.7.0" classnames "^2.2.6" @@ -16181,6 +16186,11 @@ jwt-decode@*, jwt-decode@^3.1.0: resolved "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== +kafkajs@^1.15.0: + version "1.15.0" + resolved "https://registry.npmjs.org/kafkajs/-/kafkajs-1.15.0.tgz#a5ada0d933edca2149177393562be6fb0875ec3a" + integrity sha512-yjPyEnQCkPxAuQLIJnY5dI+xnmmgXmhuOQ1GVxClG5KTOV/rJcW1qA3UfvyEJKTp/RTSqQnUR3HJsKFvHyTpNg== + keyv@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9"