From a537e51dddd879522e03b520514af247e21aef71 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 12 Apr 2018 08:46:13 -0600 Subject: [PATCH 01/16] add statusCheck configuration to instructionSetSchema --- .../common/tutorials/tutorial_schema.js | 13 +- .../kibana/server/tutorials/apm/index.js | 4 +- .../kibana/server/tutorials/apm/on_prem.js | 163 ++++++++++-------- 3 files changed, 109 insertions(+), 71 deletions(-) diff --git a/src/core_plugins/kibana/common/tutorials/tutorial_schema.js b/src/core_plugins/kibana/common/tutorials/tutorial_schema.js index 9baebd8c85678..57f5d856a4efc 100644 --- a/src/core_plugins/kibana/common/tutorials/tutorial_schema.js +++ b/src/core_plugins/kibana/common/tutorials/tutorial_schema.js @@ -25,6 +25,16 @@ const artifactsSchema = Joi.object({ }), }); +const statusCheckSchema = Joi.object({ + success: Joi.string().required(), + error: Joi.string().required(), + esHitsCheck: Joi.object({ + index: Joi.string().required(), + cluster: Joi.string().valid(['data', 'admin']).required(), + query: Joi.object().required(), + }).required(), +}); + const instructionSchema = Joi.object({ title: Joi.string(), textPre: Joi.string(), @@ -40,7 +50,8 @@ const instructionVariantSchema = Joi.object({ const instructionSetSchema = Joi.object({ title: Joi.string(), // Variants (OSes, languages, etc.) for which tutorial instructions are specified. - instructionVariants: Joi.array().items(instructionVariantSchema).required() + instructionVariants: Joi.array().items(instructionVariantSchema).required(), + statusCheck: statusCheckSchema, }); const paramSchema = Joi.object({ diff --git a/src/core_plugins/kibana/server/tutorials/apm/index.js b/src/core_plugins/kibana/server/tutorials/apm/index.js index 2e4de77510598..b6bf922bdb458 100644 --- a/src/core_plugins/kibana/server/tutorials/apm/index.js +++ b/src/core_plugins/kibana/server/tutorials/apm/index.js @@ -1,5 +1,5 @@ import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category'; -import { ON_PREM_INSTRUCTIONS } from './on_prem'; +import { onPremInstructions } from './on_prem'; import { ELASTIC_CLOUD_INSTRUCTIONS } from './elastic_cloud'; const apmIntro = 'Collect in-depth performance metrics and errors from inside your applications.'; @@ -42,7 +42,7 @@ export function apmSpecProvider(server) { ' [Learn more]({config.docs.base_url}guide/en/apm/get-started/{config.docs.version}/index.html).', euiIconType: 'apmApp', artifacts: artifacts, - onPrem: ON_PREM_INSTRUCTIONS, + onPrem: onPremInstructions(server), elasticCloud: ELASTIC_CLOUD_INSTRUCTIONS, previewImagePath: '/plugins/kibana/home/tutorial_resources/apm/apm.png', }; diff --git a/src/core_plugins/kibana/server/tutorials/apm/on_prem.js b/src/core_plugins/kibana/server/tutorials/apm/on_prem.js index 34f517aed6788..f4b16a9dd0cd4 100644 --- a/src/core_plugins/kibana/server/tutorials/apm/on_prem.js +++ b/src/core_plugins/kibana/server/tutorials/apm/on_prem.js @@ -17,72 +17,99 @@ import { JS_CLIENT_INSTRUCTIONS, } from './apm_client_instructions'; -export const ON_PREM_INSTRUCTIONS = { - instructionSets: [ - { - title: 'APM Server', - instructionVariants: [ - { - id: INSTRUCTION_VARIANT.OSX, - instructions: [ - DOWNLOAD_SERVER_OSX, - IMPORT_DASHBOARD_UNIX, - EDIT_CONFIG, - START_SERVER_UNIX, - ], - }, - { - id: INSTRUCTION_VARIANT.DEB, - instructions: [ - DOWNLOAD_SERVER_DEB, - IMPORT_DASHBOARD_UNIX, - EDIT_CONFIG, - START_SERVER_UNIX, - ], - }, - { - id: INSTRUCTION_VARIANT.RPM, - instructions: [ - DOWNLOAD_SERVER_RPM, - IMPORT_DASHBOARD_UNIX, - EDIT_CONFIG, - START_SERVER_UNIX, - ], - }, - { - id: INSTRUCTION_VARIANT.WINDOWS, - instructions: WINDOWS_SERVER_INSTRUCTIONS, - }, - ], - }, - { - title: 'APM Agents', - instructionVariants: [ - { - id: INSTRUCTION_VARIANT.NODE, - instructions: NODE_CLIENT_INSTRUCTIONS, - }, - { - id: INSTRUCTION_VARIANT.DJANGO, - instructions: DJANGO_CLIENT_INSTRUCTIONS, - }, - { - id: INSTRUCTION_VARIANT.FLASK, - instructions: FLASK_CLIENT_INSTRUCTIONS, - }, - { - id: INSTRUCTION_VARIANT.RAILS, - instructions: RAILS_CLIENT_INSTRUCTIONS, - }, - { - id: INSTRUCTION_VARIANT.RACK, - instructions: RACK_CLIENT_INSTRUCTIONS, - }, - { - id: INSTRUCTION_VARIANT.JS, - instructions: JS_CLIENT_INSTRUCTIONS, +export function onPremInstructions(server) { + let apmIndexPattern = 'apm*'; + try { + apmIndexPattern = server.config().get('xpack.apm.indexPattern'); + } catch (error) { + // ignore error when config does not contain 'xpack.apm.indexPattern'. + // This is expected when APM plugin is not running. + } + + return { + instructionSets: [ + { + title: 'APM Server', + instructionVariants: [ + { + id: INSTRUCTION_VARIANT.OSX, + instructions: [ + DOWNLOAD_SERVER_OSX, + IMPORT_DASHBOARD_UNIX, + EDIT_CONFIG, + START_SERVER_UNIX, + ], + }, + { + id: INSTRUCTION_VARIANT.DEB, + instructions: [ + DOWNLOAD_SERVER_DEB, + IMPORT_DASHBOARD_UNIX, + EDIT_CONFIG, + START_SERVER_UNIX, + ], + }, + { + id: INSTRUCTION_VARIANT.RPM, + instructions: [ + DOWNLOAD_SERVER_RPM, + IMPORT_DASHBOARD_UNIX, + EDIT_CONFIG, + START_SERVER_UNIX, + ], + }, + { + id: INSTRUCTION_VARIANT.WINDOWS, + instructions: WINDOWS_SERVER_INSTRUCTIONS, + }, + ], + statusCheck: { + success: 'You have correctly setup APM-Server', + error: 'APM-Server has still not connected to Elasticsearch', + esHitsCheck: { + index: apmIndexPattern, + cluster: 'data', + query: { + bool: { + filter: { + exists: { + field: 'processor.name', + }, + }, + }, + }, + }, }, - ], - }, - ], -}; + }, + { + title: 'APM Agents', + instructionVariants: [ + { + id: INSTRUCTION_VARIANT.NODE, + instructions: NODE_CLIENT_INSTRUCTIONS, + }, + { + id: INSTRUCTION_VARIANT.DJANGO, + instructions: DJANGO_CLIENT_INSTRUCTIONS, + }, + { + id: INSTRUCTION_VARIANT.FLASK, + instructions: FLASK_CLIENT_INSTRUCTIONS, + }, + { + id: INSTRUCTION_VARIANT.RAILS, + instructions: RAILS_CLIENT_INSTRUCTIONS, + }, + { + id: INSTRUCTION_VARIANT.RACK, + instructions: RACK_CLIENT_INSTRUCTIONS, + }, + { + id: INSTRUCTION_VARIANT.JS, + instructions: JS_CLIENT_INSTRUCTIONS, + }, + ], + }, + ], + }; +} From 07f88d507e0d159b96c7a693521fcccb2c596406 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 12 Apr 2018 10:00:48 -0600 Subject: [PATCH 02/16] add status check step to instruction set --- .../common/tutorials/tutorial_schema.js | 8 +-- .../components/tutorial/instruction_set.js | 50 +++++++++++++++++++ .../home/components/tutorial/tutorial.js | 2 + .../kibana/server/tutorials/apm/on_prem.js | 4 +- 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/core_plugins/kibana/common/tutorials/tutorial_schema.js b/src/core_plugins/kibana/common/tutorials/tutorial_schema.js index 57f5d856a4efc..58fb0ba9e14a1 100644 --- a/src/core_plugins/kibana/common/tutorials/tutorial_schema.js +++ b/src/core_plugins/kibana/common/tutorials/tutorial_schema.js @@ -26,11 +26,13 @@ const artifactsSchema = Joi.object({ }); const statusCheckSchema = Joi.object({ - success: Joi.string().required(), - error: Joi.string().required(), + title: Joi.string(), + text: Joi.string(), + btnLabel: Joi.string(), + success: Joi.string(), + error: Joi.string(), esHitsCheck: Joi.object({ index: Joi.string().required(), - cluster: Joi.string().valid(['data', 'admin']).required(), query: Joi.object().required(), }).required(), }); diff --git a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js index a8a1918506a19..8cd0507765244 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js +++ b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js @@ -13,6 +13,10 @@ import { EuiTab, EuiSpacer, EuiSteps, + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiButton, } from '@elastic/eui'; export class InstructionSet extends React.Component { @@ -60,6 +64,35 @@ export class InstructionSet extends React.Component { )); } + renderStatusCheck() { + const checkStausStep = ( + + + +

+ {this.props.statusCheck.text} +

+
+
+ + + {this.props.onStatusCheck();}} + > + {this.props.statusCheck.btnLabel ? this.props.statusCheck.btnLabel : 'Check status'} + + +
+ ); + return { + title: this.props.statusCheck.title ? this.props.statusCheck.title : 'Status Check', + children: checkStausStep, + key: 'checkStatusStep' + }; + } + renderInstructions = () => { const instructionVariant = this.props.instructionVariants.find(variant => { return variant.id === this.state.selectedTabId; @@ -79,12 +112,17 @@ export class InstructionSet extends React.Component { /> ); return { + className: 'test', title: instruction.title, children: step, key: index }; }); + if (this.props.statusCheck) { + steps.push(this.renderStatusCheck()); + } + return ( {}} offset={currentOffset} params={instructions.params} paramValues={this.state.paramValues} diff --git a/src/core_plugins/kibana/server/tutorials/apm/on_prem.js b/src/core_plugins/kibana/server/tutorials/apm/on_prem.js index f4b16a9dd0cd4..17bce3312772d 100644 --- a/src/core_plugins/kibana/server/tutorials/apm/on_prem.js +++ b/src/core_plugins/kibana/server/tutorials/apm/on_prem.js @@ -64,11 +64,13 @@ export function onPremInstructions(server) { }, ], statusCheck: { + title: 'APM Server status', + text: 'Make sure APM Server is running before you start implementing the APM agents.', + btnLabel: 'Check APM Server status', success: 'You have correctly setup APM-Server', error: 'APM-Server has still not connected to Elasticsearch', esHitsCheck: { index: apmIndexPattern, - cluster: 'data', query: { bool: { filter: { From 297610874883792beac793d6eaf79414ce803665 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 16 Apr 2018 12:30:07 -0600 Subject: [PATCH 03/16] track status check state --- .../components/tutorial/instruction_set.js | 3 +- .../home/components/tutorial/tutorial.js | 31 ++++++++++++++++--- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js index 8cd0507765244..a0ba0ea4107f2 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js +++ b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js @@ -88,6 +88,7 @@ export class InstructionSet extends React.Component { ); return { title: this.props.statusCheck.title ? this.props.statusCheck.title : 'Status Check', + status: this.props.statusCheckState, children: checkStausStep, key: 'checkStatusStep' }; @@ -226,7 +227,7 @@ InstructionSet.propTypes = { title: PropTypes.string.isRequired, instructionVariants: PropTypes.arrayOf(instructionVariantShape).isRequired, statusCheck: statusCheckShape, - statusCheckState: PropTypes.bool, + statusCheckState: PropTypes.string, onStatusCheck: PropTypes.func.isRequired, offset: PropTypes.number.isRequired, params: PropTypes.array, diff --git a/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js b/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js index 25fabdca694e5..5bb08eea21c94 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js +++ b/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js @@ -22,6 +22,7 @@ export class Tutorial extends React.Component { this.state = { notFound: false, paramValues: {}, + statusCheck: [], // array holding instruction set status check state tutorial: null }; @@ -51,7 +52,7 @@ export class Tutorial extends React.Component { // eslint-disable-next-line react/no-did-mount-set-state this.setState({ tutorial: tutorial - }, this.setParamDefaults); + }, this.initInstructionsState); } else { // eslint-disable-next-line react/no-did-mount-set-state this.setState({ @@ -77,23 +78,33 @@ export class Tutorial extends React.Component { } } - setParamDefaults = () => { + initInstructionsState = () => { const instructions = this.getInstructions(); + const paramValues = {}; if (instructions.params) { instructions.params.forEach((param => { paramValues[param.id] = param.defaultValue; })); } + + const statusCheck = instructions.instructionSets.map((instructionSet) => { + return { + hasStatusCheck: instructionSet.statusCheck ? true : false, + isComplete: false, + }; + }); + this.setState({ - paramValues: paramValues + paramValues, + statusCheck, }); } setVisibleInstructions = (instructionsType) => { this.setState({ visibleInstructions: instructionsType - }, this.setParamDefaults); + }, this.initInstructionsState); } setParameter = (paramId, newValue) => { @@ -132,12 +143,22 @@ export class Tutorial extends React.Component { return instructions.instructionSets.map((instructionSet, index) => { const currentOffset = offset; offset += instructionSet.instructionVariants[0].instructions.length; + let statusCheckState = undefined; + if (_.get(this.state, `statusCheck[${index}].hasStatusCheck`, false)) { + statusCheckState = this.state.statusCheck[index].isComplete ? 'complete' : 'incomplete'; + } return ( {}} + statusCheckState={statusCheckState} + onStatusCheck={() => { + this.setState((prevState) => { + prevState.statusCheck[index].isComplete = true; + return { statusCheck: prevState.statusCheck }; + }); + }} offset={currentOffset} params={instructions.params} paramValues={this.state.paramValues} From 4a59a285a047ef8ff9a56033443ff7257ba789ac Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 16 Apr 2018 13:35:50 -0600 Subject: [PATCH 04/16] query elasticsearch for hits --- .../components/tutorial/instruction_set.js | 2 + .../home/components/tutorial/tutorial.js | 44 +++++++++++++++++-- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js index a0ba0ea4107f2..35e88ea6c4a7c 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js +++ b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js @@ -80,6 +80,7 @@ export class InstructionSet extends React.Component { > {this.props.onStatusCheck();}} + isLoading={this.props.isCheckingStatus} > {this.props.statusCheck.btnLabel ? this.props.statusCheck.btnLabel : 'Check status'} @@ -229,6 +230,7 @@ InstructionSet.propTypes = { statusCheck: statusCheckShape, statusCheckState: PropTypes.string, onStatusCheck: PropTypes.func.isRequired, + isCheckingStatus: PropTypes.bool, offset: PropTypes.number.isRequired, params: PropTypes.array, paramValues: PropTypes.object.isRequired, diff --git a/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js b/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js index 5bb08eea21c94..40d0b8650f53b 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js +++ b/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js @@ -92,6 +92,7 @@ export class Tutorial extends React.Component { return { hasStatusCheck: instructionSet.statusCheck ? true : false, isComplete: false, + isFetchingStatus: false, }; }); @@ -115,6 +116,40 @@ export class Tutorial extends React.Component { }); } + checkInstructionSetStatus = async (instructionSetIndex) => { + const instructions = this.getInstructions(); + const esHitsCheckConfig = _.get(instructions, `instructionSets[${instructionSetIndex}].statusCheck.esHitsCheck`); + if (!esHitsCheckConfig) { + return; + } + + const searchHeader = JSON.stringify({ index: esHitsCheckConfig.index }); + const searchBody = JSON.stringify({ query: esHitsCheckConfig.query, size: 1 }); + const body = `${searchHeader}\n${searchBody}`; + + const response = await fetch(this.props.addBasePath('/elasticsearch/_msearch'), { + method: 'post', + body: body, + headers: { + accept: 'application/json', + 'content-type': 'application/x-ndjson', + 'kbn-xsrf': 'kibana', + }, + credentials: 'same-origin' + }); + + let hasHits = false; + if (response.status < 300) { + + } + + this.setState((prevState) => { + prevState.statusCheck[instructionSetIndex].isComplete = hasHits; + prevState.statusCheck[instructionSetIndex].hasFailed = !hasHits; + return { statusCheck: prevState.statusCheck }; + }); + } + onPrem = () => { this.setVisibleInstructions(INSTRUCTIONS_TYPE.ON_PREM); } @@ -144,8 +179,10 @@ export class Tutorial extends React.Component { const currentOffset = offset; offset += instructionSet.instructionVariants[0].instructions.length; let statusCheckState = undefined; + let isCheckingStatus = false; if (_.get(this.state, `statusCheck[${index}].hasStatusCheck`, false)) { statusCheckState = this.state.statusCheck[index].isComplete ? 'complete' : 'incomplete'; + isCheckingStatus = this.state.statusCheck[index].isFetchingStatus; } return ( { this.setState((prevState) => { - prevState.statusCheck[index].isComplete = true; + prevState.statusCheck[index].isFetchingStatus = true; return { statusCheck: prevState.statusCheck }; - }); + }, this.checkInstructionSetStatus.bind(null, index)); }} offset={currentOffset} params={instructions.params} @@ -261,5 +299,5 @@ Tutorial.propTypes = { isCloudEnabled: PropTypes.bool.isRequired, getTutorial: PropTypes.func.isRequired, replaceTemplateStrings: PropTypes.func.isRequired, - tutorialId: PropTypes.string.isRequired + tutorialId: PropTypes.string.isRequired, }; From 84fab71b47eb146a473cf95c66590c674afc90aa Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 16 Apr 2018 16:37:01 -0600 Subject: [PATCH 05/16] display message when status check completes --- .../components/tutorial/instruction_set.js | 37 +++++++++--- .../home/components/tutorial/tutorial.js | 56 +++++++++++-------- 2 files changed, 62 insertions(+), 31 deletions(-) diff --git a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js index 35e88ea6c4a7c..46a94c3c5afbc 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js +++ b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js @@ -17,6 +17,7 @@ import { EuiFlexItem, EuiText, EuiButton, + EuiTextColor, } from '@elastic/eui'; export class InstructionSet extends React.Component { @@ -64,13 +65,34 @@ export class InstructionSet extends React.Component { )); } + renderStatusCheckMsg(msg, color) { + return ( + +

+ + {msg} + +

+
+ ); + } + renderStatusCheck() { + let statusMsg; + if (this.props.statusCheckState === 'complete') { + const msg = this.props.statusCheckConfig.success ? this.props.statusCheckConfig.success : 'Success'; + statusMsg = this.renderStatusCheckMsg(msg, 'secondary'); + } else if (this.props.hasStatusCheckFailed) { + const msg = this.props.statusCheckConfig.error ? this.props.statusCheckConfig.error : 'No data found'; + statusMsg = this.renderStatusCheckMsg(msg, 'warning'); + } + const checkStausStep = (

- {this.props.statusCheck.text} + {this.props.statusCheckConfig.text}

@@ -82,13 +104,14 @@ export class InstructionSet extends React.Component { onClick={() => {this.props.onStatusCheck();}} isLoading={this.props.isCheckingStatus} > - {this.props.statusCheck.btnLabel ? this.props.statusCheck.btnLabel : 'Check status'} + {this.props.statusCheckConfig.btnLabel ? this.props.statusCheckConfig.btnLabel : 'Check status'} + {statusMsg}
); return { - title: this.props.statusCheck.title ? this.props.statusCheck.title : 'Status Check', + title: this.props.statusCheckConfig.title ? this.props.statusCheckConfig.title : 'Status Check', status: this.props.statusCheckState, children: checkStausStep, key: 'checkStatusStep' @@ -121,7 +144,7 @@ export class InstructionSet extends React.Component { }; }); - if (this.props.statusCheck) { + if (this.props.statusCheckConfig) { steps.push(this.renderStatusCheck()); } @@ -215,22 +238,22 @@ const instructionVariantShape = PropTypes.shape({ instructions: PropTypes.arrayOf(instructionShape).isRequired, }); -const statusCheckShape = PropTypes.shape({ +const statusCheckConfigShape = PropTypes.shape({ success: PropTypes.string, error: PropTypes.string, title: PropTypes.string, text: PropTypes.string, btnLabel: PropTypes.string, - esHitsCheck: PropTypes.object.isRequired, }); InstructionSet.propTypes = { title: PropTypes.string.isRequired, instructionVariants: PropTypes.arrayOf(instructionVariantShape).isRequired, - statusCheck: statusCheckShape, + statusCheckConfig: statusCheckConfigShape, statusCheckState: PropTypes.string, onStatusCheck: PropTypes.func.isRequired, isCheckingStatus: PropTypes.bool, + hasStatusCheckFailed: PropTypes.bool, offset: PropTypes.number.isRequired, params: PropTypes.array, paramValues: PropTypes.object.isRequired, diff --git a/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js b/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js index 40d0b8650f53b..69d5e214e12e1 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js +++ b/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js @@ -92,6 +92,7 @@ export class Tutorial extends React.Component { return { hasStatusCheck: instructionSet.statusCheck ? true : false, isComplete: false, + hasFailed: false, isFetchingStatus: false, }; }); @@ -117,35 +118,39 @@ export class Tutorial extends React.Component { } checkInstructionSetStatus = async (instructionSetIndex) => { + let isComplete = false; + let hasFailed = false; const instructions = this.getInstructions(); const esHitsCheckConfig = _.get(instructions, `instructionSets[${instructionSetIndex}].statusCheck.esHitsCheck`); - if (!esHitsCheckConfig) { - return; - } - - const searchHeader = JSON.stringify({ index: esHitsCheckConfig.index }); - const searchBody = JSON.stringify({ query: esHitsCheckConfig.query, size: 1 }); - const body = `${searchHeader}\n${searchBody}`; - - const response = await fetch(this.props.addBasePath('/elasticsearch/_msearch'), { - method: 'post', - body: body, - headers: { - accept: 'application/json', - 'content-type': 'application/x-ndjson', - 'kbn-xsrf': 'kibana', - }, - credentials: 'same-origin' - }); - - let hasHits = false; - if (response.status < 300) { + if (esHitsCheckConfig) { + const searchHeader = JSON.stringify({ index: esHitsCheckConfig.index }); + const searchBody = JSON.stringify({ query: esHitsCheckConfig.query, size: 1 }); + const body = `${searchHeader}\n${searchBody}\n`; + + const response = await fetch(this.props.addBasePath('/elasticsearch/_msearch'), { + method: 'post', + body: body, + headers: { + accept: 'application/json', + 'content-type': 'application/x-ndjson', + 'kbn-xsrf': 'kibana', + }, + credentials: 'same-origin' + }); + if (response.status < 300) { + const results = await response.json(); + if (_.get(results, 'responses.[0].hits.hits.length', 0) > 0) { + isComplete = true; + } + } + hasFailed = !isComplete; } this.setState((prevState) => { - prevState.statusCheck[instructionSetIndex].isComplete = hasHits; - prevState.statusCheck[instructionSetIndex].hasFailed = !hasHits; + prevState.statusCheck[instructionSetIndex].isComplete = isComplete; + prevState.statusCheck[instructionSetIndex].hasFailed = hasFailed; + prevState.statusCheck[instructionSetIndex].isFetchingStatus = false; return { statusCheck: prevState.statusCheck }; }); } @@ -180,17 +185,20 @@ export class Tutorial extends React.Component { offset += instructionSet.instructionVariants[0].instructions.length; let statusCheckState = undefined; let isCheckingStatus = false; + let hasFailed = false; if (_.get(this.state, `statusCheck[${index}].hasStatusCheck`, false)) { statusCheckState = this.state.statusCheck[index].isComplete ? 'complete' : 'incomplete'; isCheckingStatus = this.state.statusCheck[index].isFetchingStatus; + hasFailed = this.state.statusCheck[index].hasFailed; } return ( { this.setState((prevState) => { prevState.statusCheck[index].isFetchingStatus = true; From 9742ccde19f11ec77ef895dc6d49968513065f78 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 16 Apr 2018 16:56:29 -0600 Subject: [PATCH 06/16] clean up --- .../kibana/public/home/components/tutorial/instruction_set.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js index 46a94c3c5afbc..db5d45d888215 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js +++ b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js @@ -89,7 +89,7 @@ export class InstructionSet extends React.Component { const checkStausStep = ( - +

{this.props.statusCheckConfig.text} @@ -137,7 +137,6 @@ export class InstructionSet extends React.Component { /> ); return { - className: 'test', title: instruction.title, children: step, key: index From 48fe20a71a5d884f3c6c0612415e442800dccfab Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 27 Apr 2018 12:30:51 -0600 Subject: [PATCH 07/16] use callout to display status check results --- .../components/tutorial/instruction_set.js | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js index db5d45d888215..30f2f17406a4b 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js +++ b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js @@ -1,5 +1,5 @@ import classNames from 'classnames'; -import React from 'react'; +import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import { KuiBar, @@ -17,7 +17,7 @@ import { EuiFlexItem, EuiText, EuiButton, - EuiTextColor, + EuiCallOut, } from '@elastic/eui'; export class InstructionSet extends React.Component { @@ -67,13 +67,10 @@ export class InstructionSet extends React.Component { renderStatusCheckMsg(msg, color) { return ( - -

- - {msg} - -

-
+ ); } @@ -81,34 +78,39 @@ export class InstructionSet extends React.Component { let statusMsg; if (this.props.statusCheckState === 'complete') { const msg = this.props.statusCheckConfig.success ? this.props.statusCheckConfig.success : 'Success'; - statusMsg = this.renderStatusCheckMsg(msg, 'secondary'); + statusMsg = this.renderStatusCheckMsg(msg, 'success'); } else if (this.props.hasStatusCheckFailed) { const msg = this.props.statusCheckConfig.error ? this.props.statusCheckConfig.error : 'No data found'; statusMsg = this.renderStatusCheckMsg(msg, 'warning'); } const checkStausStep = ( - - - -

- {this.props.statusCheckConfig.text} -

-
-
- - - {this.props.onStatusCheck();}} - isLoading={this.props.isCheckingStatus} + + + + +

+ {this.props.statusCheckConfig.text} +

+
+
+ + - {this.props.statusCheckConfig.btnLabel ? this.props.statusCheckConfig.btnLabel : 'Check status'} -
- {statusMsg} -
-
+ {this.props.onStatusCheck();}} + isLoading={this.props.isCheckingStatus} + > + {this.props.statusCheckConfig.btnLabel ? this.props.statusCheckConfig.btnLabel : 'Check status'} + +
+
+ + + + {statusMsg} + ); return { title: this.props.statusCheckConfig.title ? this.props.statusCheckConfig.title : 'Status Check', From a9e9b8131362ba49c24773060ae3fa2808a5e38d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Mon, 30 Apr 2018 13:41:55 +0200 Subject: [PATCH 08/16] Updated status check --- .../kibana/server/tutorials/apm/on_prem.js | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/core_plugins/kibana/server/tutorials/apm/on_prem.js b/src/core_plugins/kibana/server/tutorials/apm/on_prem.js index 17bce3312772d..42d12c3a321f1 100644 --- a/src/core_plugins/kibana/server/tutorials/apm/on_prem.js +++ b/src/core_plugins/kibana/server/tutorials/apm/on_prem.js @@ -65,7 +65,8 @@ export function onPremInstructions(server) { ], statusCheck: { title: 'APM Server status', - text: 'Make sure APM Server is running before you start implementing the APM agents.', + text: + 'Make sure APM Server is running before you start implementing the APM agents.', btnLabel: 'Check APM Server status', success: 'You have correctly setup APM-Server', error: 'APM-Server has still not connected to Elasticsearch', @@ -75,7 +76,7 @@ export function onPremInstructions(server) { bool: { filter: { exists: { - field: 'processor.name', + field: 'listening', }, }, }, @@ -111,6 +112,26 @@ export function onPremInstructions(server) { instructions: JS_CLIENT_INSTRUCTIONS, }, ], + statusCheck: { + title: 'Agent status', + text: + 'Make sure you application is running, and the agents are sending data', + btnLabel: 'Check agent status', + success: 'Data succesfully received from one or more agents', + error: `No data has been received from agents yet`, + esHitsCheck: { + index: apmIndexPattern, + query: { + bool: { + filter: { + exists: { + field: 'processor.name', + }, + }, + }, + }, + }, + }, }, ], }; From 47ad8311ed68c3c20e613782c67d3f34e9f9af57 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 30 Apr 2018 07:46:14 -0600 Subject: [PATCH 09/16] update tutorial snapshot --- .../tutorial/__snapshots__/tutorial.test.js.snap | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/tutorial.test.js.snap b/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/tutorial.test.js.snap index f765acc65c36c..70727a8e7c3d6 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/tutorial.test.js.snap +++ b/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/tutorial.test.js.snap @@ -42,6 +42,7 @@ exports[`isCloudEnabled is false should not render instruction toggle when ON_PR paddingSize="l" > Date: Mon, 30 Apr 2018 08:06:12 -0600 Subject: [PATCH 10/16] add jest tests for InstructionSet component --- .../instruction_set.test.js.snap | 595 ++++++++++++++++++ .../tutorial/instruction_set.test.js | 118 ++++ 2 files changed, 713 insertions(+) create mode 100644 src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/instruction_set.test.js.snap create mode 100644 src/core_plugins/kibana/public/home/components/tutorial/instruction_set.test.js diff --git a/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/instruction_set.test.js.snap b/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/instruction_set.test.js.snap new file mode 100644 index 0000000000000..8735757a0766f --- /dev/null +++ b/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/instruction_set.test.js.snap @@ -0,0 +1,595 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`render 1`] = ` +
+ + +
+ title1 +
+
+ +
+ + + OSX + + + Windows + + + + , + "key": 0, + "title": "step 1", + }, + Object { + "children": , + "key": 1, + "title": "step 2", + }, + ] + } + /> +
+`; + +exports[`status check checking status 1`] = ` +
+ + +
+ title1 +
+
+ +
+ + + OSX + + + Windows + + + + , + "key": 0, + "title": "step 1", + }, + Object { + "children": , + "key": 1, + "title": "step 2", + }, + Object { + "children": + + + +

+ custom status check description +

+
+
+ + + custom btn label + + +
+ +
, + "key": "checkStatusStep", + "status": "incomplete", + "title": "custom title", + }, + ] + } + /> +
+`; + +exports[`status check failed status check 1`] = ` +
+ + +
+ title1 +
+
+ +
+ + + OSX + + + Windows + + + + , + "key": 0, + "title": "step 1", + }, + Object { + "children": , + "key": 1, + "title": "step 2", + }, + Object { + "children": + + + +

+ custom status check description +

+
+
+ + + custom btn label + + +
+ + +
, + "key": "checkStatusStep", + "status": "incomplete", + "title": "custom title", + }, + ] + } + /> +
+`; + +exports[`status check initial state - no check has been attempted 1`] = ` +
+ + +
+ title1 +
+
+ +
+ + + OSX + + + Windows + + + + , + "key": 0, + "title": "step 1", + }, + Object { + "children": , + "key": 1, + "title": "step 2", + }, + Object { + "children": + + + +

+ custom status check description +

+
+
+ + + custom btn label + + +
+ +
, + "key": "checkStatusStep", + "status": "incomplete", + "title": "custom title", + }, + ] + } + /> +
+`; + +exports[`status check successful status check 1`] = ` +
+ + +
+ title1 +
+
+ +
+ + + OSX + + + Windows + + + + , + "key": 0, + "title": "step 1", + }, + Object { + "children": , + "key": 1, + "title": "step 2", + }, + Object { + "children": + + + +

+ custom status check description +

+
+
+ + + custom btn label + + +
+ + +
, + "key": "checkStatusStep", + "status": "complete", + "title": "custom title", + }, + ] + } + /> +
+`; diff --git a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.test.js b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.test.js new file mode 100644 index 0000000000000..4311c8593e36e --- /dev/null +++ b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.test.js @@ -0,0 +1,118 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import { + InstructionSet, +} from './instruction_set'; + +const instructions = [ + { + title: 'step 1', + commands: [ + 'do stuff in command line', + ], + }, + { + title: 'step 2', + commands: [ + 'do more stuff in command line', + ], + } +]; + +const instructionVariants = [ + { + id: 'OSX', + instructions: instructions + }, + { + id: 'windows', + instructions: instructions, + } +]; + +test('render', () => { + const component = shallow( {}} + offset={1} + paramValues={{}} + replaceTemplateStrings={() => {}} + />); + expect(component).toMatchSnapshot(); // eslint-disable-line +}); + +describe('status check', () => { + const statusCheckConfig = { + success: 'custom success msg', + error: 'custom error msg', + title: 'custom title', + text: 'custom status check description', + btnLabel: 'custom btn label', + }; + + test('initial state - no check has been attempted', () => { + const component = shallow( {}} + offset={1} + paramValues={{}} + statusCheckConfig={statusCheckConfig} + replaceTemplateStrings={() => {}} + statusCheckState="incomplete" + hasStatusCheckFailed={false} + isCheckingStatus={false} + />); + expect(component).toMatchSnapshot(); // eslint-disable-line + }); + + test('checking status', () => { + const component = shallow( {}} + offset={1} + paramValues={{}} + statusCheckConfig={statusCheckConfig} + replaceTemplateStrings={() => {}} + statusCheckState="incomplete" + hasStatusCheckFailed={false} + isCheckingStatus={true} + />); + expect(component).toMatchSnapshot(); // eslint-disable-line + }); + + test('failed status check', () => { + const component = shallow( {}} + offset={1} + paramValues={{}} + statusCheckConfig={statusCheckConfig} + replaceTemplateStrings={() => {}} + statusCheckState="incomplete" + hasStatusCheckFailed={true} + isCheckingStatus={false} + />); + expect(component).toMatchSnapshot(); // eslint-disable-line + }); + + test('successful status check', () => { + const component = shallow( {}} + offset={1} + paramValues={{}} + statusCheckConfig={statusCheckConfig} + replaceTemplateStrings={() => {}} + statusCheckState="complete" + hasStatusCheckFailed={false} + isCheckingStatus={false} + />); + expect(component).toMatchSnapshot(); // eslint-disable-line + }); +}); From dbd57f8d6f7a46b88ad32f6dbd2baaec70cc30c1 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 1 May 2018 08:47:55 -0600 Subject: [PATCH 11/16] pass function as prop instead of wrapping in new function --- .../kibana/public/home/components/tutorial/instruction_set.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js index 30f2f17406a4b..e199005e841ab 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js +++ b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js @@ -99,7 +99,7 @@ export class InstructionSet extends React.Component { grow={false} > {this.props.onStatusCheck();}} + onClick={this.props.onStatusCheck} isLoading={this.props.isCheckingStatus} > {this.props.statusCheckConfig.btnLabel ? this.props.statusCheckConfig.btnLabel : 'Check status'} From 30245f1a57ca435dafe025fd427233d61371eabb Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 1 May 2018 13:16:16 -0600 Subject: [PATCH 12/16] refactor checkInstructionSetStatus --- .../components/tutorial/instruction_set.js | 4 +- .../home/components/tutorial/tutorial.js | 66 ++++++++++--------- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js index e199005e841ab..ca4c220bb950b 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js +++ b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js @@ -102,7 +102,7 @@ export class InstructionSet extends React.Component { onClick={this.props.onStatusCheck} isLoading={this.props.isCheckingStatus} > - {this.props.statusCheckConfig.btnLabel ? this.props.statusCheckConfig.btnLabel : 'Check status'} + {this.props.statusCheckConfig.btnLabel || 'Check status'} @@ -113,7 +113,7 @@ export class InstructionSet extends React.Component { ); return { - title: this.props.statusCheckConfig.title ? this.props.statusCheckConfig.title : 'Status Check', + title: this.props.statusCheckConfig.title || 'Status Check', status: this.props.statusCheckState, children: checkStausStep, key: 'checkStatusStep' diff --git a/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js b/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js index 69d5e214e12e1..8e24934237759 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js +++ b/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js @@ -22,7 +22,7 @@ export class Tutorial extends React.Component { this.state = { notFound: false, paramValues: {}, - statusCheck: [], // array holding instruction set status check state + statusCheck: [], tutorial: null }; @@ -118,41 +118,47 @@ export class Tutorial extends React.Component { } checkInstructionSetStatus = async (instructionSetIndex) => { - let isComplete = false; - let hasFailed = false; const instructions = this.getInstructions(); const esHitsCheckConfig = _.get(instructions, `instructionSets[${instructionSetIndex}].statusCheck.esHitsCheck`); - if (esHitsCheckConfig) { - const searchHeader = JSON.stringify({ index: esHitsCheckConfig.index }); - const searchBody = JSON.stringify({ query: esHitsCheckConfig.query, size: 1 }); - const body = `${searchHeader}\n${searchBody}\n`; - - const response = await fetch(this.props.addBasePath('/elasticsearch/_msearch'), { - method: 'post', - body: body, - headers: { - accept: 'application/json', - 'content-type': 'application/x-ndjson', - 'kbn-xsrf': 'kibana', - }, - credentials: 'same-origin' - }); + const { hasFailed, isComplete } = await this.fetchEsHitsStatus(esHitsCheckConfig); - if (response.status < 300) { - const results = await response.json(); - if (_.get(results, 'responses.[0].hits.hits.length', 0) > 0) { - isComplete = true; - } - } - hasFailed = !isComplete; + this.setState((prevState) => { + const statusCheck = _.cloneDeep(prevState.statusCheck); + statusCheck[instructionSetIndex].isComplete = isComplete; + statusCheck[instructionSetIndex].hasFailed = hasFailed; + statusCheck[instructionSetIndex].isFetchingStatus = false; + return { statusCheck }; + }); + } + + fetchEsHitsStatus = async (esHitsCheckConfig) => { + if (!esHitsCheckConfig) { + return { hasFailed: false, isComplete: false }; } - this.setState((prevState) => { - prevState.statusCheck[instructionSetIndex].isComplete = isComplete; - prevState.statusCheck[instructionSetIndex].hasFailed = hasFailed; - prevState.statusCheck[instructionSetIndex].isFetchingStatus = false; - return { statusCheck: prevState.statusCheck }; + const searchHeader = JSON.stringify({ index: esHitsCheckConfig.index }); + const searchBody = JSON.stringify({ query: esHitsCheckConfig.query, size: 1 }); + const response = await fetch(this.props.addBasePath('/elasticsearch/_msearch'), { + method: 'post', + body: `${searchHeader}\n${searchBody}\n`, + headers: { + accept: 'application/json', + 'content-type': 'application/x-ndjson', + 'kbn-xsrf': 'kibana', + }, + credentials: 'same-origin' }); + + if (response.status > 300) { + return { hasFailed: true, isComplete: false }; + } + + const results = await response.json(); + const numHits = _.get(results, 'responses.[0].hits.hits.length', 0); + return { + hasFailed: numHits === 0, + isComplete: numHits > 0 + }; } onPrem = () => { From d5176f16980057c8d148c79a828566bbed3cb483 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 14 May 2018 10:11:03 -0600 Subject: [PATCH 13/16] update snapshots that broke after rebase --- .../tutorial/__snapshots__/instruction_set.test.js.snap | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/instruction_set.test.js.snap b/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/instruction_set.test.js.snap index 8735757a0766f..9cbd029e594ff 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/instruction_set.test.js.snap +++ b/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/instruction_set.test.js.snap @@ -159,6 +159,7 @@ exports[`status check checking status 1`] = ` Date: Tue, 15 May 2018 12:48:17 -0400 Subject: [PATCH 14/16] Suggested changes (#24) --- .../components/tutorial/instruction_set.js | 68 ++++++----- .../tutorial/status_check_states.js | 5 + .../home/components/tutorial/tutorial.js | 108 +++++++++--------- 3 files changed, 98 insertions(+), 83 deletions(-) create mode 100644 src/core_plugins/kibana/public/home/components/tutorial/status_check_states.js diff --git a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js index ca4c220bb950b..b8e585bbbff9f 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js +++ b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.js @@ -19,6 +19,7 @@ import { EuiButton, EuiCallOut, } from '@elastic/eui'; +import * as StatusCheckStates from './status_check_states'; export class InstructionSet extends React.Component { @@ -63,34 +64,42 @@ export class InstructionSet extends React.Component { {tab.name} )); - } + }; - renderStatusCheckMsg(msg, color) { + renderStatusCheckMessage() { + let message; + let color; + switch (this.props.statusCheckState) { + case StatusCheckStates.NOT_CHECKED: + case StatusCheckStates.FETCHING: + return null; // Don't show any message while fetching or if you haven't yet checked. + case StatusCheckStates.HAS_DATA: + message = this.props.statusCheckConfig.success ? this.props.statusCheckConfig.success : 'Success'; + color = 'success'; + break; + case StatusCheckStates.ERROR: + case StatusCheckStates.NO_DATA: + message = this.props.statusCheckConfig.error ? this.props.statusCheckConfig.error : 'No data found'; + color = 'warning'; + break; + } return ( ); } renderStatusCheck() { - let statusMsg; - if (this.props.statusCheckState === 'complete') { - const msg = this.props.statusCheckConfig.success ? this.props.statusCheckConfig.success : 'Success'; - statusMsg = this.renderStatusCheckMsg(msg, 'success'); - } else if (this.props.hasStatusCheckFailed) { - const msg = this.props.statusCheckConfig.error ? this.props.statusCheckConfig.error : 'No data found'; - statusMsg = this.renderStatusCheckMsg(msg, 'warning'); - } - - const checkStausStep = ( + const { statusCheckState, statusCheckConfig, onStatusCheck } = this.props; + const checkStatusStep = (

- {this.props.statusCheckConfig.text} + {statusCheckConfig.text}

@@ -99,23 +108,26 @@ export class InstructionSet extends React.Component { grow={false} > - {this.props.statusCheckConfig.btnLabel || 'Check status'} + {statusCheckConfig.btnLabel || 'Check status'}
- {statusMsg} + {this.renderStatusCheckMessage()}
); + + const stepStatus = statusCheckState === StatusCheckStates.NOT_CHECKED || + statusCheckState === StatusCheckStates.FETCHING ? 'incomplete' : 'complete'; return { - title: this.props.statusCheckConfig.title || 'Status Check', - status: this.props.statusCheckState, - children: checkStausStep, + title: statusCheckConfig.title || 'Status Check', + status: stepStatus, + children: checkStatusStep, key: 'checkStatusStep' }; } @@ -155,7 +167,7 @@ export class InstructionSet extends React.Component { firstStepNumber={this.props.offset} /> ); - } + }; renderHeader = () => { let paramsVisibilityToggle; @@ -193,7 +205,7 @@ export class InstructionSet extends React.Component { ); - } + }; render() { let paramsForm; @@ -251,10 +263,14 @@ InstructionSet.propTypes = { title: PropTypes.string.isRequired, instructionVariants: PropTypes.arrayOf(instructionVariantShape).isRequired, statusCheckConfig: statusCheckConfigShape, - statusCheckState: PropTypes.string, + statusCheckState: PropTypes.oneOf([ + StatusCheckStates.FETCHING, + StatusCheckStates.NOT_CHECKED, + StatusCheckStates.HAS_DATA, + StatusCheckStates.NO_DATA, + StatusCheckStates.ERROR, + ]), onStatusCheck: PropTypes.func.isRequired, - isCheckingStatus: PropTypes.bool, - hasStatusCheckFailed: PropTypes.bool, offset: PropTypes.number.isRequired, params: PropTypes.array, paramValues: PropTypes.object.isRequired, diff --git a/src/core_plugins/kibana/public/home/components/tutorial/status_check_states.js b/src/core_plugins/kibana/public/home/components/tutorial/status_check_states.js new file mode 100644 index 0000000000000..5ae6f4a16faa3 --- /dev/null +++ b/src/core_plugins/kibana/public/home/components/tutorial/status_check_states.js @@ -0,0 +1,5 @@ +export const HAS_DATA = 'has_data'; +export const FETCHING = 'FETCHING'; +export const NO_DATA = 'NO_DATA'; +export const NOT_CHECKED = 'NOT_CHECKED'; +export const ERROR = 'ERROR'; diff --git a/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js b/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js index 8e24934237759..2ed2285929e4e 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js +++ b/src/core_plugins/kibana/public/home/components/tutorial/tutorial.js @@ -7,6 +7,7 @@ import { Introduction } from './introduction'; import { InstructionSet } from './instruction_set'; import { RadioButtonGroup } from './radio_button_group'; import { EuiSpacer, EuiPage, EuiPanel, EuiLink, EuiText } from '@elastic/eui'; +import * as StatusCheckStates from './status_check_states'; const INSTRUCTIONS_TYPE = { ELASTIC_CLOUD: 'elasticCloud', @@ -22,7 +23,7 @@ export class Tutorial extends React.Component { this.state = { notFound: false, paramValues: {}, - statusCheck: [], + statusCheckStates: [], tutorial: null }; @@ -76,7 +77,9 @@ export class Tutorial extends React.Component { default: throw new Error(`Unhandled instruction type ${this.state.visibleInstructions}`); } - } + }; + + getInstructionSets = () => this.getInstructions().instructionSets; initInstructionsState = () => { const instructions = this.getInstructions(); @@ -88,26 +91,19 @@ export class Tutorial extends React.Component { })); } - const statusCheck = instructions.instructionSets.map((instructionSet) => { - return { - hasStatusCheck: instructionSet.statusCheck ? true : false, - isComplete: false, - hasFailed: false, - isFetchingStatus: false, - }; - }); + const statusCheckStates = new Array(instructions.instructionSets.length).fill(StatusCheckStates.NOT_CHECKED); this.setState({ paramValues, - statusCheck, + statusCheckStates, }); - } + }; setVisibleInstructions = (instructionsType) => { this.setState({ visibleInstructions: instructionsType }, this.initInstructionsState); - } + }; setParameter = (paramId, newValue) => { this.setState(previousState => { @@ -115,27 +111,30 @@ export class Tutorial extends React.Component { paramValues[paramId] = newValue; return { paramValues: paramValues }; }); - } + }; checkInstructionSetStatus = async (instructionSetIndex) => { - const instructions = this.getInstructions(); - const esHitsCheckConfig = _.get(instructions, `instructionSets[${instructionSetIndex}].statusCheck.esHitsCheck`); - const { hasFailed, isComplete } = await this.fetchEsHitsStatus(esHitsCheckConfig); - - this.setState((prevState) => { - const statusCheck = _.cloneDeep(prevState.statusCheck); - statusCheck[instructionSetIndex].isComplete = isComplete; - statusCheck[instructionSetIndex].hasFailed = hasFailed; - statusCheck[instructionSetIndex].isFetchingStatus = false; - return { statusCheck }; - }); - } + const instructionSet = this.getInstructionSets()[instructionSetIndex]; + const esHitsCheckConfig = _.get(instructionSet, `statusCheck.esHitsCheck`); - fetchEsHitsStatus = async (esHitsCheckConfig) => { - if (!esHitsCheckConfig) { - return { hasFailed: false, isComplete: false }; + if (esHitsCheckConfig) { + const statusCheckState = await this.fetchEsHitsStatus(esHitsCheckConfig); + + this.setState((prevState) => ({ + statusCheckStates: { + ...prevState.statusCheckStates, + [instructionSetIndex]: statusCheckState, + } + })); } + }; + /** + * + * @param esHitsCheckConfig + * @return {Promise} + */ + fetchEsHitsStatus = async (esHitsCheckConfig) => { const searchHeader = JSON.stringify({ index: esHitsCheckConfig.index }); const searchBody = JSON.stringify({ query: esHitsCheckConfig.query, size: 1 }); const response = await fetch(this.props.addBasePath('/elasticsearch/_msearch'), { @@ -150,24 +149,21 @@ export class Tutorial extends React.Component { }); if (response.status > 300) { - return { hasFailed: true, isComplete: false }; + return StatusCheckStates.ERROR; } const results = await response.json(); const numHits = _.get(results, 'responses.[0].hits.hits.length', 0); - return { - hasFailed: numHits === 0, - isComplete: numHits > 0 - }; - } + return numHits === 0 ? StatusCheckStates.NO_DATA : StatusCheckStates.HAS_DATA; + }; onPrem = () => { this.setVisibleInstructions(INSTRUCTIONS_TYPE.ON_PREM); - } + }; onPremElasticCloud = () => { this.setVisibleInstructions(INSTRUCTIONS_TYPE.ON_PREM_ELASTIC_CLOUD); - } + }; renderInstructionSetsToggle = () => { if (!this.props.isCloudEnabled && this.state.tutorial.onPremElasticCloud) { @@ -182,35 +178,33 @@ export class Tutorial extends React.Component { /> ); } - } + }; + + onStatusCheck = (instructionSetIndex) => { + this.setState( + (prevState) => ({ + statusCheckStates: { + ...prevState.statusCheckStates, + [instructionSetIndex]: StatusCheckStates.FETCHING, + } + }), + this.checkInstructionSetStatus.bind(null, instructionSetIndex) + ); + }; renderInstructionSets = (instructions) => { let offset = 1; return instructions.instructionSets.map((instructionSet, index) => { const currentOffset = offset; offset += instructionSet.instructionVariants[0].instructions.length; - let statusCheckState = undefined; - let isCheckingStatus = false; - let hasFailed = false; - if (_.get(this.state, `statusCheck[${index}].hasStatusCheck`, false)) { - statusCheckState = this.state.statusCheck[index].isComplete ? 'complete' : 'incomplete'; - isCheckingStatus = this.state.statusCheck[index].isFetchingStatus; - hasFailed = this.state.statusCheck[index].hasFailed; - } + return ( { - this.setState((prevState) => { - prevState.statusCheck[index].isFetchingStatus = true; - return { statusCheck: prevState.statusCheck }; - }, this.checkInstructionSetStatus.bind(null, index)); - }} + statusCheckState={this.state.statusCheckStates[index]} + onStatusCheck={() => { this.onStatusCheck(index); }} offset={currentOffset} params={instructions.params} paramValues={this.state.paramValues} @@ -220,7 +214,7 @@ export class Tutorial extends React.Component { /> ); }); - } + }; renderFooter = () => { let label; @@ -246,7 +240,7 @@ export class Tutorial extends React.Component { /> ); } - } + }; render() { let content; From 101a8d1b617144a223fc1140cd9982aaf0cf7a51 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 15 May 2018 12:23:40 -0600 Subject: [PATCH 15/16] update jest test for statusCheckState prop enum --- .../instruction_set.test.js.snap | 142 +++++++++++++++++- .../tutorial/instruction_set.test.js | 35 +++-- 2 files changed, 158 insertions(+), 19 deletions(-) diff --git a/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/instruction_set.test.js.snap b/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/instruction_set.test.js.snap index 9cbd029e594ff..e574f575a5517 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/instruction_set.test.js.snap +++ b/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/instruction_set.test.js.snap @@ -80,7 +80,7 @@ exports[`render 1`] = ` `; -exports[`status check checking status 1`] = ` +exports[`statusCheckState checking status 1`] = `
@@ -207,7 +207,7 @@ exports[`status check checking status 1`] = `
`; -exports[`status check failed status check 1`] = ` +exports[`statusCheckState failed status check - error 1`] = `
@@ -330,7 +330,7 @@ exports[`status check failed status check 1`] = ` /> , "key": "checkStatusStep", - "status": "incomplete", + "status": "complete", "title": "custom title", }, ] @@ -339,7 +339,7 @@ exports[`status check failed status check 1`] = `
`; -exports[`status check initial state - no check has been attempted 1`] = ` +exports[`statusCheckState failed status check - no data 1`] = `
@@ -455,6 +455,138 @@ exports[`status check initial state - no check has been attempted 1`] = ` + + , + "key": "checkStatusStep", + "status": "complete", + "title": "custom title", + }, + ] + } + /> +
+`; + +exports[`statusCheckState initial state - no check has been attempted 1`] = ` +
+ + +
+ title1 +
+
+ +
+ + + OSX + + + Windows + + + + , + "key": 0, + "title": "step 1", + }, + Object { + "children": , + "key": 1, + "title": "step 2", + }, + Object { + "children": + + + +

+ custom status check description +

+
+
+ + + custom btn label + + +
+
, "key": "checkStatusStep", "status": "incomplete", @@ -466,7 +598,7 @@ exports[`status check initial state - no check has been attempted 1`] = `
`; -exports[`status check successful status check 1`] = ` +exports[`statusCheckState successful status check 1`] = `
diff --git a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.test.js b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.test.js index 4311c8593e36e..12769dc6f2c6d 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.test.js +++ b/src/core_plugins/kibana/public/home/components/tutorial/instruction_set.test.js @@ -4,6 +4,7 @@ import { shallow } from 'enzyme'; import { InstructionSet, } from './instruction_set'; +import * as StatusCheckStates from './status_check_states'; const instructions = [ { @@ -43,7 +44,7 @@ test('render', () => { expect(component).toMatchSnapshot(); // eslint-disable-line }); -describe('status check', () => { +describe('statusCheckState', () => { const statusCheckConfig = { success: 'custom success msg', error: 'custom error msg', @@ -61,9 +62,7 @@ describe('status check', () => { paramValues={{}} statusCheckConfig={statusCheckConfig} replaceTemplateStrings={() => {}} - statusCheckState="incomplete" - hasStatusCheckFailed={false} - isCheckingStatus={false} + statusCheckState={StatusCheckStates.FETCHING} />); expect(component).toMatchSnapshot(); // eslint-disable-line }); @@ -77,14 +76,12 @@ describe('status check', () => { paramValues={{}} statusCheckConfig={statusCheckConfig} replaceTemplateStrings={() => {}} - statusCheckState="incomplete" - hasStatusCheckFailed={false} - isCheckingStatus={true} + statusCheckState={StatusCheckStates.FETCHING} />); expect(component).toMatchSnapshot(); // eslint-disable-line }); - test('failed status check', () => { + test('failed status check - error', () => { const component = shallow( { paramValues={{}} statusCheckConfig={statusCheckConfig} replaceTemplateStrings={() => {}} - statusCheckState="incomplete" - hasStatusCheckFailed={true} - isCheckingStatus={false} + statusCheckState={StatusCheckStates.ERROR} + />); + expect(component).toMatchSnapshot(); // eslint-disable-line + }); + + test('failed status check - no data', () => { + const component = shallow( {}} + offset={1} + paramValues={{}} + statusCheckConfig={statusCheckConfig} + replaceTemplateStrings={() => {}} + statusCheckState={StatusCheckStates.NO_DATA} />); expect(component).toMatchSnapshot(); // eslint-disable-line }); @@ -109,9 +118,7 @@ describe('status check', () => { paramValues={{}} statusCheckConfig={statusCheckConfig} replaceTemplateStrings={() => {}} - statusCheckState="complete" - hasStatusCheckFailed={false} - isCheckingStatus={false} + statusCheckState={StatusCheckStates.HAS_DATA} />); expect(component).toMatchSnapshot(); // eslint-disable-line }); From 184fa39b82af376a31d10e01f6ef5ea216e74d98 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 15 May 2018 13:38:55 -0600 Subject: [PATCH 16/16] update tutorial snapshots --- .../tutorial/__snapshots__/tutorial.test.js.snap | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/tutorial.test.js.snap b/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/tutorial.test.js.snap index 70727a8e7c3d6..807ab76357adc 100644 --- a/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/tutorial.test.js.snap +++ b/src/core_plugins/kibana/public/home/components/tutorial/__snapshots__/tutorial.test.js.snap @@ -42,7 +42,6 @@ exports[`isCloudEnabled is false should not render instruction toggle when ON_PR paddingSize="l" > @@ -130,7 +129,6 @@ exports[`isCloudEnabled is false should render ON_PREM instructions with instruc paddingSize="l" > @@ -200,7 +198,6 @@ exports[`should render ELASTIC_CLOUD instructions when isCloudEnabled is true 1` paddingSize="l" >