Use unique keys to identify healthchecks
Summary: Added unique keys for each healthcheck and used them to save already seen failures. Later I will also use them for gathering doctor analytics. Reviewed By: jknoxville Differential Revision: D19301583 fbshipit-source-id: 0c0aa977ea73ce555e0d9fb8e8ead844624fb9cd
This commit is contained in:
committed by
Facebook Github Bot
parent
cdb0990ac8
commit
751c778069
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "flipper-doctor",
|
"name": "flipper-doctor",
|
||||||
"version": "0.5.0",
|
"version": "0.6.0",
|
||||||
"description": "Utility for checking for issues with a flipper installation",
|
"description": "Utility for checking for issues with a flipper installation",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "lib/index.d.ts",
|
"types": "lib/index.d.ts",
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ import {getEnvInfo} from './environmentInfo';
|
|||||||
: {
|
: {
|
||||||
label: category.label,
|
label: category.label,
|
||||||
results: await Promise.all(
|
results: await Promise.all(
|
||||||
category.healthchecks.map(async ({label, run}) => ({
|
category.healthchecks.map(async ({key, label, run}) => ({
|
||||||
|
key,
|
||||||
label,
|
label,
|
||||||
result: await run(environmentInfo),
|
result: await run(environmentInfo),
|
||||||
})),
|
})),
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export type Healthchecks = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type Healthcheck = {
|
export type Healthcheck = {
|
||||||
|
key: string;
|
||||||
label: string;
|
label: string;
|
||||||
isRequired?: boolean;
|
isRequired?: boolean;
|
||||||
run: (
|
run: (
|
||||||
@@ -48,6 +49,7 @@ export type CategoryResult = [
|
|||||||
{
|
{
|
||||||
label: string;
|
label: string;
|
||||||
results: Array<{
|
results: Array<{
|
||||||
|
key: string;
|
||||||
label: string;
|
label: string;
|
||||||
isRequired: boolean;
|
isRequired: boolean;
|
||||||
result: {hasProblem: boolean};
|
result: {hasProblem: boolean};
|
||||||
@@ -63,6 +65,7 @@ export function getHealthchecks(): Healthchecks {
|
|||||||
isSkipped: false,
|
isSkipped: false,
|
||||||
healthchecks: [
|
healthchecks: [
|
||||||
{
|
{
|
||||||
|
key: 'common.openssl',
|
||||||
label: 'OpenSSL Installed',
|
label: 'OpenSSL Installed',
|
||||||
run: async (_: EnvironmentInfo) => {
|
run: async (_: EnvironmentInfo) => {
|
||||||
const isAvailable = await commandSucceeds('openssl version');
|
const isAvailable = await commandSucceeds('openssl version');
|
||||||
@@ -72,6 +75,7 @@ export function getHealthchecks(): Healthchecks {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
key: 'common.watchman',
|
||||||
label: 'Watchman Installed',
|
label: 'Watchman Installed',
|
||||||
run: async (_: EnvironmentInfo) => {
|
run: async (_: EnvironmentInfo) => {
|
||||||
const isAvailable = await isWatchmanAvailable();
|
const isAvailable = await isWatchmanAvailable();
|
||||||
@@ -88,6 +92,7 @@ export function getHealthchecks(): Healthchecks {
|
|||||||
isSkipped: false,
|
isSkipped: false,
|
||||||
healthchecks: [
|
healthchecks: [
|
||||||
{
|
{
|
||||||
|
key: 'android.sdk',
|
||||||
label: 'SDK Installed',
|
label: 'SDK Installed',
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
run: async (e: EnvironmentInfo) => ({
|
run: async (e: EnvironmentInfo) => ({
|
||||||
@@ -104,6 +109,7 @@ export function getHealthchecks(): Healthchecks {
|
|||||||
isSkipped: false,
|
isSkipped: false,
|
||||||
healthchecks: [
|
healthchecks: [
|
||||||
{
|
{
|
||||||
|
key: 'ios.sdk',
|
||||||
label: 'SDK Installed',
|
label: 'SDK Installed',
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
run: async (e: EnvironmentInfo) => ({
|
run: async (e: EnvironmentInfo) => ({
|
||||||
@@ -111,6 +117,7 @@ export function getHealthchecks(): Healthchecks {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
key: 'ios.xcode',
|
||||||
label: 'XCode Installed',
|
label: 'XCode Installed',
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
run: async (e: EnvironmentInfo) => ({
|
run: async (e: EnvironmentInfo) => ({
|
||||||
@@ -118,6 +125,7 @@ export function getHealthchecks(): Healthchecks {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
key: 'ios.xcode-select',
|
||||||
label: 'xcode-select set',
|
label: 'xcode-select set',
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
run: async (_: EnvironmentInfo) => ({
|
run: async (_: EnvironmentInfo) => ({
|
||||||
@@ -125,6 +133,7 @@ export function getHealthchecks(): Healthchecks {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
key: 'ios.instruments',
|
||||||
label: 'Instruments exists',
|
label: 'Instruments exists',
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
run: async (_: EnvironmentInfo) => {
|
run: async (_: EnvironmentInfo) => {
|
||||||
@@ -164,17 +173,20 @@ export async function runHealthchecks(): Promise<
|
|||||||
{
|
{
|
||||||
label: category.label,
|
label: category.label,
|
||||||
results: await Promise.all(
|
results: await Promise.all(
|
||||||
category.healthchecks.map(async ({label, run, isRequired}) => ({
|
category.healthchecks.map(
|
||||||
label,
|
async ({key, label, run, isRequired}) => ({
|
||||||
isRequired: isRequired ?? true,
|
key,
|
||||||
result: await run(environmentInfo).catch(e => {
|
label,
|
||||||
console.error(e);
|
isRequired: isRequired ?? true,
|
||||||
// TODO Improve result type to be: OK | Problem(message, fix...)
|
result: await run(environmentInfo).catch(e => {
|
||||||
return {
|
console.error(e);
|
||||||
hasProblem: true,
|
// TODO Improve result type to be: OK | Problem(message, fix...)
|
||||||
};
|
return {
|
||||||
|
hasProblem: true,
|
||||||
|
};
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
})),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -23,14 +23,14 @@ import runHealthchecks, {
|
|||||||
HealthcheckEventsHandler,
|
HealthcheckEventsHandler,
|
||||||
} from '../utils/runHealthchecks';
|
} from '../utils/runHealthchecks';
|
||||||
import {
|
import {
|
||||||
|
HealthcheckResult,
|
||||||
updateHealthcheckResult,
|
updateHealthcheckResult,
|
||||||
startHealthchecks,
|
startHealthchecks,
|
||||||
finishHealthchecks,
|
finishHealthchecks,
|
||||||
HealthcheckStatus,
|
|
||||||
} from '../reducers/healthchecks';
|
} from '../reducers/healthchecks';
|
||||||
|
|
||||||
type StateFromProps = {
|
type StateFromProps = {
|
||||||
healthcheckStatus: HealthcheckStatus;
|
healthcheckResult: HealthcheckResult;
|
||||||
} & HealthcheckSettings;
|
} & HealthcheckSettings;
|
||||||
|
|
||||||
type DispatchFromProps = {
|
type DispatchFromProps = {
|
||||||
@@ -52,7 +52,11 @@ class DoctorBar extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
async showMessageIfChecksFailed() {
|
async showMessageIfChecksFailed() {
|
||||||
await runHealthchecks(this.props);
|
await runHealthchecks(this.props);
|
||||||
if (this.props.healthcheckStatus === 'FAILED') {
|
if (
|
||||||
|
(this.props.healthcheckResult.status === 'FAILED' ||
|
||||||
|
this.props.healthcheckResult.status === 'WARNING') &&
|
||||||
|
!this.props.healthcheckResult.isAcknowledged
|
||||||
|
) {
|
||||||
this.setVisible(true);
|
this.setVisible(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,11 +103,11 @@ export default connect<StateFromProps, DispatchFromProps, {}, Store>(
|
|||||||
({
|
({
|
||||||
settingsState: {enableAndroid},
|
settingsState: {enableAndroid},
|
||||||
healthchecks: {
|
healthchecks: {
|
||||||
healthcheckReport: {status},
|
healthcheckReport: {result},
|
||||||
},
|
},
|
||||||
}) => ({
|
}) => ({
|
||||||
enableAndroid,
|
enableAndroid,
|
||||||
healthcheckStatus: status,
|
healthcheckResult: result,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
setActiveSheet,
|
setActiveSheet,
|
||||||
|
|||||||
@@ -26,14 +26,12 @@ import {State as Store} from '../reducers';
|
|||||||
import {
|
import {
|
||||||
HealthcheckResult,
|
HealthcheckResult,
|
||||||
HealthcheckReportCategory,
|
HealthcheckReportCategory,
|
||||||
HealthcheckReportItem,
|
|
||||||
HealthcheckReport,
|
HealthcheckReport,
|
||||||
startHealthchecks,
|
startHealthchecks,
|
||||||
finishHealthchecks,
|
finishHealthchecks,
|
||||||
updateHealthcheckResult,
|
updateHealthcheckResult,
|
||||||
acknowledgeProblems,
|
acknowledgeProblems,
|
||||||
resetAcknowledgedProblems,
|
resetAcknowledgedProblems,
|
||||||
HealthcheckStatus,
|
|
||||||
} from '../reducers/healthchecks';
|
} from '../reducers/healthchecks';
|
||||||
import runHealthchecks, {
|
import runHealthchecks, {
|
||||||
HealthcheckSettings,
|
HealthcheckSettings,
|
||||||
@@ -123,17 +121,18 @@ function CenteredCheckbox(props: {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function HealthcheckIcon(props: {check: HealthcheckResult}) {
|
function HealthcheckIcon(props: {checkResult: HealthcheckResult}) {
|
||||||
switch (props.check.status) {
|
const {checkResult: check} = props;
|
||||||
|
switch (props.checkResult.status) {
|
||||||
case 'IN_PROGRESS':
|
case 'IN_PROGRESS':
|
||||||
return <LoadingIndicator size={16} title={props.check.message} />;
|
return <LoadingIndicator size={16} title={props.checkResult.message} />;
|
||||||
case 'SKIPPED':
|
case 'SKIPPED':
|
||||||
return (
|
return (
|
||||||
<Glyph
|
<Glyph
|
||||||
size={16}
|
size={16}
|
||||||
name={'question'}
|
name={'question'}
|
||||||
color={colors.grey}
|
color={colors.grey}
|
||||||
title={props.check.message}
|
title={props.checkResult.message}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 'SUCCESS':
|
case 'SUCCESS':
|
||||||
@@ -142,7 +141,7 @@ function HealthcheckIcon(props: {check: HealthcheckResult}) {
|
|||||||
size={16}
|
size={16}
|
||||||
name={'checkmark'}
|
name={'checkmark'}
|
||||||
color={colors.green}
|
color={colors.green}
|
||||||
title={props.check.message}
|
title={props.checkResult.message}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 'FAILED':
|
case 'FAILED':
|
||||||
@@ -151,17 +150,8 @@ function HealthcheckIcon(props: {check: HealthcheckResult}) {
|
|||||||
size={16}
|
size={16}
|
||||||
name={'cross'}
|
name={'cross'}
|
||||||
color={colors.red}
|
color={colors.red}
|
||||||
title={props.check.message}
|
title={props.checkResult.message}
|
||||||
/>
|
variant={check.isAcknowledged ? 'outline' : 'filled'}
|
||||||
);
|
|
||||||
case 'FAILED_ACKNOWLEDGED':
|
|
||||||
return (
|
|
||||||
<Glyph
|
|
||||||
size={16}
|
|
||||||
name={'cross'}
|
|
||||||
color={colors.red}
|
|
||||||
title={props.check.message}
|
|
||||||
variant="outline"
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
@@ -170,26 +160,26 @@ function HealthcheckIcon(props: {check: HealthcheckResult}) {
|
|||||||
size={16}
|
size={16}
|
||||||
name={'caution'}
|
name={'caution'}
|
||||||
color={colors.yellow}
|
color={colors.yellow}
|
||||||
title={props.check.message}
|
title={props.checkResult.message}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function HealthcheckDisplay(props: {
|
function HealthcheckDisplay(props: {
|
||||||
category: HealthcheckReportCategory;
|
label: string;
|
||||||
check: HealthcheckReportItem;
|
result: HealthcheckResult;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<FlexColumn shrink>
|
<FlexColumn shrink>
|
||||||
<HealthcheckDisplayContainer shrink title={props.check.message}>
|
<HealthcheckDisplayContainer shrink title={props.result.message}>
|
||||||
<HealthcheckIcon check={props.check} />
|
<HealthcheckIcon checkResult={props.result} />
|
||||||
<HealthcheckLabel
|
<HealthcheckLabel
|
||||||
underline={!!props.onClick}
|
underline={!!props.onClick}
|
||||||
cursor={props.onClick && 'pointer'}
|
cursor={props.onClick && 'pointer'}
|
||||||
onClick={props.onClick}>
|
onClick={props.onClick}>
|
||||||
{props.check.label}
|
{props.label}
|
||||||
</HealthcheckLabel>
|
</HealthcheckLabel>
|
||||||
</HealthcheckDisplayContainer>
|
</HealthcheckDisplayContainer>
|
||||||
</FlexColumn>
|
</FlexColumn>
|
||||||
@@ -221,20 +211,13 @@ function SideMessageDisplay(props: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasProblems(status: HealthcheckStatus) {
|
function hasProblems(result: HealthcheckResult) {
|
||||||
return (
|
const {status} = result;
|
||||||
status === 'FAILED' ||
|
return status === 'FAILED' || status === 'WARNING';
|
||||||
status === 'FAILED_ACKNOWLEDGED' ||
|
|
||||||
status === 'WARNING'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasFailedChecks(status: HealthcheckStatus) {
|
function hasNewProblems(result: HealthcheckResult) {
|
||||||
return status === 'FAILED' || status === 'FAILED_ACKNOWLEDGED';
|
return hasProblems(result) && !result.isAcknowledged;
|
||||||
}
|
|
||||||
|
|
||||||
function hasNewFailedChecks(status: HealthcheckStatus) {
|
|
||||||
return status === 'FAILED';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
@@ -254,21 +237,21 @@ class DoctorSheet extends Component<Props, State> {
|
|||||||
static getDerivedStateFromProps(props: Props, state: State): State | null {
|
static getDerivedStateFromProps(props: Props, state: State): State | null {
|
||||||
if (
|
if (
|
||||||
!state.acknowledgeCheckboxVisible &&
|
!state.acknowledgeCheckboxVisible &&
|
||||||
hasFailedChecks(props.healthcheckReport.status)
|
hasProblems(props.healthcheckReport.result)
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
acknowledgeCheckboxVisible: true,
|
acknowledgeCheckboxVisible: true,
|
||||||
acknowledgeOnClose:
|
acknowledgeOnClose:
|
||||||
state.acknowledgeOnClose === undefined
|
state.acknowledgeOnClose === undefined
|
||||||
? !hasNewFailedChecks(props.healthcheckReport.status)
|
? !hasNewProblems(props.healthcheckReport.result)
|
||||||
: state.acknowledgeOnClose,
|
: state.acknowledgeOnClose,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
state.acknowledgeCheckboxVisible &&
|
state.acknowledgeCheckboxVisible &&
|
||||||
!hasFailedChecks(props.healthcheckReport.status)
|
!hasProblems(props.healthcheckReport.result)
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@@ -296,8 +279,8 @@ class DoctorSheet extends Component<Props, State> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
openHelpUrl(check: HealthcheckReportItem): void {
|
openHelpUrl(helpUrl?: string): void {
|
||||||
check.helpUrl && shell.openExternal(check.helpUrl);
|
helpUrl && shell.openExternal(helpUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
async runHealthchecks() {
|
async runHealthchecks() {
|
||||||
@@ -311,29 +294,34 @@ class DoctorSheet extends Component<Props, State> {
|
|||||||
<FlexRow>
|
<FlexRow>
|
||||||
<HealthcheckListContainer>
|
<HealthcheckListContainer>
|
||||||
{Object.values(this.props.healthcheckReport.categories).map(
|
{Object.values(this.props.healthcheckReport.categories).map(
|
||||||
(category: HealthcheckReportCategory, categoryIdx: number) => {
|
(category: HealthcheckReportCategory) => {
|
||||||
return (
|
return (
|
||||||
<CategoryContainer key={categoryIdx}>
|
<CategoryContainer key={category.key}>
|
||||||
<HealthcheckDisplay check={category} category={category} />
|
<HealthcheckDisplay
|
||||||
{category.status !== 'SKIPPED' && (
|
label={category.label}
|
||||||
|
result={category.result}
|
||||||
|
/>
|
||||||
|
{category.result.status !== 'SKIPPED' && (
|
||||||
<CategoryContainer>
|
<CategoryContainer>
|
||||||
{category.checks.map((check, checkIdx) => (
|
{Object.values(category.checks).map(check => (
|
||||||
<HealthcheckDisplay
|
<HealthcheckDisplay
|
||||||
key={checkIdx}
|
key={check.key}
|
||||||
category={category}
|
label={check.label}
|
||||||
check={check}
|
result={check.result}
|
||||||
onClick={
|
onClick={
|
||||||
check.helpUrl
|
check.result.helpUrl
|
||||||
? () => this.openHelpUrl(check)
|
? () => this.openHelpUrl(check.result.helpUrl)
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</CategoryContainer>
|
</CategoryContainer>
|
||||||
)}
|
)}
|
||||||
{category.status === 'SKIPPED' && (
|
{category.result.status === 'SKIPPED' && (
|
||||||
<CategoryContainer>
|
<CategoryContainer>
|
||||||
<SkipReasonLabel>{category.message}</SkipReasonLabel>
|
<SkipReasonLabel>
|
||||||
|
{category.result.message}
|
||||||
|
</SkipReasonLabel>
|
||||||
</CategoryContainer>
|
</CategoryContainer>
|
||||||
)}
|
)}
|
||||||
</CategoryContainer>
|
</CategoryContainer>
|
||||||
@@ -345,9 +333,9 @@ class DoctorSheet extends Component<Props, State> {
|
|||||||
<SideContainer shrink>
|
<SideContainer shrink>
|
||||||
<SideMessageDisplay
|
<SideMessageDisplay
|
||||||
isHealthcheckInProgress={
|
isHealthcheckInProgress={
|
||||||
this.props.healthcheckReport.status === 'IN_PROGRESS'
|
this.props.healthcheckReport.result.status === 'IN_PROGRESS'
|
||||||
}
|
}
|
||||||
hasProblems={hasProblems(this.props.healthcheckReport.status)}
|
hasProblems={hasProblems(this.props.healthcheckReport.result)}
|
||||||
/>
|
/>
|
||||||
</SideContainer>
|
</SideContainer>
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
@@ -366,7 +354,9 @@ class DoctorSheet extends Component<Props, State> {
|
|||||||
Close
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
disabled={this.props.healthcheckReport.status === 'IN_PROGRESS'}
|
disabled={
|
||||||
|
this.props.healthcheckReport.result.status === 'IN_PROGRESS'
|
||||||
|
}
|
||||||
type="primary"
|
type="primary"
|
||||||
compact
|
compact
|
||||||
padded
|
padded
|
||||||
|
|||||||
335
src/reducers/__tests__/__snapshots__/healthchecks.node.tsx.snap
Normal file
335
src/reducers/__tests__/__snapshots__/healthchecks.node.tsx.snap
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`acknowledgeProblems 1`] = `
|
||||||
|
Object {
|
||||||
|
"acknowledgedProblems": Array [
|
||||||
|
"ios.sdk",
|
||||||
|
"common.openssl",
|
||||||
|
],
|
||||||
|
"healthcheckReport": Object {
|
||||||
|
"categories": Object {
|
||||||
|
"android": Object {
|
||||||
|
"checks": Object {
|
||||||
|
"android.sdk": Object {
|
||||||
|
"key": "android.sdk",
|
||||||
|
"label": "SDK Installed",
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": true,
|
||||||
|
"status": "SUCCESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"key": "android",
|
||||||
|
"label": "Android",
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": true,
|
||||||
|
"status": "SUCCESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"common": Object {
|
||||||
|
"checks": Object {
|
||||||
|
"common.openssl": Object {
|
||||||
|
"key": "common.openssl",
|
||||||
|
"label": "OpenSSL Istalled",
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": true,
|
||||||
|
"status": "FAILED",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"key": "common",
|
||||||
|
"label": "Common",
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": true,
|
||||||
|
"status": "FAILED",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ios": Object {
|
||||||
|
"checks": Object {
|
||||||
|
"ios.sdk": Object {
|
||||||
|
"key": "ios.sdk",
|
||||||
|
"label": "SDK Installed",
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": true,
|
||||||
|
"status": "FAILED",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"key": "ios",
|
||||||
|
"label": "iOS",
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": true,
|
||||||
|
"status": "FAILED",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": true,
|
||||||
|
"status": "FAILED",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`finish 1`] = `
|
||||||
|
Object {
|
||||||
|
"acknowledgedProblems": Array [],
|
||||||
|
"healthcheckReport": Object {
|
||||||
|
"categories": Object {
|
||||||
|
"android": Object {
|
||||||
|
"checks": Object {
|
||||||
|
"android.sdk": Object {
|
||||||
|
"key": "android.sdk",
|
||||||
|
"label": "SDK Installed",
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": false,
|
||||||
|
"message": "Updated Test Message",
|
||||||
|
"status": "SUCCESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"key": "android",
|
||||||
|
"label": "Android",
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": false,
|
||||||
|
"status": "SUCCESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"common": Object {
|
||||||
|
"checks": Object {
|
||||||
|
"common.openssl": Object {
|
||||||
|
"key": "common.openssl",
|
||||||
|
"label": "OpenSSL Istalled",
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": false,
|
||||||
|
"message": "Updated Test Message",
|
||||||
|
"status": "SUCCESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"key": "common",
|
||||||
|
"label": "Common",
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": false,
|
||||||
|
"status": "SUCCESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ios": Object {
|
||||||
|
"checks": Object {
|
||||||
|
"ios.sdk": Object {
|
||||||
|
"key": "ios.sdk",
|
||||||
|
"label": "SDK Installed",
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": false,
|
||||||
|
"message": "Updated Test Message",
|
||||||
|
"status": "SUCCESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"key": "ios",
|
||||||
|
"label": "iOS",
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": false,
|
||||||
|
"status": "SUCCESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"result": Object {
|
||||||
|
"status": "SUCCESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`startHealthCheck 1`] = `
|
||||||
|
Object {
|
||||||
|
"acknowledgedProblems": Array [],
|
||||||
|
"healthcheckReport": Object {
|
||||||
|
"categories": Object {
|
||||||
|
"android": Object {
|
||||||
|
"checks": Object {
|
||||||
|
"android.sdk": Object {
|
||||||
|
"key": "android.sdk",
|
||||||
|
"label": "SDK Installed",
|
||||||
|
"result": Object {
|
||||||
|
"status": "IN_PROGRESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"key": "android",
|
||||||
|
"label": "Android",
|
||||||
|
"result": Object {
|
||||||
|
"status": "IN_PROGRESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"common": Object {
|
||||||
|
"checks": Object {
|
||||||
|
"common.openssl": Object {
|
||||||
|
"key": "common.openssl",
|
||||||
|
"label": "OpenSSL Istalled",
|
||||||
|
"result": Object {
|
||||||
|
"status": "IN_PROGRESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"key": "common",
|
||||||
|
"label": "Common",
|
||||||
|
"result": Object {
|
||||||
|
"status": "IN_PROGRESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ios": Object {
|
||||||
|
"checks": Object {
|
||||||
|
"ios.sdk": Object {
|
||||||
|
"key": "ios.sdk",
|
||||||
|
"label": "SDK Installed",
|
||||||
|
"result": Object {
|
||||||
|
"status": "IN_PROGRESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"key": "ios",
|
||||||
|
"label": "iOS",
|
||||||
|
"result": Object {
|
||||||
|
"status": "IN_PROGRESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"result": Object {
|
||||||
|
"status": "IN_PROGRESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`statuses updated after healthchecks finished 1`] = `
|
||||||
|
Object {
|
||||||
|
"acknowledgedProblems": Array [],
|
||||||
|
"healthcheckReport": Object {
|
||||||
|
"categories": Object {
|
||||||
|
"android": Object {
|
||||||
|
"checks": Object {
|
||||||
|
"android.sdk": Object {
|
||||||
|
"key": "android.sdk",
|
||||||
|
"label": "SDK Installed",
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": false,
|
||||||
|
"message": "Updated Test Message",
|
||||||
|
"status": "FAILED",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"key": "android",
|
||||||
|
"label": "Android",
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": false,
|
||||||
|
"status": "FAILED",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"common": Object {
|
||||||
|
"checks": Object {
|
||||||
|
"common.openssl": Object {
|
||||||
|
"key": "common.openssl",
|
||||||
|
"label": "OpenSSL Istalled",
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": false,
|
||||||
|
"message": "Updated Test Message",
|
||||||
|
"status": "SUCCESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"key": "common",
|
||||||
|
"label": "Common",
|
||||||
|
"result": Object {
|
||||||
|
"status": "SUCCESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ios": Object {
|
||||||
|
"checks": Object {
|
||||||
|
"ios.sdk": Object {
|
||||||
|
"key": "ios.sdk",
|
||||||
|
"label": "SDK Installed",
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": false,
|
||||||
|
"message": "Updated Test Message",
|
||||||
|
"status": "SUCCESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"key": "ios",
|
||||||
|
"label": "iOS",
|
||||||
|
"result": Object {
|
||||||
|
"status": "SUCCESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": false,
|
||||||
|
"status": "FAILED",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`updateHealthcheckResult 1`] = `
|
||||||
|
Object {
|
||||||
|
"acknowledgedProblems": Array [],
|
||||||
|
"healthcheckReport": Object {
|
||||||
|
"categories": Object {
|
||||||
|
"android": Object {
|
||||||
|
"checks": Object {
|
||||||
|
"android.sdk": Object {
|
||||||
|
"key": "android.sdk",
|
||||||
|
"label": "SDK Installed",
|
||||||
|
"result": Object {
|
||||||
|
"isAcknowledged": false,
|
||||||
|
"message": "Updated Test Message",
|
||||||
|
"status": "SUCCESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"key": "android",
|
||||||
|
"label": "Android",
|
||||||
|
"result": Object {
|
||||||
|
"status": "IN_PROGRESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"common": Object {
|
||||||
|
"checks": Object {
|
||||||
|
"common.openssl": Object {
|
||||||
|
"key": "common.openssl",
|
||||||
|
"label": "OpenSSL Istalled",
|
||||||
|
"result": Object {
|
||||||
|
"status": "IN_PROGRESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"key": "common",
|
||||||
|
"label": "Common",
|
||||||
|
"result": Object {
|
||||||
|
"status": "IN_PROGRESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ios": Object {
|
||||||
|
"checks": Object {
|
||||||
|
"ios.sdk": Object {
|
||||||
|
"key": "ios.sdk",
|
||||||
|
"label": "SDK Installed",
|
||||||
|
"result": Object {
|
||||||
|
"status": "IN_PROGRESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"key": "ios",
|
||||||
|
"label": "iOS",
|
||||||
|
"result": Object {
|
||||||
|
"status": "IN_PROGRESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"result": Object {
|
||||||
|
"status": "IN_PROGRESS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -64,143 +64,106 @@ const HEALTHCHECKS: Healthchecks = {
|
|||||||
|
|
||||||
test('startHealthCheck', () => {
|
test('startHealthCheck', () => {
|
||||||
const res = reducer(undefined, startHealthchecks(HEALTHCHECKS));
|
const res = reducer(undefined, startHealthchecks(HEALTHCHECKS));
|
||||||
expect(res.healthcheckReport.status).toBe('IN_PROGRESS');
|
expect(res).toMatchSnapshot();
|
||||||
expect(res.healthcheckReport.categories.length).toBe(3);
|
|
||||||
expect(res.healthcheckReport.categories[0].status).toEqual('IN_PROGRESS');
|
|
||||||
expect(res.healthcheckReport.categories[0].label).toEqual('iOS');
|
|
||||||
expect(res.healthcheckReport.categories[0].checks.length).toEqual(1);
|
|
||||||
expect(res.healthcheckReport.categories[0].checks[0].label).toEqual(
|
|
||||||
'SDK Installed',
|
|
||||||
);
|
|
||||||
expect(res.healthcheckReport.categories[0].checks[0].status).toEqual(
|
|
||||||
'IN_PROGRESS',
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('updateHealthcheckResult', () => {
|
test('updateHealthcheckResult', () => {
|
||||||
let res = reducer(undefined, startHealthchecks(HEALTHCHECKS));
|
let res = reducer(undefined, startHealthchecks(HEALTHCHECKS));
|
||||||
res = reducer(
|
res = reducer(
|
||||||
res,
|
res,
|
||||||
updateHealthcheckResult(0, 0, {
|
updateHealthcheckResult('android', 'android.sdk', {
|
||||||
message: 'Updated Test Message',
|
message: 'Updated Test Message',
|
||||||
|
isAcknowledged: false,
|
||||||
status: 'SUCCESS',
|
status: 'SUCCESS',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
expect(res.healthcheckReport.status).toBe('IN_PROGRESS');
|
expect(res).toMatchSnapshot();
|
||||||
expect(res.healthcheckReport.categories[0].checks[0].message).toEqual(
|
|
||||||
'Updated Test Message',
|
|
||||||
);
|
|
||||||
expect(res.healthcheckReport.categories[0].checks[0].status).toEqual(
|
|
||||||
'SUCCESS',
|
|
||||||
);
|
|
||||||
expect(res.healthcheckReport.categories[0].status).toEqual('IN_PROGRESS');
|
|
||||||
expect(res.healthcheckReport.categories[1].checks[0].message).toBeUndefined();
|
|
||||||
expect(res.healthcheckReport.categories[1].checks[0].status).toEqual(
|
|
||||||
'IN_PROGRESS',
|
|
||||||
);
|
|
||||||
expect(res.healthcheckReport.categories[1].status).toEqual('IN_PROGRESS');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('finish', () => {
|
test('finish', () => {
|
||||||
let res = reducer(undefined, startHealthchecks(HEALTHCHECKS));
|
let res = reducer(undefined, startHealthchecks(HEALTHCHECKS));
|
||||||
res = reducer(
|
res = reducer(
|
||||||
res,
|
res,
|
||||||
updateHealthcheckResult(0, 0, {
|
updateHealthcheckResult('ios', 'ios.sdk', {
|
||||||
message: 'Updated Test Message',
|
message: 'Updated Test Message',
|
||||||
|
isAcknowledged: false,
|
||||||
status: 'SUCCESS',
|
status: 'SUCCESS',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
res = reducer(
|
res = reducer(
|
||||||
res,
|
res,
|
||||||
updateHealthcheckResult(1, 0, {
|
updateHealthcheckResult('android', 'android.sdk', {
|
||||||
message: 'Updated Test Message',
|
message: 'Updated Test Message',
|
||||||
|
isAcknowledged: false,
|
||||||
status: 'SUCCESS',
|
status: 'SUCCESS',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
res = reducer(
|
res = reducer(
|
||||||
res,
|
res,
|
||||||
updateHealthcheckResult(2, 0, {
|
updateHealthcheckResult('common', 'common.openssl', {
|
||||||
message: 'Updated Test Message',
|
message: 'Updated Test Message',
|
||||||
|
isAcknowledged: false,
|
||||||
status: 'SUCCESS',
|
status: 'SUCCESS',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
res = reducer(res, finishHealthchecks());
|
res = reducer(res, finishHealthchecks());
|
||||||
expect(res.healthcheckReport.status).toBe('SUCCESS');
|
expect(res).toMatchSnapshot();
|
||||||
expect(res.healthcheckReport.categories.map(c => c.status)).toEqual([
|
|
||||||
'SUCCESS',
|
|
||||||
'SUCCESS',
|
|
||||||
'SUCCESS',
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('statuses updated after healthchecks finished', () => {
|
test('statuses updated after healthchecks finished', () => {
|
||||||
let res = reducer(undefined, startHealthchecks(HEALTHCHECKS));
|
let res = reducer(undefined, startHealthchecks(HEALTHCHECKS));
|
||||||
res = reducer(
|
res = reducer(
|
||||||
res,
|
res,
|
||||||
updateHealthcheckResult(1, 0, {
|
updateHealthcheckResult('android', 'android.sdk', {
|
||||||
message: 'Updated Test Message',
|
message: 'Updated Test Message',
|
||||||
|
isAcknowledged: false,
|
||||||
status: 'FAILED',
|
status: 'FAILED',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
res = reducer(
|
res = reducer(
|
||||||
res,
|
res,
|
||||||
updateHealthcheckResult(0, 0, {
|
updateHealthcheckResult('ios', 'ios.sdk', {
|
||||||
message: 'Updated Test Message',
|
message: 'Updated Test Message',
|
||||||
|
isAcknowledged: false,
|
||||||
status: 'SUCCESS',
|
status: 'SUCCESS',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
res = reducer(
|
res = reducer(
|
||||||
res,
|
res,
|
||||||
updateHealthcheckResult(2, 0, {
|
updateHealthcheckResult('common', 'common.openssl', {
|
||||||
message: 'Updated Test Message',
|
message: 'Updated Test Message',
|
||||||
|
isAcknowledged: false,
|
||||||
status: 'SUCCESS',
|
status: 'SUCCESS',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
res = reducer(res, finishHealthchecks());
|
res = reducer(res, finishHealthchecks());
|
||||||
expect(res.healthcheckReport.status).toBe('FAILED');
|
expect(res).toMatchSnapshot();
|
||||||
expect(res.healthcheckReport.categories.map(c => c.status)).toEqual([
|
|
||||||
'SUCCESS',
|
|
||||||
'FAILED',
|
|
||||||
'SUCCESS',
|
|
||||||
]);
|
|
||||||
expect(res.healthcheckReport.categories[1].checks[0].message).toEqual(
|
|
||||||
'Updated Test Message',
|
|
||||||
);
|
|
||||||
expect(res.healthcheckReport.categories[1].checks[0].status).toEqual(
|
|
||||||
'FAILED',
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('acknowledgeProblems', () => {
|
test('acknowledgeProblems', () => {
|
||||||
let res = reducer(undefined, startHealthchecks(HEALTHCHECKS));
|
let res = reducer(undefined, startHealthchecks(HEALTHCHECKS));
|
||||||
res = reducer(
|
res = reducer(
|
||||||
res,
|
res,
|
||||||
updateHealthcheckResult(0, 0, {
|
updateHealthcheckResult('ios', 'ios.sdk', {
|
||||||
|
isAcknowledged: false,
|
||||||
status: 'FAILED',
|
status: 'FAILED',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
res = reducer(
|
res = reducer(
|
||||||
res,
|
res,
|
||||||
updateHealthcheckResult(1, 0, {
|
updateHealthcheckResult('android', 'android.sdk', {
|
||||||
|
isAcknowledged: false,
|
||||||
status: 'SUCCESS',
|
status: 'SUCCESS',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
res = reducer(
|
res = reducer(
|
||||||
res,
|
res,
|
||||||
updateHealthcheckResult(2, 0, {
|
updateHealthcheckResult('common', 'common.openssl', {
|
||||||
|
isAcknowledged: false,
|
||||||
status: 'FAILED',
|
status: 'FAILED',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
res = reducer(res, finishHealthchecks());
|
res = reducer(res, finishHealthchecks());
|
||||||
res = reducer(res, acknowledgeProblems());
|
res = reducer(res, acknowledgeProblems());
|
||||||
expect(res.healthcheckReport.categories[0].status).toEqual(
|
expect(res).toMatchSnapshot();
|
||||||
'FAILED_ACKNOWLEDGED',
|
|
||||||
);
|
|
||||||
expect(res.healthcheckReport.categories[0].checks[0].status).toEqual(
|
|
||||||
'FAILED_ACKNOWLEDGED',
|
|
||||||
);
|
|
||||||
expect(res.healthcheckReport.categories[1].status).toEqual('SUCCESS');
|
|
||||||
expect(res.healthcheckReport.categories[2].status).toEqual(
|
|
||||||
'FAILED_ACKNOWLEDGED',
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ export type Action =
|
|||||||
| {
|
| {
|
||||||
type: 'UPDATE_HEALTHCHECK_RESULT';
|
type: 'UPDATE_HEALTHCHECK_RESULT';
|
||||||
payload: {
|
payload: {
|
||||||
categoryIdx: number;
|
categoryKey: string;
|
||||||
itemIdx: number;
|
itemKey: string;
|
||||||
result: HealthcheckResult;
|
result: HealthcheckResult;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -41,157 +41,170 @@ export type Action =
|
|||||||
|
|
||||||
const INITIAL_STATE: State = {
|
const INITIAL_STATE: State = {
|
||||||
healthcheckReport: {
|
healthcheckReport: {
|
||||||
status: 'IN_PROGRESS',
|
result: {status: 'IN_PROGRESS'},
|
||||||
categories: [],
|
categories: {},
|
||||||
},
|
},
|
||||||
acknowledgedProblems: [],
|
acknowledgedProblems: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type Dictionary<T> = {[key: string]: T};
|
||||||
|
|
||||||
export type HealthcheckStatus =
|
export type HealthcheckStatus =
|
||||||
| 'IN_PROGRESS'
|
| 'IN_PROGRESS'
|
||||||
| 'SUCCESS'
|
| 'SUCCESS'
|
||||||
| 'FAILED'
|
| 'FAILED'
|
||||||
| 'FAILED_ACKNOWLEDGED'
|
|
||||||
| 'SKIPPED'
|
| 'SKIPPED'
|
||||||
| 'WARNING';
|
| 'WARNING';
|
||||||
|
|
||||||
export type HealthcheckResult = {
|
export type HealthcheckResult = {
|
||||||
status: HealthcheckStatus;
|
status: HealthcheckStatus;
|
||||||
|
isAcknowledged?: boolean;
|
||||||
message?: string;
|
message?: string;
|
||||||
helpUrl?: string;
|
helpUrl?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type HealthcheckReportItem = {
|
export type HealthcheckReportItem = {
|
||||||
|
key: string;
|
||||||
label: string;
|
label: string;
|
||||||
} & HealthcheckResult;
|
result: HealthcheckResult;
|
||||||
|
|
||||||
export type HealthcheckReportCategory = {
|
|
||||||
label: string;
|
|
||||||
checks: Array<HealthcheckReportItem>;
|
|
||||||
} & HealthcheckResult;
|
|
||||||
|
|
||||||
export type HealthcheckReport = {
|
|
||||||
status: HealthcheckStatus;
|
|
||||||
categories: Array<HealthcheckReportCategory>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function getHealthcheckIdentifier(
|
export type HealthcheckReportCategory = {
|
||||||
category: HealthcheckReportCategory,
|
key: string;
|
||||||
item: HealthcheckReportItem,
|
label: string;
|
||||||
) {
|
result: HealthcheckResult;
|
||||||
return `${category.label} : ${item.label}`;
|
checks: Dictionary<HealthcheckReportItem>;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export type HealthcheckReport = {
|
||||||
|
result: HealthcheckResult;
|
||||||
|
categories: Dictionary<HealthcheckReportCategory>;
|
||||||
|
};
|
||||||
|
|
||||||
function recomputeHealthcheckStatus(draft: State): void {
|
function recomputeHealthcheckStatus(draft: State): void {
|
||||||
draft.healthcheckReport.status = computeAggregatedStatus(
|
draft.healthcheckReport.result = computeAggregatedResult(
|
||||||
draft.healthcheckReport.categories.map(c => c.status),
|
Object.values(draft.healthcheckReport.categories).map(c => c.result),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function computeAggregatedStatus(
|
function computeAggregatedResult(
|
||||||
statuses: HealthcheckStatus[],
|
results: HealthcheckResult[],
|
||||||
): HealthcheckStatus {
|
): HealthcheckResult {
|
||||||
return statuses.some(s => s === 'IN_PROGRESS')
|
return results.some(r => r.status === 'IN_PROGRESS')
|
||||||
? 'IN_PROGRESS'
|
? {status: 'IN_PROGRESS'}
|
||||||
: statuses.every(s => s === 'SUCCESS')
|
: results.every(r => r.status === 'SUCCESS')
|
||||||
? 'SUCCESS'
|
? {status: 'SUCCESS'}
|
||||||
: statuses.some(s => s === 'FAILED')
|
: results.some(r => r.status === 'FAILED' && !r.isAcknowledged)
|
||||||
? 'FAILED'
|
? {status: 'FAILED', isAcknowledged: false}
|
||||||
: statuses.some(s => s === 'FAILED_ACKNOWLEDGED')
|
: results.some(r => r.status === 'FAILED')
|
||||||
? 'FAILED_ACKNOWLEDGED'
|
? {status: 'FAILED', isAcknowledged: true}
|
||||||
: statuses.some(s => s === 'WARNING')
|
: results.some(r => r.status === 'WARNING' && !r.isAcknowledged)
|
||||||
? 'WARNING'
|
? {status: 'WARNING', isAcknowledged: false}
|
||||||
: 'SKIPPED';
|
: results.some(r => r.status === 'WARNING')
|
||||||
|
? {status: 'WARNING', isAcknowledged: true}
|
||||||
|
: {status: 'SKIPPED'};
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateCheckResult = produce(
|
const updateCheckResult = produce(
|
||||||
(
|
(
|
||||||
draft: State,
|
draft: State,
|
||||||
{
|
{
|
||||||
categoryIdx,
|
categoryKey,
|
||||||
itemIdx,
|
itemKey,
|
||||||
result,
|
result,
|
||||||
}: {
|
}: {
|
||||||
categoryIdx: number;
|
categoryKey: string;
|
||||||
itemIdx: number;
|
itemKey: string;
|
||||||
result: HealthcheckResult;
|
result: HealthcheckResult;
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
const category = draft.healthcheckReport.categories[categoryIdx];
|
const category = draft.healthcheckReport.categories[categoryKey];
|
||||||
const item = category.checks[itemIdx];
|
const item = category.checks[itemKey];
|
||||||
Object.assign(item, result);
|
Object.assign(item.result, result);
|
||||||
if (
|
item.result.isAcknowledged = draft.acknowledgedProblems.includes(item.key);
|
||||||
result.status === 'FAILED' &&
|
|
||||||
draft.acknowledgedProblems.includes(
|
|
||||||
getHealthcheckIdentifier(category, item),
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
item.status = 'FAILED_ACKNOWLEDGED';
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function createDict<T>(pairs: [string, T][]): Dictionary<T> {
|
||||||
|
const obj: Dictionary<T> = {};
|
||||||
|
for (const pair of pairs) {
|
||||||
|
obj[pair[0]] = pair[1];
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
const start = produce((draft: State, healthchecks: Healthchecks) => {
|
const start = produce((draft: State, healthchecks: Healthchecks) => {
|
||||||
draft.healthcheckReport = {
|
draft.healthcheckReport = {
|
||||||
status: 'IN_PROGRESS',
|
result: {status: 'IN_PROGRESS'},
|
||||||
categories: Object.values(healthchecks)
|
categories: createDict<HealthcheckReportCategory>(
|
||||||
.map(category => {
|
Object.entries(healthchecks).map(([categoryKey, category]) => {
|
||||||
if (category.isSkipped) {
|
if (category.isSkipped) {
|
||||||
return {
|
return [
|
||||||
status: 'SKIPPED',
|
categoryKey,
|
||||||
label: category.label,
|
{
|
||||||
checks: [],
|
key: categoryKey,
|
||||||
message: category.skipReason,
|
result: {
|
||||||
};
|
status: 'SKIPPED',
|
||||||
|
message: category.skipReason,
|
||||||
|
},
|
||||||
|
label: category.label,
|
||||||
|
checks: createDict<HealthcheckReportItem>([]),
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
return {
|
return [
|
||||||
status: 'IN_PROGRESS',
|
categoryKey,
|
||||||
label: category.label,
|
{
|
||||||
checks: category.healthchecks.map(x => ({
|
key: categoryKey,
|
||||||
status: 'IN_PROGRESS',
|
result: {status: 'IN_PROGRESS'},
|
||||||
label: x.label,
|
label: category.label,
|
||||||
})),
|
checks: createDict<HealthcheckReportItem>(
|
||||||
};
|
category.healthchecks.map(check => [
|
||||||
})
|
check.key,
|
||||||
.filter(x => !!x)
|
{
|
||||||
.map(x => x as HealthcheckReportCategory),
|
key: check.key,
|
||||||
|
result: {status: 'IN_PROGRESS'},
|
||||||
|
label: check.label,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const finish = produce((draft: State) => {
|
const finish = produce((draft: State) => {
|
||||||
draft.healthcheckReport.categories
|
Object.values(draft.healthcheckReport.categories)
|
||||||
.filter(cat => cat.status !== 'SKIPPED')
|
.filter(cat => cat.result.status !== 'SKIPPED')
|
||||||
.forEach(cat => {
|
.forEach(cat => {
|
||||||
cat.message = undefined;
|
cat.result.message = undefined;
|
||||||
cat.status = computeAggregatedStatus(cat.checks.map(c => c.status));
|
cat.result = computeAggregatedResult(
|
||||||
|
Object.values(cat.checks).map(c => c.result),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
recomputeHealthcheckStatus(draft);
|
recomputeHealthcheckStatus(draft);
|
||||||
if (draft.healthcheckReport.status === 'SUCCESS') {
|
if (draft.healthcheckReport.result.status === 'SUCCESS') {
|
||||||
setAcknowledgedProblemsToEmpty(draft);
|
setAcknowledgedProblemsToEmpty(draft);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const acknowledge = produce((draft: State) => {
|
const acknowledge = produce((draft: State) => {
|
||||||
draft.acknowledgedProblems = ([] as string[]).concat(
|
draft.acknowledgedProblems = ([] as string[]).concat(
|
||||||
...draft.healthcheckReport.categories.map(cat =>
|
...Object.values(draft.healthcheckReport.categories).map(cat =>
|
||||||
cat.checks
|
Object.values(cat.checks)
|
||||||
.filter(
|
.filter(
|
||||||
chk =>
|
chk =>
|
||||||
chk.status === 'FAILED' ||
|
chk.result.status === 'FAILED' || chk.result.status === 'WARNING',
|
||||||
chk.status === 'FAILED_ACKNOWLEDGED' ||
|
|
||||||
chk.status === 'WARNING',
|
|
||||||
)
|
)
|
||||||
.map(chk => getHealthcheckIdentifier(cat, chk)),
|
.map(chk => chk.key),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
draft.healthcheckReport.categories.forEach(cat => {
|
Object.values(draft.healthcheckReport.categories).forEach(cat => {
|
||||||
if (cat.status === 'FAILED') {
|
cat.result.isAcknowledged = true;
|
||||||
cat.status = 'FAILED_ACKNOWLEDGED';
|
Object.values(cat.checks).forEach(chk => {
|
||||||
}
|
chk.result.isAcknowledged = true;
|
||||||
cat.checks.forEach(chk => {
|
|
||||||
if (chk.status == 'FAILED') {
|
|
||||||
chk.status = 'FAILED_ACKNOWLEDGED';
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
recomputeHealthcheckStatus(draft);
|
recomputeHealthcheckStatus(draft);
|
||||||
@@ -199,14 +212,10 @@ const acknowledge = produce((draft: State) => {
|
|||||||
|
|
||||||
function setAcknowledgedProblemsToEmpty(draft: State) {
|
function setAcknowledgedProblemsToEmpty(draft: State) {
|
||||||
draft.acknowledgedProblems = [];
|
draft.acknowledgedProblems = [];
|
||||||
draft.healthcheckReport.categories.forEach(cat => {
|
Object.values(draft.healthcheckReport.categories).forEach(cat => {
|
||||||
if (cat.status === 'FAILED_ACKNOWLEDGED') {
|
cat.result.isAcknowledged = false;
|
||||||
cat.status = 'FAILED';
|
Object.values(cat.checks).forEach(chk => {
|
||||||
}
|
chk.result.isAcknowledged = false;
|
||||||
cat.checks.forEach(chk => {
|
|
||||||
if (chk.status == 'FAILED_ACKNOWLEDGED') {
|
|
||||||
chk.status = 'FAILED';
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -234,14 +243,14 @@ export default function reducer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const updateHealthcheckResult = (
|
export const updateHealthcheckResult = (
|
||||||
categoryIdx: number,
|
categoryKey: string,
|
||||||
itemIdx: number,
|
itemKey: string,
|
||||||
result: HealthcheckResult,
|
result: HealthcheckResult,
|
||||||
): Action => ({
|
): Action => ({
|
||||||
type: 'UPDATE_HEALTHCHECK_RESULT',
|
type: 'UPDATE_HEALTHCHECK_RESULT',
|
||||||
payload: {
|
payload: {
|
||||||
categoryIdx,
|
categoryKey,
|
||||||
itemIdx,
|
itemKey,
|
||||||
result,
|
result,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ let runningHealthcheck: Promise<void>;
|
|||||||
|
|
||||||
export type HealthcheckEventsHandler = {
|
export type HealthcheckEventsHandler = {
|
||||||
updateHealthcheckResult: (
|
updateHealthcheckResult: (
|
||||||
categoryIdx: number,
|
categoryKey: string,
|
||||||
itemIdx: number,
|
itemKey: string,
|
||||||
result: HealthcheckResult,
|
result: HealthcheckResult,
|
||||||
) => void;
|
) => void;
|
||||||
startHealthchecks: (healthchecks: Healthchecks) => void;
|
startHealthchecks: (healthchecks: Healthchecks) => void;
|
||||||
@@ -41,17 +41,11 @@ async function launchHealthchecks(options: HealthcheckOptions): Promise<void> {
|
|||||||
}
|
}
|
||||||
options.startHealthchecks(healthchecks);
|
options.startHealthchecks(healthchecks);
|
||||||
const environmentInfo = await getEnvInfo();
|
const environmentInfo = await getEnvInfo();
|
||||||
const categories = Object.values(healthchecks);
|
for (const [categoryKey, category] of Object.entries(healthchecks)) {
|
||||||
for (const [categoryIdx, category] of categories.entries()) {
|
|
||||||
if (category.isSkipped) {
|
if (category.isSkipped) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (
|
for (const h of category.healthchecks) {
|
||||||
let healthcheckIdx = 0;
|
|
||||||
healthcheckIdx < category.healthchecks.length;
|
|
||||||
healthcheckIdx++
|
|
||||||
) {
|
|
||||||
const h = category.healthchecks[healthcheckIdx];
|
|
||||||
const checkResult = await h.run(environmentInfo);
|
const checkResult = await h.run(environmentInfo);
|
||||||
const result: HealthcheckResult =
|
const result: HealthcheckResult =
|
||||||
checkResult.hasProblem && h.isRequired
|
checkResult.hasProblem && h.isRequired
|
||||||
@@ -65,7 +59,7 @@ async function launchHealthchecks(options: HealthcheckOptions): Promise<void> {
|
|||||||
helpUrl: checkResult.helpUrl,
|
helpUrl: checkResult.helpUrl,
|
||||||
}
|
}
|
||||||
: {status: 'SUCCESS'};
|
: {status: 'SUCCESS'};
|
||||||
options.updateHealthcheckResult(categoryIdx, healthcheckIdx, result);
|
options.updateHealthcheckResult(categoryKey, h.key, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
options.finishHealthchecks();
|
options.finishHealthchecks();
|
||||||
|
|||||||
@@ -294,5 +294,8 @@
|
|||||||
],
|
],
|
||||||
"play": [
|
"play": [
|
||||||
16
|
16
|
||||||
|
],
|
||||||
|
"cross-outline": [
|
||||||
|
16
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user