diff --git a/microsite/data/plugins/should-i-deploy.yaml b/microsite/data/plugins/should-i-deploy.yaml new file mode 100644 index 0000000000..828ea50715 --- /dev/null +++ b/microsite/data/plugins/should-i-deploy.yaml @@ -0,0 +1,10 @@ +--- +title: Should I Deploy Today? +author: NandoRFS +authorUrl: https://github.com/NandoRFS +category: Humor +description: Can I deploy this plugin if it's Friday? Oh, hell no!!! +documentation: https://github.com/backstage/backstage/blob/master/plugins/should-i-deploy/README.md +iconUrl: /img/should-i-deploy-logo.png +npmPackageName: '@backstage/plugin-should-i-deploy' +addedDate: '2023-11-03' diff --git a/microsite/static/img/should-i-deploy-logo.png b/microsite/static/img/should-i-deploy-logo.png new file mode 100644 index 0000000000..311544cf53 Binary files /dev/null and b/microsite/static/img/should-i-deploy-logo.png differ diff --git a/plugins/should-i-deploy/.eslintrc.js b/plugins/should-i-deploy/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/should-i-deploy/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/should-i-deploy/CHANGELOG.md b/plugins/should-i-deploy/CHANGELOG.md new file mode 100644 index 0000000000..4e00f59bcc --- /dev/null +++ b/plugins/should-i-deploy/CHANGELOG.md @@ -0,0 +1 @@ +# @backstage/plugin-should-i-deploy diff --git a/plugins/should-i-deploy/README.md b/plugins/should-i-deploy/README.md new file mode 100644 index 0000000000..231a01318e --- /dev/null +++ b/plugins/should-i-deploy/README.md @@ -0,0 +1,54 @@ +# @backstage/plugin-should-i-deploy + +--- + +Welcome to the @backstage/plugin-should-i-deploy plugin! + +## Overview + +--- + +Tired of looking at the calendar to see if today is a good day to deploy? Your problems are solved! This plugin creates the perfect card containing this information for your home! The phrases and background color change according to the day, based on UTC or the time zone. + +### Simple Card + +![Simple card](./docs/should-i-deploy-card-without-title.png) + +### Card with title + +![Simple card](./docs/should-i-deploy-card-with-title.png) + +## Getting started + +--- + +### Install the package + +```bash +# From your Backstage root directory +yarn add --cwd packages/app @backstage/plugin-should-i-deploy +``` + +### Import the card + +Add a **Should I deploy card** to the home page on the `packages/app/src/components/home/HomePage.tsx` add: + +```diff ++ import { ShouldIDeployCard } from '@backstage/plugin-should-i-deploy'; + + +
+ + + + ++ ++ ++ + + {/* ...other items */} +``` + +**_Note_**: Title and time zone are optional. If you don't specify a title, it will not be rendered. If you don't specify a time zone, the API will use UTC. + +This project uses the API available at [shouldideploy](https://github.com/baires/shouldideploy) :heart: diff --git a/plugins/should-i-deploy/catalog-info.yaml b/plugins/should-i-deploy/catalog-info.yaml new file mode 100644 index 0000000000..90be14ce2a --- /dev/null +++ b/plugins/should-i-deploy/catalog-info.yaml @@ -0,0 +1,9 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-should-i-deploy + title: '@backstage/plugin-should-i-deploy' +spec: + lifecycle: experimental + type: backstage-frontend-plugin + owner: maintainers diff --git a/plugins/should-i-deploy/dev/index.tsx b/plugins/should-i-deploy/dev/index.tsx new file mode 100644 index 0000000000..97c5112ba2 --- /dev/null +++ b/plugins/should-i-deploy/dev/index.tsx @@ -0,0 +1,47 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { createDevApp } from '@backstage/dev-utils'; +import { configApiRef } from '@backstage/core-plugin-api'; +import { shouldIDeployPlugin, ShouldIDeployCard } from '../src/plugin'; +import { ShouldIDeployCIApiRef, ShouldIDeployCIClient } from '../src/api'; + +createDevApp() + .registerPlugin(shouldIDeployPlugin) + .registerApi({ + api: ShouldIDeployCIApiRef, + deps: { configApi: configApiRef }, + factory: () => { + const cli = new ShouldIDeployCIClient({ + fetchApi: { + fetch: window.fetch.bind(window), + }, + }); + + return cli; + }, + }) + .addPage({ + element: ( + + ), + title: 'Should I Deploy Today?', + path: '/plugin-should-i-deploy', + }) + .render(); diff --git a/plugins/should-i-deploy/docs/should-i-deploy-card-with-title.png b/plugins/should-i-deploy/docs/should-i-deploy-card-with-title.png new file mode 100644 index 0000000000..4c006c0ed5 Binary files /dev/null and b/plugins/should-i-deploy/docs/should-i-deploy-card-with-title.png differ diff --git a/plugins/should-i-deploy/docs/should-i-deploy-card-without-title.png b/plugins/should-i-deploy/docs/should-i-deploy-card-without-title.png new file mode 100644 index 0000000000..d2e658c0bc Binary files /dev/null and b/plugins/should-i-deploy/docs/should-i-deploy-card-without-title.png differ diff --git a/plugins/should-i-deploy/package.json b/plugins/should-i-deploy/package.json new file mode 100644 index 0000000000..d6c9bd6d6e --- /dev/null +++ b/plugins/should-i-deploy/package.json @@ -0,0 +1,63 @@ +{ + "name": "@backstage/plugin-should-i-deploy", + "version": "0.1.0", + "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" + }, + "backstage": { + "role": "frontend-plugin" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/should-i-deploy" + }, + "sideEffects": false, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-home-react": "workspace:^", + "@backstage/theme": "workspace:^", + "@material-ui/core": "^4.9.13", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.57", + "@testing-library/jest-dom": "^5.10.1", + "cross-fetch": "^3.1.5", + "react-use": "^17.2.4" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0", + "react-router": "6.0.0-beta.0 || ^6.3.0", + "react-router-dom": "6.0.0-beta.0 || ^6.3.0" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", + "@testing-library/react": "12.1.3", + "@testing-library/user-event": "^14.0.0", + "@types/jest": "*", + "@types/node": "*", + "msw": "^0.44.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/should-i-deploy/src/@types/api/apiResponse.ts b/plugins/should-i-deploy/src/@types/api/apiResponse.ts new file mode 100644 index 0000000000..834087d9f4 --- /dev/null +++ b/plugins/should-i-deploy/src/@types/api/apiResponse.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 type ApiResponse = { + timezone: string; + date: string; + shouldideploy: boolean; + message: string; +}; diff --git a/plugins/should-i-deploy/src/api/ShouldIDeployCIApi.ts b/plugins/should-i-deploy/src/api/ShouldIDeployCIApi.ts new file mode 100644 index 0000000000..188f650479 --- /dev/null +++ b/plugins/should-i-deploy/src/api/ShouldIDeployCIApi.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { createApiRef } from '@backstage/core-plugin-api'; +import { ApiResponse } from '../@types/api/apiResponse'; + +export const ShouldIDeployCIApiRef = createApiRef({ + id: 'backstage.plugin.should.i.deploy', +}); + +export type ShouldIDeployCIApi = { + get(timeZone?: string): Promise; +}; diff --git a/plugins/should-i-deploy/src/api/ShouldIDeployCIClient.test.ts b/plugins/should-i-deploy/src/api/ShouldIDeployCIClient.test.ts new file mode 100644 index 0000000000..c7f052972e --- /dev/null +++ b/plugins/should-i-deploy/src/api/ShouldIDeployCIClient.test.ts @@ -0,0 +1,85 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { FetchApi } from '@backstage/core-plugin-api'; +import { ShouldIDeployCIClient } from './ShouldIDeployCIClient'; +import { ShouldIDeployCIApi } from './ShouldIDeployCIApi'; + +interface ExtendedFetchApi extends FetchApi { + fetch: jest.Mock>; +} + +describe('ShouldIDeployCIClient', () => { + let fetchApi: ExtendedFetchApi; + let client: ShouldIDeployCIApi; + + beforeEach(() => { + fetchApi = { + fetch: jest.fn(), + }; + client = new ShouldIDeployCIClient({ fetchApi }); + }); + + it('should create an instance of ShouldIDeployCIClient', () => { + expect(client).toBeInstanceOf(ShouldIDeployCIClient); + }); + + it('should call the API and return the response', async () => { + const mockResponse = { some: 'data' }; + fetchApi.fetch.mockResolvedValue({ + ok: true, + json: () => mockResponse, + }); + + const response = await client.get(); + + expect(fetchApi.fetch).toHaveBeenCalledWith( + 'https://shouldideploy.today/api', + ); + expect(response).toEqual(mockResponse); + }); + + it('should build the correct URL with timeZone', async () => { + const mockResponse = { some: 'data' }; + fetchApi.fetch.mockResolvedValue({ + ok: true, + json: () => mockResponse, + }); + + const timeZone = 'America/Sao_Paulo'; + + await client.get(timeZone); + + expect(fetchApi.fetch).toHaveBeenCalledWith( + `https://shouldideploy.today/api?tz=${timeZone}`, + ); + }); + + it('should handle API error and throw an error', async () => { + let error; + fetchApi.fetch.mockResolvedValue({ + ok: false, + }); + + try { + await client.get(); + } catch (err) { + error = err; + } + + expect(error).toBeInstanceOf(Error); + expect(error.message).toBe('Error to load response.'); + }); +}); diff --git a/plugins/should-i-deploy/src/api/ShouldIDeployCIClient.ts b/plugins/should-i-deploy/src/api/ShouldIDeployCIClient.ts new file mode 100644 index 0000000000..c87d8dd192 --- /dev/null +++ b/plugins/should-i-deploy/src/api/ShouldIDeployCIClient.ts @@ -0,0 +1,49 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { FetchApi } from '@backstage/core-plugin-api'; +import { ShouldIDeployCIApi } from './ShouldIDeployCIApi'; +import { ApiResponse } from '../@types/api/apiResponse'; + +export class ShouldIDeployCIClient implements ShouldIDeployCIApi { + fetchApi: FetchApi; + + constructor({ fetchApi }: { fetchApi: FetchApi }) { + this.fetchApi = fetchApi; + } + + protected async callApi(timeZone?: string): Promise { + const url = timeZone + ? `https://shouldideploy.today/api?tz=${timeZone}` + : 'https://shouldideploy.today/api'; + const response = await this.fetchApi.fetch(url); + + if (response.ok) { + return (await response.json()) as T; + } + + return Promise.reject(response); + } + + async get(timeZone?: string): Promise { + try { + const response = await this.callApi(timeZone); + + return response; + } catch (_error) { + throw new Error('Error to load response.'); + } + } +} diff --git a/plugins/should-i-deploy/src/api/index.ts b/plugins/should-i-deploy/src/api/index.ts new file mode 100644 index 0000000000..167860cbec --- /dev/null +++ b/plugins/should-i-deploy/src/api/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { ShouldIDeployCIApiRef } from './ShouldIDeployCIApi'; +export { ShouldIDeployCIClient } from './ShouldIDeployCIClient'; diff --git a/plugins/should-i-deploy/src/components/ShouldIDeployCard/CardStyles.ts b/plugins/should-i-deploy/src/components/ShouldIDeployCard/CardStyles.ts new file mode 100644 index 0000000000..257b88cc21 --- /dev/null +++ b/plugins/should-i-deploy/src/components/ShouldIDeployCard/CardStyles.ts @@ -0,0 +1,53 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { makeStyles } from '@material-ui/core'; + +export const cardStyles = makeStyles({ + card: { padding: '20px' }, + reason: { fontSize: '1.5em' }, + tagline: { + font: "800 0.75rem/0.75rem SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace", + marginBottom: '1em', + color: '#FFF', + textTransform: 'uppercase', + padding: '14px 0 13px 0', + }, + text: { + font: "800 1.5rem -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif", + color: '#111', + textTransform: 'uppercase', + hyphens: 'auto', + }, + reload: { + display: 'inline-block', + font: "350 0.75rem/0.75rem SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace", + background: 'transparent', + textDecoration: 'none', + color: 'gray', + marginTop: '1rem', + outline: 'none', + cursor: 'pointer', + width: '4rem', + padding: '0.25em 1em', + border: '1px solid', + borderRadius: '4px', + borderColor: 'gray', + }, + container: { + textAlign: 'center', + alignItems: 'center', + }, +}); diff --git a/plugins/should-i-deploy/src/components/ShouldIDeployCard/Content.test.tsx b/plugins/should-i-deploy/src/components/ShouldIDeployCard/Content.test.tsx new file mode 100644 index 0000000000..235dea660e --- /dev/null +++ b/plugins/should-i-deploy/src/components/ShouldIDeployCard/Content.test.tsx @@ -0,0 +1,79 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { Content } from './Content'; +import { ShouldIDeployCIApiRef } from '../../api'; +import { ShouldIDeployCIApi } from '../../api/ShouldIDeployCIApi'; +import { screen } from '@testing-library/react'; + +const mockSuccessApiRef = { + get: async () => + Promise.resolve({ + timezone: 'UTC', + date: '2023-10-30T11:10:26.000Z', + shouldideploy: true, + message: 'Make me proud', + }), +}; + +const mockErrorApiRef = { + get: async () => Promise.reject(new Error('Error to load infos')), +}; + +describe('ShouldIDeployCard', () => { + beforeEach(async () => { + await renderInTestApp( + + + , + ); + }); + + it('should render', async () => { + expect( + await screen.findByTestId('should-i-deploy-card'), + ).toBeInTheDocument(); + }); +}); + +describe('ShouldIDeployCard Error', () => { + beforeEach(async () => { + await renderInTestApp( + + + , + ); + }); + + it('should render error panel', async () => { + expect(screen.getByText('Error to load infos')).toBeInTheDocument(); + }); +}); diff --git a/plugins/should-i-deploy/src/components/ShouldIDeployCard/Content.tsx b/plugins/should-i-deploy/src/components/ShouldIDeployCard/Content.tsx new file mode 100644 index 0000000000..a25146a9c8 --- /dev/null +++ b/plugins/should-i-deploy/src/components/ShouldIDeployCard/Content.tsx @@ -0,0 +1,21 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { ShouldIDeployCard } from './ShouldIDeployCard'; + +export const Content = ({ timeZone }: { timeZone?: string }) => { + return ; +}; diff --git a/plugins/should-i-deploy/src/components/ShouldIDeployCard/ShouldIDeployCard.test.tsx b/plugins/should-i-deploy/src/components/ShouldIDeployCard/ShouldIDeployCard.test.tsx new file mode 100644 index 0000000000..9fd7785bee --- /dev/null +++ b/plugins/should-i-deploy/src/components/ShouldIDeployCard/ShouldIDeployCard.test.tsx @@ -0,0 +1,62 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { ShouldIDeployCard } from './ShouldIDeployCard'; +import { ShouldIDeployCIApiRef } from '../../api'; +import { ShouldIDeployCIApi } from '../../api/ShouldIDeployCIApi'; +import { screen, fireEvent } from '@testing-library/react'; + +const mockSuccessApiRef = { + get: jest.fn(() => + Promise.resolve({ + message: "It's a good day to deploy!", + date: new Date().toISOString(), + }), + ), +}; + +describe('ShouldIDeployCard', () => { + beforeEach(async () => { + await renderInTestApp( + + + , + ); + }); + + it('should render the card with data', async () => { + await screen.findByText("It's a good day to deploy!"); + expect(screen.getByText('Should I Deploy Today?')).toBeInTheDocument(); + expect(screen.getByText("It's a good day to deploy!")).toBeInTheDocument(); + expect(screen.getByText('Click')).toBeInTheDocument(); + }); + + it('should reload data on click', async () => { + await screen.findByText("It's a good day to deploy!"); + fireEvent.click(screen.getByText('Click')); + expect( + await screen.findByText("It's a good day to deploy!"), + ).toBeInTheDocument(); + }); +}); diff --git a/plugins/should-i-deploy/src/components/ShouldIDeployCard/ShouldIDeployCard.tsx b/plugins/should-i-deploy/src/components/ShouldIDeployCard/ShouldIDeployCard.tsx new file mode 100644 index 0000000000..9f46ea761f --- /dev/null +++ b/plugins/should-i-deploy/src/components/ShouldIDeployCard/ShouldIDeployCard.tsx @@ -0,0 +1,76 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { Progress, ResponseErrorPanel } from '@backstage/core-components'; +import { useApi } from '@backstage/core-plugin-api'; +import useAsync from 'react-use/lib/useAsync'; +import { ShouldIDeployCIApiRef } from '../../api'; +import { getBackegroundColor } from '../../helpers/getBackgroundColor'; +import { cardStyles } from './CardStyles'; +import { Typography } from '@material-ui/core'; + +export const ShouldIDeployCard = ({ timeZone }: { timeZone?: string }) => { + const [reason, setReasons] = React.useState(); + const [backgroundColor, setBackgroundColor] = React.useState(); + const classes = cardStyles(); + + const ShouldIDeployCIApi = useApi(ShouldIDeployCIApiRef); + const { loading, error } = useAsync(async () => { + const res = await ShouldIDeployCIApi.get(timeZone); + const day = new Date(res.date).getDay(); + setBackgroundColor(getBackegroundColor(day)); + setReasons(res?.message); + + return res; + }, []); + + const onClick = async (event: { type: string }) => { + if (event.type === 'click') { + const response = await ShouldIDeployCIApi.get(timeZone); + setReasons(response.message); + } + }; + + if (loading) { + return ; + } + + if (error) { + return ; + } + + return ( +
+
+

