Skip to content

Commit

Permalink
Merge pull request #133 from giranm/release/v0.1.0-beta.0
Browse files Browse the repository at this point in the history
[Release] v0.1.0 beta.0
  • Loading branch information
giranm authored Jun 13, 2022
2 parents 68991d6 + a788d4b commit a8c2f41
Show file tree
Hide file tree
Showing 46 changed files with 1,432 additions and 556 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,16 @@ To prepare PagerDuty Live for release, the current workflow should be carried ou
1. Checkout to `develop` branch and verify if it's stable - i.e. no test and linting failures.

2. Update version information in `package.json` using `npm version` - example commands given below:
- Bumping patch version for alpha release
- Bumping patch version for beta release
```
$ npm --no-git-tag-version version prepatch --preid alpha
v0.0.1-alpha.0
$ npm --no-git-tag-version version preminor --preid beta
v0.1.0-beta.0
```
- Bumping minor version for main release
```
$ npm --no-git-tag-version version minor
v0.1.0
v0.2.0
```
3. Update application code version using `$ yarn genversion`
Expand Down
42 changes: 42 additions & 0 deletions cypress/integration/Query/query.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
checkIncidentCellIconAllRows,
manageIncidentTableColumns,
priorityNames,
selectIncident,
} from '../../support/util/common';

registerLocale('en-GB', gb);
Expand Down Expand Up @@ -67,9 +68,45 @@ describe('Query Incidents', { failFast: { enabled: false } }, () => {

// Reset query for next test
activateButton('query-urgency-low-button');
});

it('Query for incidents exceeding MAX_INCIDENTS_LIMIT; Cancel Request', () => {
// Update since date to T-2
const queryDate = moment()
.subtract(2, 'days')
.set({ hour: 0, minute: 0, second: 0, millisecond: 0 });
cy.get('#query-date-input').clear().type(queryDate.format('DD/MM/yyyy')).type('{enter}');

// Cancel request from modal
cy.get('#cancel-incident-query-button').click();
cy.get('div.query-cancelled-ctr')
.should('be.visible')
.should('contain.text', 'Query has been cancelled by user');
cy.get('div.selected-incidents-ctr').should('be.visible').should('contain.text', 'N/A');

// Reset query for next test
deactivateButton('query-status-resolved-button');
});

it('Query for incidents exceeding MAX_INCIDENTS_LIMIT; Accept Request', () => {
// Accept request from modal
activateButton('query-status-resolved-button');
cy.get('#retrieve-incident-query-button').click();
cy.get('div.query-active-ctr')
.should('be.visible')
.should('contain.text', 'Querying PagerDuty API');
cy.get('div.selected-incidents-ctr').should('be.visible').should('contain.text', 'Querying');
waitForIncidentTable();

// Reset query for next test
deactivateButton('query-status-resolved-button');
const queryDate = moment()
.subtract(1, 'days')
.set({ hour: 0, minute: 0, second: 0, millisecond: 0 });
cy.get('#query-date-input').clear().type(queryDate.format('DD/MM/yyyy')).type('{enter}');
waitForIncidentTable();
});