Should I Deploy Today?

+

+ {reason} +

+ + Click + +
+
+ ); +}; diff --git a/plugins/should-i-deploy/src/components/ShouldIDeployCard/index.ts b/plugins/should-i-deploy/src/components/ShouldIDeployCard/index.ts new file mode 100644 index 0000000000..183cfbf637 --- /dev/null +++ b/plugins/should-i-deploy/src/components/ShouldIDeployCard/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { ShouldIDeployCard } from './ShouldIDeployCard'; +export { Content } from './Content'; diff --git a/plugins/should-i-deploy/src/helpers/getBackgroundColor.test.ts b/plugins/should-i-deploy/src/helpers/getBackgroundColor.test.ts new file mode 100644 index 0000000000..d42161eb94 --- /dev/null +++ b/plugins/should-i-deploy/src/helpers/getBackgroundColor.test.ts @@ -0,0 +1,58 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { getBackegroundColor } from './getBackgroundColor'; + +describe('getBackegroundColor', () => { + it('should return green for Monday (day 1)', () => { + const result = getBackegroundColor(1); + expect(result).toBe('MediumSpringGreen'); + }); + + it('should return green for Tuesday (day 2)', () => { + const result = getBackegroundColor(2); + expect(result).toBe('MediumSpringGreen'); + }); + + it('should return green for Wednesday (day 3)', () => { + const result = getBackegroundColor(3); + expect(result).toBe('MediumSpringGreen'); + }); + + it('should return orange for Thursday (day 4)', () => { + const result = getBackegroundColor(4); + expect(result).toBe('LightSalmon'); + }); + + it('should return red for Friday (day 5)', () => { + const result = getBackegroundColor(5); + expect(result).toBe('Red'); + }); + + it('should return red for Saturday (day 6)', () => { + const result = getBackegroundColor(6); + expect(result).toBe('Red'); + }); + + it('should return red for Sunday (day 0)', () => { + const result = getBackegroundColor(0); + expect(result).toBe('Red'); + }); + + it('should return white for an invalid day (e.g., 7)', () => { + const result = getBackegroundColor(7); + expect(result).toBe('White'); + }); +}); diff --git a/plugins/should-i-deploy/src/helpers/getBackgroundColor.ts b/plugins/should-i-deploy/src/helpers/getBackgroundColor.ts new file mode 100644 index 0000000000..b3d42456a3 --- /dev/null +++ b/plugins/should-i-deploy/src/helpers/getBackgroundColor.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 const getBackegroundColor = (day: number) => { + const red = 'Red'; + const green = 'MediumSpringGreen'; + const orange = 'LightSalmon'; + const white = 'White'; + switch (day) { + case 1: + return green; + case 2: + return green; + case 3: + return green; + case 4: + return orange; + case 5: + return red; + case 6: + return red; + case 0: + return red; + default: + return white; + } +}; diff --git a/plugins/should-i-deploy/src/helpers/timezones.ts b/plugins/should-i-deploy/src/helpers/timezones.ts new file mode 100644 index 0000000000..3191d113e0 --- /dev/null +++ b/plugins/should-i-deploy/src/helpers/timezones.ts @@ -0,0 +1,609 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 const timeZones = [ + 'Africa/Abidjan', + 'Africa/Accra', + 'Africa/Addis_Ababa', + 'Africa/Algiers', + 'Africa/Asmara', + 'Africa/Asmera', + 'Africa/Bamako', + 'Africa/Bangui', + 'Africa/Banjul', + 'Africa/Bissau', + 'Africa/Blantyre', + 'Africa/Brazzaville', + 'Africa/Bujumbura', + 'Africa/Cairo', + 'Africa/Casablanca', + 'Africa/Ceuta', + 'Africa/Conakry', + 'Africa/Dakar', + 'Africa/Dar_es_Salaam', + 'Africa/Djibouti', + 'Africa/Douala', + 'Africa/El_Aaiun', + 'Africa/Freetown', + 'Africa/Gaborone', + 'Africa/Harare', + 'Africa/Johannesburg', + 'Africa/Juba', + 'Africa/Kampala', + 'Africa/Khartoum', + 'Africa/Kigali', + 'Africa/Kinshasa', + 'Africa/Lagos', + 'Africa/Libreville', + 'Africa/Lome', + 'Africa/Luanda', + 'Africa/Lubumbashi', + 'Africa/Lusaka', + 'Africa/Malabo', + 'Africa/Maputo', + 'Africa/Maseru', + 'Africa/Mbabane', + 'Africa/Mogadishu', + 'Africa/Monrovia', + 'Africa/Nairobi', + 'Africa/Ndjamena', + 'Africa/Niamey', + 'Africa/Nouakchott', + 'Africa/Ouagadougou', + 'Africa/Porto-Novo', + 'Africa/Sao_Tome', + 'Africa/Timbuktu', + 'Africa/Tripoli', + 'Africa/Tunis', + 'Africa/Windhoek', + 'America/Adak', + 'America/Anchorage', + 'America/Anguilla', + 'America/Antigua', + 'America/Araguaina', + 'America/Argentina/Buenos_Aires', + 'America/Argentina/Catamarca', + 'America/Argentina/ComodRivadavia', + 'America/Argentina/Cordoba', + 'America/Argentina/Jujuy', + 'America/Argentina/La_Rioja', + 'America/Argentina/Mendoza', + 'America/Argentina/Rio_Gallegos', + 'America/Argentina/Salta', + 'America/Argentina/San_Juan', + 'America/Argentina/San_Luis', + 'America/Argentina/Tucuman', + 'America/Argentina/Ushuaia', + 'America/Aruba', + 'America/Asuncion', + 'America/Atikokan', + 'America/Atka', + 'America/Bahia', + 'America/Bahia_Banderas', + 'America/Barbados', + 'America/Belem', + 'America/Belize', + 'America/Blanc-Sablon', + 'America/Boa_Vista', + 'America/Bogota', + 'America/Boise', + 'America/Buenos_Aires', + 'America/Cambridge_Bay', + 'America/Campo_Grande', + 'America/Cancun', + 'America/Caracas', + 'America/Catamarca', + 'America/Cayenne', + 'America/Cayman', + 'America/Chicago', + 'America/Chihuahua', + 'America/Coral_Harbour', + 'America/Cordoba', + 'America/Costa_Rica', + 'America/Creston', + 'America/Cuiaba', + 'America/Curacao', + 'America/Danmarkshavn', + 'America/Dawson', + 'America/Dawson_Creek', + 'America/Denver', + 'America/Detroit', + 'America/Dominica', + 'America/Edmonton', + 'America/Eirunepe', + 'America/El_Salvador', + 'America/Ensenada', + 'America/Fort_Nelson', + 'America/Fort_Wayne', + 'America/Fortaleza', + 'America/Glace_Bay', + 'America/Godthab', + 'America/Goose_Bay', + 'America/Grand_Turk', + 'America/Grenada', + 'America/Guadeloupe', + 'America/Guatemala', + 'America/Guayaquil', + 'America/Guyana', + 'America/Halifax', + 'America/Havana', + 'America/Hermosillo', + 'America/Indiana/Indianapolis', + 'America/Indiana/Knox', + 'America/Indiana/Marengo', + 'America/Indiana/Petersburg', + 'America/Indiana/Tell_City', + 'America/Indiana/Vevay', + 'America/Indiana/Vincennes', + 'America/Indiana/Winamac', + 'America/Indianapolis', + 'America/Inuvik', + 'America/Iqaluit', + 'America/Jamaica', + 'America/Jujuy', + 'America/Juneau', + 'America/Kentucky/Louisville', + 'America/Kentucky/Monticello', + 'America/Knox_IN', + 'America/Kralendijk', + 'America/La_Paz', + 'America/Lima', + 'America/Los_Angeles', + 'America/Louisville', + 'America/Lower_Princes', + 'America/Maceio', + 'America/Managua', + 'America/Manaus', + 'America/Marigot', + 'America/Martinique', + 'America/Matamoros', + 'America/Mazatlan', + 'America/Mendoza', + 'America/Menominee', + 'America/Merida', + 'America/Metlakatla', + 'America/Mexico_City', + 'America/Miquelon', + 'America/Moncton', + 'America/Monterrey', + 'America/Montevideo', + 'America/Montreal', + 'America/Montserrat', + 'America/Nassau', + 'America/New_York', + 'America/Nipigon', + 'America/Nome', + 'America/Noronha', + 'America/North_Dakota/Beulah', + 'America/North_Dakota/Center', + 'America/North_Dakota/New_Salem', + 'America/Ojinaga', + 'America/Panama', + 'America/Pangnirtung', + 'America/Paramaribo', + 'America/Phoenix', + 'America/Port-au-Prince', + 'America/Port_of_Spain', + 'America/Porto_Acre', + 'America/Porto_Velho', + 'America/Puerto_Rico', + 'America/Punta_Arenas', + 'America/Rainy_River', + 'America/Rankin_Inlet', + 'America/Recife', + 'America/Regina', + 'America/Resolute', + 'America/Rio_Branco', + 'America/Rosario', + 'America/Santa_Isabel', + 'America/Santarem', + 'America/Santiago', + 'America/Santo_Domingo', + 'America/Sao_Paulo', + 'America/Scoresbysund', + 'America/Shiprock', + 'America/Sitka', + 'America/St_Barthelemy', + 'America/St_Johns', + 'America/St_Kitts', + 'America/St_Lucia', + 'America/St_Thomas', + 'America/St_Vincent', + 'America/Swift_Current', + 'America/Tegucigalpa', + 'America/Thule', + 'America/Thunder_Bay', + 'America/Tijuana', + 'America/Toronto', + 'America/Tortola', + 'America/Vancouver', + 'America/Virgin', + 'America/Whitehorse', + 'America/Winnipeg', + 'America/Yakutat', + 'America/Yellowknife', + 'Antarctica/Casey', + 'Antarctica/Davis', + 'Antarctica/DumontDUrville', + 'Antarctica/Macquarie', + 'Antarctica/Mawson', + 'Antarctica/McMurdo', + 'Antarctica/Palmer', + 'Antarctica/Rothera', + 'Antarctica/South_Pole', + 'Antarctica/Syowa', + 'Antarctica/Troll', + 'Antarctica/Vostok', + 'Arctic/Longyearbyen', + 'Asia/Aden', + 'Asia/Almaty', + 'Asia/Amman', + 'Asia/Anadyr', + 'Asia/Aqtau', + 'Asia/Aqtobe', + 'Asia/Ashgabat', + 'Asia/Ashkhabad', + 'Asia/Atyrau', + 'Asia/Baghdad', + 'Asia/Bahrain', + 'Asia/Baku', + 'Asia/Bangkok', + 'Asia/Barnaul', + 'Asia/Beirut', + 'Asia/Bishkek', + 'Asia/Brunei', + 'Asia/Calcutta', + 'Asia/Chita', + 'Asia/Choibalsan', + 'Asia/Chongqing', + 'Asia/Chungking', + 'Asia/Colombo', + 'Asia/Dacca', + 'Asia/Damascus', + 'Asia/Dhaka', + 'Asia/Dili', + 'Asia/Dubai', + 'Asia/Dushanbe', + 'Asia/Famagusta', + 'Asia/Gaza', + 'Asia/Harbin', + 'Asia/Hebron', + 'Asia/Ho_Chi_Minh', + 'Asia/Hong_Kong', + 'Asia/Hovd', + 'Asia/Irkutsk', + 'Asia/Istanbul', + 'Asia/Jakarta', + 'Asia/Jayapura', + 'Asia/Jerusalem', + 'Asia/Kabul', + 'Asia/Kamchatka', + 'Asia/Karachi', + 'Asia/Kashgar', + 'Asia/Kathmandu', + 'Asia/Katmandu', + 'Asia/Khandyga', + 'Asia/Kolkata', + 'Asia/Krasnoyarsk', + 'Asia/Kuala_Lumpur', + 'Asia/Kuching', + 'Asia/Kuwait', + 'Asia/Macao', + 'Asia/Macau', + 'Asia/Magadan', + 'Asia/Makassar', + 'Asia/Manila', + 'Asia/Muscat', + 'Asia/Nicosia', + 'Asia/Novokuznetsk', + 'Asia/Novosibirsk', + 'Asia/Omsk', + 'Asia/Oral', + 'Asia/Phnom_Penh', + 'Asia/Pontianak', + 'Asia/Pyongyang', + 'Asia/Qatar', + 'Asia/Qyzylorda', + 'Asia/Rangoon', + 'Asia/Riyadh', + 'Asia/Saigon', + 'Asia/Sakhalin', + 'Asia/Samarkand', + 'Asia/Seoul', + 'Asia/Shanghai', + 'Asia/Singapore', + 'Asia/Srednekolymsk', + 'Asia/Taipei', + 'Asia/Tashkent', + 'Asia/Tbilisi', + 'Asia/Tehran', + 'Asia/Tel_Aviv', + 'Asia/Thimbu', + 'Asia/Thimphu', + 'Asia/Tokyo', + 'Asia/Tomsk', + 'Asia/Ujung_Pandang', + 'Asia/Ulaanbaatar', + 'Asia/Ulan_Bator', + 'Asia/Urumqi', + 'Asia/Ust-Nera', + 'Asia/Vientiane', + 'Asia/Vladivostok', + 'Asia/Yakutsk', + 'Asia/Yangon', + 'Asia/Yekaterinburg', + 'Asia/Yerevan', + 'Atlantic/Azores', + 'Atlantic/Bermuda', + 'Atlantic/Canary', + 'Atlantic/Cape_Verde', + 'Atlantic/Faeroe', + 'Atlantic/Faroe', + 'Atlantic/Jan_Mayen', + 'Atlantic/Madeira', + 'Atlantic/Reykjavik', + 'Atlantic/South_Georgia', + 'Atlantic/St_Helena', + 'Atlantic/Stanley', + 'Australia/ACT', + 'Australia/Adelaide', + 'Australia/Brisbane', + 'Australia/Broken_Hill', + 'Australia/Canberra', + 'Australia/Currie', + 'Australia/Darwin', + 'Australia/Eucla', + 'Australia/Hobart', + 'Australia/LHI', + 'Australia/Lindeman', + 'Australia/Lord_Howe', + 'Australia/Melbourne', + 'Australia/NSW', + 'Australia/North', + 'Australia/Perth', + 'Australia/Queensland', + 'Australia/South', + 'Australia/Sydney', + 'Australia/Tasmania', + 'Australia/Victoria', + 'Australia/West', + 'Australia/Yancowinna', + 'Brazil/Acre', + 'Brazil/DeNoronha', + 'Brazil/East', + 'Brazil/West', + 'CET', + 'CST6CDT', + 'Canada/Atlantic', + 'Canada/Central', + 'Canada/Eastern', + 'Canada/Mountain', + 'Canada/Newfoundland', + 'Canada/Pacific', + 'Canada/Saskatchewan', + 'Canada/Yukon', + 'Chile/Continental', + 'Chile/EasterIsland', + 'Cuba', + 'EET', + 'EST', + 'EST5EDT', + 'Egypt', + 'Eire', + 'Etc/GMT', + 'Etc/GMT+0', + 'Etc/GMT+1', + 'Etc/GMT+10', + 'Etc/GMT+11', + 'Etc/GMT+12', + 'Etc/GMT+2', + 'Etc/GMT+3', + 'Etc/GMT+4', + 'Etc/GMT+5', + 'Etc/GMT+6', + 'Etc/GMT+7', + 'Etc/GMT+8', + 'Etc/GMT+9', + 'Etc/GMT-0', + 'Etc/GMT-1', + 'Etc/GMT-10', + 'Etc/GMT-11', + 'Etc/GMT-12', + 'Etc/GMT-13', + 'Etc/GMT-14', + 'Etc/GMT-2', + 'Etc/GMT-3', + 'Etc/GMT-4', + 'Etc/GMT-5', + 'Etc/GMT-6', + 'Etc/GMT-7', + 'Etc/GMT-8', + 'Etc/GMT-9', + 'Etc/GMT0', + 'Etc/Greenwich', + 'Etc/UCT', + 'Etc/UTC', + 'Etc/Universal', + 'Etc/Zulu', + 'Europe/Amsterdam', + 'Europe/Andorra', + 'Europe/Astrakhan', + 'Europe/Athens', + 'Europe/Belfast', + 'Europe/Belgrade', + 'Europe/Berlin', + 'Europe/Bratislava', + 'Europe/Brussels', + 'Europe/Bucharest', + 'Europe/Budapest', + 'Europe/Busingen', + 'Europe/Chisinau', + 'Europe/Copenhagen', + 'Europe/Dublin', + 'Europe/Gibraltar', + 'Europe/Guernsey', + 'Europe/Helsinki', + 'Europe/Isle_of_Man', + 'Europe/Istanbul', + 'Europe/Jersey', + 'Europe/Kaliningrad', + 'Europe/Kiev', + 'Europe/Kirov', + 'Europe/Lisbon', + 'Europe/Ljubljana', + 'Europe/London', + 'Europe/Luxembourg', + 'Europe/Madrid', + 'Europe/Malta', + 'Europe/Mariehamn', + 'Europe/Minsk', + 'Europe/Monaco', + 'Europe/Moscow', + 'Europe/Nicosia', + 'Europe/Oslo', + 'Europe/Paris', + 'Europe/Podgorica', + 'Europe/Prague', + 'Europe/Riga', + 'Europe/Rome', + 'Europe/Samara', + 'Europe/San_Marino', + 'Europe/Sarajevo', + 'Europe/Saratov', + 'Europe/Simferopol', + 'Europe/Skopje', + 'Europe/Sofia', + 'Europe/Stockholm', + 'Europe/Tallinn', + 'Europe/Tirane', + 'Europe/Tiraspol', + 'Europe/Ulyanovsk', + 'Europe/Uzhgorod', + 'Europe/Vaduz', + 'Europe/Vatican', + 'Europe/Vienna', + 'Europe/Vilnius', + 'Europe/Volgograd', + 'Europe/Warsaw', + 'Europe/Zagreb', + 'Europe/Zaporozhye', + 'Europe/Zurich', + 'GB', + 'GB-Eire', + 'GMT', + 'GMT+0', + 'GMT-0', + 'GMT0', + 'Greenwich', + 'HST', + 'Hongkong', + 'Iceland', + 'Indian/Antananarivo', + 'Indian/Chagos', + 'Indian/Christmas', + 'Indian/Cocos', + 'Indian/Comoro', + 'Indian/Kerguelen', + 'Indian/Mahe', + 'Indian/Maldives', + 'Indian/Mauritius', + 'Indian/Mayotte', + 'Indian/Reunion', + 'Iran', + 'Israel', + 'Jamaica', + 'Japan', + 'Kwajalein', + 'Libya', + 'MET', + 'MST', + 'MST7MDT', + 'Mexico/BajaNorte', + 'Mexico/BajaSur', + 'Mexico/General', + 'NZ', + 'NZ-CHAT', + 'Navajo', + 'PRC', + 'PST8PDT', + 'Pacific/Apia', + 'Pacific/Auckland', + 'Pacific/Bougainville', + 'Pacific/Chatham', + 'Pacific/Chuuk', + 'Pacific/Easter', + 'Pacific/Efate', + 'Pacific/Enderbury', + 'Pacific/Fakaofo', + 'Pacific/Fiji', + 'Pacific/Funafuti', + 'Pacific/Galapagos', + 'Pacific/Gambier', + 'Pacific/Guadalcanal', + 'Pacific/Guam', + 'Pacific/Honolulu', + 'Pacific/Johnston', + 'Pacific/Kiritimati', + 'Pacific/Kosrae', + 'Pacific/Kwajalein', + 'Pacific/Majuro', + 'Pacific/Marquesas', + 'Pacific/Midway', + 'Pacific/Nauru', + 'Pacific/Niue', + 'Pacific/Norfolk', + 'Pacific/Noumea', + 'Pacific/Pago_Pago', + 'Pacific/Palau', + 'Pacific/Pitcairn', + 'Pacific/Pohnpei', + 'Pacific/Ponape', + 'Pacific/Port_Moresby', + 'Pacific/Rarotonga', + 'Pacific/Saipan', + 'Pacific/Samoa', + 'Pacific/Tahiti', + 'Pacific/Tarawa', + 'Pacific/Tongatapu', + 'Pacific/Truk', + 'Pacific/Wake', + 'Pacific/Wallis', + 'Pacific/Yap', + 'Poland', + 'Portugal', + 'ROC', + 'ROK', + 'Singapore', + 'Turkey', + 'UCT', + 'US/Alaska', + 'US/Aleutian', + 'US/Arizona', + 'US/Central', + 'US/East-Indiana', + 'US/Eastern', + 'US/Hawaii', + 'US/Indiana-Starke', + 'US/Michigan', + 'US/Mountain', + 'US/Pacific', + 'US/Pacific-New', + 'US/Samoa', + 'UTC', + 'Universal', + 'W-SU', + 'WET', + 'Zulu', +]; diff --git a/plugins/should-i-deploy/src/index.ts b/plugins/should-i-deploy/src/index.ts new file mode 100644 index 0000000000..ad6f751f61 --- /dev/null +++ b/plugins/should-i-deploy/src/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { + shouldIDeployPlugin, + ShouldIDeployCard, + ShouldIDeployPage, +} from './plugin'; diff --git a/plugins/should-i-deploy/src/plugin.test.tsx b/plugins/should-i-deploy/src/plugin.test.tsx new file mode 100644 index 0000000000..e57d456207 --- /dev/null +++ b/plugins/should-i-deploy/src/plugin.test.tsx @@ -0,0 +1,107 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { Routes, Route } from 'react-router'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { + ShouldIDeployCard, + ShouldIDeployPage, + shouldIDeployPlugin, +} from './plugin'; +import { + ShouldIDeployCIApi, + ShouldIDeployCIApiRef, +} from './api/ShouldIDeployCIApi'; +import { screen } from '@testing-library/react'; + +const fetchPageApi = { + fetch: jest.fn().mockResolvedValue({ + ok: true, + json: () => + Promise.resolve({ + message: "It's a good day to deploy!", + date: new Date().toISOString(), + }), + }), +}; + +const mockSuccessApiRef = { + get: async () => + Promise.resolve({ + message: "It's a good day to deploy!", + date: new Date().toISOString(), + }), +}; + +describe('Plugin base', () => { + it('should export plugin', () => { + expect(shouldIDeployPlugin).toBeDefined(); + }); + + it('should create plugin api', () => { + const apiArray = Array.from(shouldIDeployPlugin.getApis()); + const api = apiArray[0]?.factory({ + fetchApi: fetchPageApi, + }) as ShouldIDeployCIApi[]; + expect(api).toBeDefined(); + }); +}); + +describe('Plugin page', () => { + beforeEach(async () => { + await renderInTestApp( + + + } /> + + , + ); + }, 35000); + + it('should export plugin page', () => { + expect(ShouldIDeployPage).toBeDefined(); + const { getByText } = screen; + const element = getByText(/Should i deploy today/i); + expect(element).toBeInTheDocument(); + }); +}); + +describe('Plugin card', () => { + beforeEach(async () => { + await renderInTestApp( + + + } /> + + , + ); + }, 35000); + + it('should export plugin card', async () => { + expect(ShouldIDeployCard).toBeDefined(); + expect( + await screen.findByTestId('should-i-deploy-card'), + ).toBeInTheDocument(); + }); +}); diff --git a/plugins/should-i-deploy/src/plugin.ts b/plugins/should-i-deploy/src/plugin.ts new file mode 100644 index 0000000000..41f0abf595 --- /dev/null +++ b/plugins/should-i-deploy/src/plugin.ts @@ -0,0 +1,77 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { + createApiFactory, + createPlugin, + createRoutableExtension, + fetchApiRef, +} from '@backstage/core-plugin-api'; +import { createCardExtension } from '@backstage/plugin-home-react'; +import { rootRouteRef } from './routes'; +import { ShouldIDeployCIClient, ShouldIDeployCIApiRef } from './api'; +import { timeZones } from './helpers/timezones'; + +export const shouldIDeployPlugin = createPlugin({ + id: '@backstage/plugin-should-i-deploy', + routes: { + root: rootRouteRef, + }, + apis: [ + createApiFactory({ + api: ShouldIDeployCIApiRef, + deps: { + fetchApi: fetchApiRef, + }, + factory: ({ fetchApi }) => + new ShouldIDeployCIClient({ + fetchApi, + }), + }), + ], +}); + +export const ShouldIDeployPage = shouldIDeployPlugin.provide( + createRoutableExtension({ + name: 'ShouldIDeployPage', + component: () => + import('./components/ShouldIDeployCard').then(m => m.Content), + mountPoint: rootRouteRef, + }), +); + +export const ShouldIDeployCard = shouldIDeployPlugin.provide( + createCardExtension<{ timeZone?: string; title?: string }>({ + name: 'ShouldIDeployCard', + components: () => import('./components/ShouldIDeployCard'), + layout: { + height: { minRows: 4 }, + width: { minColumns: 3 }, + }, + settings: { + schema: { + title: 'Should I Deploy settings', + type: 'object', + properties: { + timeZone: { + title: 'config local timezone', + type: 'string', + enum: timeZones, + }, + }, + }, + }, + }), +); diff --git a/plugins/should-i-deploy/src/routes.ts b/plugins/should-i-deploy/src/routes.ts new file mode 100644 index 0000000000..4137af3c26 --- /dev/null +++ b/plugins/should-i-deploy/src/routes.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { createRouteRef } from '@backstage/core-plugin-api'; + +export const rootRouteRef = createRouteRef({ + id: 'should-i-deploy', +}); diff --git a/plugins/should-i-deploy/src/setupTests.ts b/plugins/should-i-deploy/src/setupTests.ts new file mode 100644 index 0000000000..865308e634 --- /dev/null +++ b/plugins/should-i-deploy/src/setupTests.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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'; diff --git a/yarn.lock b/yarn.lock index 0a8a00ef9f..c8e7629e86 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12,7 +12,7 @@ __metadata: languageName: node linkType: hard -"@adobe/css-tools@npm:^4.3.1": +"@adobe/css-tools@npm:^4.0.1, @adobe/css-tools@npm:^4.3.1": version: 4.3.1 resolution: "@adobe/css-tools@npm:4.3.1" checksum: ad43456379ff391132aff687ece190cb23ea69395e23c9b96690eeabe2468da89a4aaf266e4f8b6eaab53db3d1064107ce0f63c3a974e864f4a04affc768da3f @@ -9389,6 +9389,37 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-should-i-deploy@workspace:plugins/should-i-deploy": + version: 0.0.0-use.local + resolution: "@backstage/plugin-should-i-deploy@workspace:plugins/should-i-deploy" + dependencies: + "@backstage/cli": "workspace:^" + "@backstage/core-app-api": "workspace:^" + "@backstage/core-components": "workspace:^" + "@backstage/core-plugin-api": "workspace:^" + "@backstage/dev-utils": "workspace:^" + "@backstage/plugin-home-react": "workspace:^" + "@backstage/test-utils": "workspace:^" + "@backstage/theme": "workspace:^" + "@material-ui/core": ^4.9.13 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": 4.0.0-alpha.57 + "@testing-library/jest-dom": ^5.10.1 + "@testing-library/react": 12.1.3 + "@testing-library/user-event": ^14.0.0 + "@types/jest": "*" + "@types/node": "*" + cross-fetch: ^3.1.5 + msw: ^0.44.0 + react-use: ^17.2.4 + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router: 6.0.0-beta.0 || ^6.3.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + languageName: unknown + linkType: soft + "@backstage/plugin-sonarqube-backend@workspace:^, @backstage/plugin-sonarqube-backend@workspace:plugins/sonarqube-backend": version: 0.0.0-use.local resolution: "@backstage/plugin-sonarqube-backend@workspace:plugins/sonarqube-backend" @@ -13861,7 +13892,7 @@ __metadata: languageName: node linkType: hard -"@mswjs/interceptors@npm:^0.17.10": +"@mswjs/interceptors@npm:^0.17.10, @mswjs/interceptors@npm:^0.17.2": version: 0.17.10 resolution: "@mswjs/interceptors@npm:0.17.10" dependencies: @@ -17892,6 +17923,22 @@ __metadata: languageName: unknown linkType: soft +"@testing-library/dom@npm:^8.0.0": + version: 8.20.1 + resolution: "@testing-library/dom@npm:8.20.1" + dependencies: + "@babel/code-frame": ^7.10.4 + "@babel/runtime": ^7.12.5 + "@types/aria-query": ^5.0.1 + aria-query: 5.1.3 + chalk: ^4.1.0 + dom-accessibility-api: ^0.5.9 + lz-string: ^1.5.0 + pretty-format: ^27.0.2 + checksum: 06fc8dc67849aadb726cbbad0e7546afdf8923bd39acb64c576d706249bd7d0d05f08e08a31913fb621162e3b9c2bd0dce15964437f030f9fa4476326fdd3007 + languageName: node + linkType: hard + "@testing-library/dom@npm:^9.0.0": version: 9.3.3 resolution: "@testing-library/dom@npm:9.3.3" @@ -17908,6 +17955,23 @@ __metadata: languageName: node linkType: hard +"@testing-library/jest-dom@npm:^5.10.1": + version: 5.17.0 + resolution: "@testing-library/jest-dom@npm:5.17.0" + dependencies: + "@adobe/css-tools": ^4.0.1 + "@babel/runtime": ^7.9.2 + "@types/testing-library__jest-dom": ^5.9.1 + aria-query: ^5.0.0 + chalk: ^3.0.0 + css.escape: ^1.5.1 + dom-accessibility-api: ^0.5.6 + lodash: ^4.17.15 + redent: ^3.0.0 + checksum: 9f28dbca8b50d7c306aae40c3aa8e06f0e115f740360004bd87d57f95acf7ab4b4f4122a7399a76dbf2bdaaafb15c99cc137fdcb0ae457a92e2de0f3fbf9b03b + languageName: node + linkType: hard + "@testing-library/jest-dom@npm:^6.0.0": version: 6.1.4 resolution: "@testing-library/jest-dom@npm:6.1.4" @@ -17960,6 +18024,20 @@ __metadata: languageName: node linkType: hard +"@testing-library/react@npm:12.1.3": + version: 12.1.3 + resolution: "@testing-library/react@npm:12.1.3" + dependencies: + "@babel/runtime": ^7.12.5 + "@testing-library/dom": ^8.0.0 + "@types/react-dom": "*" + peerDependencies: + react: "*" + react-dom: "*" + checksum: 2f059e93dc3f161c4d15910d1fa80cd4f5cfb6eff8a733939a72ac720c2499b74761e8980c02983a6e49153b776e82bd770a1c3e66e05c4429db0e01612f7d0a + languageName: node + linkType: hard + "@testing-library/react@npm:^14.0.0": version: 14.0.0 resolution: "@testing-library/react@npm:14.0.0" @@ -22949,6 +23027,16 @@ __metadata: languageName: node linkType: hard +"chalk@npm:4.1.1": + version: 4.1.1 + resolution: "chalk@npm:4.1.1" + dependencies: + ansi-styles: ^4.1.0 + supports-color: ^7.1.0 + checksum: 036e973e665ba1a32c975e291d5f3d549bceeb7b1b983320d4598fb75d70fe20c5db5d62971ec0fe76cdbce83985a00ee42372416abfc3a5584465005a7855ed + languageName: node + linkType: hard + "chalk@npm:4.1.2, chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" @@ -29470,7 +29558,7 @@ __metadata: languageName: node linkType: hard -"graphql@npm:^16.0.0, graphql@npm:^16.8.1": +"graphql@npm:^16.0.0, graphql@npm:^16.3.0, graphql@npm:^16.8.1": version: 16.8.1 resolution: "graphql@npm:16.8.1" checksum: 8d304b7b6f708c8c5cc164b06e92467dfe36aff6d4f2cf31dd19c4c2905a0e7b89edac4b7e225871131fd24e21460836b369de0c06532644d15b461d55b1ccc0 @@ -29740,6 +29828,13 @@ __metadata: languageName: node linkType: hard +"headers-polyfill@npm:^3.0.4": + version: 3.3.0 + resolution: "headers-polyfill@npm:3.3.0" + checksum: 6dd010544b7c1a878aa612b49d9e27d273aa9e8a462e1ae05ca212fafa68e6b7cbdec4765f4e443b2fe70d2818f5d9814eab53fa2ba793bf1e5a6b6eff760474 + languageName: node + linkType: hard + "helmet@npm:^6.0.0": version: 6.0.1 resolution: "helmet@npm:6.0.1" @@ -30979,7 +31074,7 @@ __metadata: languageName: node linkType: hard -"is-node-process@npm:^1.2.0": +"is-node-process@npm:^1.0.1, is-node-process@npm:^1.2.0": version: 1.2.0 resolution: "is-node-process@npm:1.2.0" checksum: 930765cdc6d81ab8f1bbecbea4a8d35c7c6d88a3ff61f3630e0fc7f22d624d7661c1df05c58547d0eb6a639dfa9304682c8e342c4113a6ed51472b704cee2928 @@ -35598,6 +35693,41 @@ __metadata: languageName: node linkType: hard +"msw@npm:^0.44.0": + version: 0.44.2 + resolution: "msw@npm:0.44.2" + dependencies: + "@mswjs/cookies": ^0.2.2 + "@mswjs/interceptors": ^0.17.2 + "@open-draft/until": ^1.0.3 + "@types/cookie": ^0.4.1 + "@types/js-levenshtein": ^1.1.1 + chalk: 4.1.1 + chokidar: ^3.4.2 + cookie: ^0.4.2 + graphql: ^16.3.0 + headers-polyfill: ^3.0.4 + inquirer: ^8.2.0 + is-node-process: ^1.0.1 + js-levenshtein: ^1.1.6 + node-fetch: ^2.6.7 + outvariant: ^1.3.0 + path-to-regexp: ^6.2.0 + statuses: ^2.0.0 + strict-event-emitter: ^0.2.0 + type-fest: ^1.2.2 + yargs: ^17.3.1 + peerDependencies: + typescript: ">= 4.2.x <= 4.7.x" + peerDependenciesMeta: + typescript: + optional: true + bin: + msw: cli/index.js + checksum: 739d536ee09d1c0a2cbc9dbe917f167c42115a6548f99780850ce9a63a5e7c377cf9b5f1a3b497e85ce7304025ab22966e997fe73adbd8d2ab968b8685f15a24 + languageName: node + linkType: hard + "msw@npm:^1.0.0, msw@npm:^1.0.1, msw@npm:^1.2.1, msw@npm:^1.2.3, msw@npm:^1.3.0, msw@npm:^1.3.1": version: 1.3.2 resolution: "msw@npm:1.3.2" @@ -36850,7 +36980,7 @@ __metadata: languageName: node linkType: hard -"outvariant@npm:^1.2.1, outvariant@npm:^1.4.0": +"outvariant@npm:^1.2.1, outvariant@npm:^1.3.0, outvariant@npm:^1.4.0": version: 1.4.0 resolution: "outvariant@npm:1.4.0" checksum: ec32dfc315c464bb6e4906b2f450d259ce0b86caf70b70b249054359d9af21a7fccf53a8b6aa232f8d718449e31c1cfa594e6ebffaafe7bf908b502495256d7b @@ -42399,7 +42529,7 @@ __metadata: languageName: node linkType: hard -"statuses@npm:2.0.1": +"statuses@npm:2.0.1, statuses@npm:^2.0.0": version: 2.0.1 resolution: "statuses@npm:2.0.1" checksum: 18c7623fdb8f646fb213ca4051be4df7efb3484d4ab662937ca6fbef7ced9b9e12842709872eb3020cc3504b93bde88935c9f6417489627a7786f24f8031cbcb @@ -42520,7 +42650,7 @@ __metadata: languageName: node linkType: hard -"strict-event-emitter@npm:^0.2.4": +"strict-event-emitter@npm:^0.2.0, strict-event-emitter@npm:^0.2.4": version: 0.2.8 resolution: "strict-event-emitter@npm:0.2.8" dependencies: @@ -44054,7 +44184,7 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^1.0.2": +"type-fest@npm:^1.0.2, type-fest@npm:^1.2.2": version: 1.4.0 resolution: "type-fest@npm:1.4.0" checksum: b011c3388665b097ae6a109a437a04d6f61d81b7357f74cbcb02246f2f5bd72b888ae33631b99871388122ba0a87f4ff1c94078e7119ff22c70e52c0ff828201