it('Query for triggered incidents only', () => {
activateButton('query-status-triggered-button');
deactivateButton('query-status-acknowledged-button');
Expand All @@ -79,6 +116,11 @@ describe('Query Incidents', { failFast: { enabled: false } }, () => {
});

it('Query for acknowledged incidents only', () => {
// Ensure at least one incident is acknowledged for test
selectIncident(0);
cy.get('#incident-action-acknowledge-button').click();
cy.get('.action-alerts-modal').type('{esc}');

deactivateButton('query-status-triggered-button');
activateButton('query-status-acknowledged-button');
deactivateButton('query-status-resolved-button');
Expand Down
14 changes: 14 additions & 0 deletions cypress/integration/Search/search.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
waitForIncidentTable,
activateButton,
priorityNames,
selectIncident,
} from '../../support/util/common';

describe('Search Incidents', { failFast: { enabled: false } }, () => {
Expand Down Expand Up @@ -33,6 +34,19 @@ describe('Search Incidents', { failFast: { enabled: false } }, () => {
});
});

it('Search for 2nd selected incident returns exactly 1 incident only', () => {
const incidentIdx = 1;
selectIncident(incidentIdx);
cy.get(`@selectedIncidentId_${incidentIdx}`).then((incidentId) => {
cy.get('#global-search-input').clear().type(incidentId);
});
cy.wait(1000);
cy.get('.selected-incidents-badge').then(($el) => {
const text = $el.text();
expect(text).to.equal('1/1');
});
});

it('Search for `zzzzzz` returns no incidents', () => {
cy.get('#global-search-input').clear().type('zzzzzz');
cy.wait(5000);
Expand Down
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module.exports = {
testEnvironment: 'jsdom',
testPathIgnorePatterns: ['./cypress/'],
setupFiles: ['dotenv/config'],
setupFilesAfterEnv: ['./setupTests.js'],
moduleDirectories: ['node_modules', 'src'],
moduleNameMapper: {
Expand Down
14 changes: 8 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "pd-live-react",
"homepage": "https://giranm.github.io/pd-live-react",
"version": "0.0.17-alpha.0",
"version": "0.1.0-beta.0",
"private": true,
"dependencies": {
"@craco/craco": "7.0.0-alpha.3",
Expand All @@ -28,7 +28,7 @@
"immer": "^9.0.6",
"lodash": "^4.17.21",
"mezr": "^0.6.2",
"moment": "^2.29.2",
"moment": "^2.29.3",
"node-sass": "^6.0.1",
"react": "^17.0.2",
"react-bootstrap": "^1.6.4",
Expand All @@ -45,7 +45,7 @@
"redux-persist": "^6.0.0",
"redux-saga": "^1.1.3",
"styled-components": "^5.3.5",
"use-debounce": "^7.0.0",
"use-debounce": "^8.0.1",
"web-vitals": "^1.1.2"
},
"resolutions": {
Expand Down Expand Up @@ -90,6 +90,7 @@
"@babel/preset-react": "^7.16.7",
"@cypress/react": "5.12.4",
"@cypress/webpack-dev-server": "^1.8.0",
"@faker-js/faker": "^7.1.0",
"cy2": "^1.3.0",
"cypress": "^9.2.1",
"cypress-fail-fast": "^3.4.1",
Expand All @@ -98,16 +99,17 @@
"eslint-config-prettier": "^8.3.0",
"eslint-config-react-app": "^7.0.0",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-import": "^2.24.2",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.26.1",
"eslint-plugin-react-hooks": "^4.2.0",
"genversion": "^3.0.2",
"gh-pages": "^3.2.3",
"html-webpack-plugin": "4",
"html-webpack-plugin": "5",
"identity-obj-proxy": "^3.0.0",
"prettier": "^2.5.1",
"jest-location-mock": "^1.0.9",
"prettier": "^2.6.2",
"prettier-eslint": "^10.1.0",
"prettier-eslint-cli": "^5.0.1",
"redux-mock-store": "^1.5.4",
Expand Down
32 changes: 13 additions & 19 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from 'react-bootstrap';
import moment from 'moment';

import AuthComponent from 'components/Auth/AuthComponent';
import UnauthorizedModalComponent from 'components/UnauthorizedModal/UnauthorizedModalComponent';
import DisclaimerModalComponent from 'components/DisclaimerModal/DisclaimerModalComponent';
import NavigationBarComponent from 'components/NavigationBar/NavigationBarComponent';
Expand All @@ -22,11 +23,8 @@ import AddNoteModalComponent from 'components/AddNoteModal/AddNoteModalComponent
import ReassignModalComponent from 'components/ReassignModal/ReassignModalComponent';
import AddResponderModalComponent from 'components/AddResponderModal/AddResponderModalComponent';
import MergeModalComponent from 'components/MergeModal/MergeModalComponent';
import ConfirmQueryModalComponent from 'components/ConfirmQueryModal/ConfirmQueryModalComponent';

import {
getIncidentsAsync as getIncidentsAsyncConnected,
getAllIncidentNotesAsync as getAllIncidentNotesAsyncConnected,
} from 'redux/incidents/actions';
import {
getLogEntriesAsync as getLogEntriesAsyncConnected,
cleanRecentLogEntriesAsync as cleanRecentLogEntriesAsyncConnected,
Expand Down Expand Up @@ -64,12 +62,10 @@ import {
store,
} from 'redux/store';

import PDOAuth from 'util/pdoauth';

import {
PD_REQUIRED_ABILITY,
PD_OAUTH_CLIENT_ID,
PD_OAUTH_CLIENT_SECRET,
PD_REQUIRED_ABILITY,
LOG_ENTRIES_POLLING_INTERVAL_SECONDS,
LOG_ENTRIES_CLEARING_INTERVAL_SECONDS,
} from 'config/constants';
Expand All @@ -90,24 +86,24 @@ const App = ({
getEscalationPoliciesAsync,
getExtensionsAsync,
getResponsePlaysAsync,
getIncidentsAsync,
getAllIncidentNotesAsync,
getLogEntriesAsync,
cleanRecentLogEntriesAsync,
}) => {
// Verify if session token is present
const token = sessionStorage.getItem('pd_access_token');
if (!token) {
useEffect(() => {
PDOAuth.login(PD_OAUTH_CLIENT_ID, PD_OAUTH_CLIENT_SECRET);
}, []);
return null;
return (
<div className="App">
<AuthComponent clientId={PD_OAUTH_CLIENT_ID} clientSecret={PD_OAUTH_CLIENT_SECRET} />
</div>
);
}

// Begin monitoring and load core objects from API
const {
userAuthorized, userAcceptedDisclaimer, currentUserLocale,
} = state.users;
const queryError = state.querySettings.error;
useEffect(() => {
userAuthorize();
if (userAuthorized) {
Expand All @@ -120,8 +116,7 @@ const App = ({
getExtensionsAsync();
getResponsePlaysAsync();
getPrioritiesAsync();
getIncidentsAsync();
getAllIncidentNotesAsync();
// NB: Get Incidents and Notes are implicitly done from query now
checkConnectionStatus();
}
}, [userAuthorized]);
Expand All @@ -139,15 +134,15 @@ const App = ({
const {
abilities,
} = store.getState().connection;
if (userAuthorized && abilities.includes(PD_REQUIRED_ABILITY)) {
if (userAuthorized && abilities.includes(PD_REQUIRED_ABILITY) && !queryError) {
const lastPolledDate = moment()
.subtract(2 * LOG_ENTRIES_POLLING_INTERVAL_SECONDS, 'seconds')
.toDate();
getLogEntriesAsync(lastPolledDate);
}
}, LOG_ENTRIES_POLLING_INTERVAL_SECONDS * 1000);
return () => clearInterval(pollingInterval);
}, [userAuthorized]);
}, [userAuthorized, queryError]);

// Setup log entry clearing
useEffect(() => {
Expand Down Expand Up @@ -191,6 +186,7 @@ const App = ({
<ReassignModalComponent />
<AddResponderModalComponent />
<MergeModalComponent />
<ConfirmQueryModalComponent />
</Container>
</div>
);
Expand All @@ -210,8 +206,6 @@ const mapDispatchToProps = (dispatch) => ({
getEscalationPoliciesAsync: () => dispatch(getEscalationPoliciesAsyncConnected()),
getExtensionsAsync: () => dispatch(getExtensionsAsyncConnected()),
getResponsePlaysAsync: () => dispatch(getResponsePlaysAsyncConnected()),
getIncidentsAsync: () => dispatch(getIncidentsAsyncConnected()),
getAllIncidentNotesAsync: () => dispatch(getAllIncidentNotesAsyncConnected()),
getLogEntriesAsync: (since) => dispatch(getLogEntriesAsyncConnected(since)),
cleanRecentLogEntriesAsync: () => dispatch(cleanRecentLogEntriesAsyncConnected()),
});
Expand Down
87 changes: 87 additions & 0 deletions src/components/Auth/AuthComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/* eslint-disable no-unused-vars */
import React, {
useState, useEffect,
} from 'react';

import {
Form, Button, Dropdown, Spinner, Row,
} from 'react-bootstrap';

import {
createCodeVerifier, getAuthURL, exchangeCodeForToken,
} from 'util/auth';

import './AuthComponent.scss';

const AuthComponent = (props) => {
const [authURL, setAuthURL] = useState('');
const urlParams = new URLSearchParams(window.location.search);
const accessToken = sessionStorage.getItem('pd_access_token');
const code = urlParams.get('code');
let codeVerifier = sessionStorage.getItem('code_verifier');
let {
redirectURL,
} = props;
const {
clientId, clientSecret,
} = props;

if (!redirectURL) {
// assume that the redirect URL is the current page
redirectURL = `${window.location.protocol}//${window.location.host}${window.location.pathname}`;
}

useEffect(() => {
if (code && codeVerifier && !accessToken) {
exchangeCodeForToken(clientId, clientSecret, redirectURL, codeVerifier, code).then(
(token) => {
sessionStorage.removeItem('code_verifier');
sessionStorage.setItem('pd_access_token', token);
window.location.assign(redirectURL);
},
);
} else if (!accessToken) {
codeVerifier = createCodeVerifier();
sessionStorage.setItem('code_verifier', codeVerifier);
getAuthURL(clientId, clientSecret, redirectURL, codeVerifier).then((url) => {
setAuthURL(url);
});
}
}, []);

if (code && codeVerifier) {
return (
<div align="center">
<br />
<Row className="justify-content-md-center">
<Spinner animation="border" role="status" variant="success" />
<h5 className="querying-incidents">
<b>Signing into PagerDuty Live</b>
</h5>
</Row>
</div>
);
}
return (
<div align="center">
<Form id="pd-login-form">
<div id="pd-login-logo" />
<Dropdown.Divider />
<div id="pd-login-description">
<h1>Live Incidents Console</h1>
<p>Connect using PagerDuty OAuth to use this app</p>
</div>
<Button
id="pd-login-button"
variant="primary"
size="lg"
onClick={() => window.location.assign(authURL)}
>
Sign In
</Button>
</Form>
</div>
);
};

export default AuthComponent;
Loading

0 comments on commit a8c2f41

Please sign in to comment.