Skip to content

Commit 03ca123

Browse files
authored
Merge pull request #969 from the-overengineer/feat/ls-kamon-apm-reporter-unconfigured-state
(status page) Add missing api key state
2 parents 0c637a4 + b5b8884 commit 03ca123

File tree

5 files changed

+98
-16
lines changed

5 files changed

+98
-16
lines changed

core/kamon-status-page/src/main/vue/package-lock.json

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/kamon-status-page/src/main/vue/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"dependencies": {
1111
"axios": "^0.21.1",
1212
"ts-option": "1.1.5",
13-
"underscore": "1.9.1",
13+
"underscore": "^1.9.1",
1414
"vue": "2.6.10",
1515
"vue-class-component": "7.0.2",
1616
"vue-property-decorator": "8.1.0",
@@ -19,15 +19,15 @@
1919
},
2020
"devDependencies": {
2121
"@fortawesome/fontawesome-free": "5.8.1",
22-
"@types/underscore": "1.8.14",
22+
"@types/underscore": "^1.8.14",
2323
"@vue/cli-plugin-typescript": "3.6.0",
2424
"@vue/cli-service": "3.6.0",
2525
"fstream": ">=1.0.12",
2626
"node-sass": "4.11.0",
2727
"sass": "^1.32.0",
2828
"sass-loader": "^7.1.0",
2929
"tar": ">=2.2.2",
30-
"typescript": "3.4.5",
30+
"typescript": "3.9.7",
3131
"vue-cli-plugin-vuetify": "~2.1.0",
3232
"vue-template-compiler": "2.6.10",
3333
"vuetify-loader": "^1.7.0"

core/kamon-status-page/src/main/vue/src/components/ModuleList.vue

+33-5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@
1616
</div>
1717
</template>
1818

19-
<module-status-card v-for="reporter in reporterModules" :key="reporter.name" :module="reporter" />
19+
<module-status-card
20+
v-for="reporter in reporterModules"
21+
:key="reporter.name"
22+
:module="reporter"
23+
:is-missing-key="isMisconfiguredApmModule(reporter)"
24+
@show:api-key="$emit('show:api-key')"
25+
/>
2026
<div v-if="!hasApmModule" class="apm-suggestion">
2127
<a href="https://kamon.io/apm/?utm_source=kamon&utm_medium=status-page&utm_campaign=kamon-status" target="_blank">
2228
<module-status-card :isSuggestion="true" :module="apmModuleSuggestion" />
@@ -31,11 +37,17 @@
3137
</template>
3238

3339
<script lang="ts">
40+
import { Option } from 'ts-option'
3441
import { Component, Prop, Vue } from 'vue-property-decorator'
42+
3543
import {Module, ModuleKind} from '../api/StatusApi'
3644
import ModuleStatusCard from './ModuleStatusCard.vue'
3745
import StatusSection from './StatusSection.vue'
3846
47+
const VALID_API_KEY_PATTERN = /^[a-zA-Z0-9]{26}$/
48+
const KNOWN_APM_CLASSES = [
49+
'kamon.apm.KamonApm'
50+
]
3951
4052
@Component({
4153
components: {
@@ -45,6 +57,8 @@ import StatusSection from './StatusSection.vue'
4557
})
4658
export default class ModuleList extends Vue {
4759
@Prop() private modules!: Module[]
60+
@Prop({ required: true }) private config!: Option<any>
61+
4862
private apmModuleSuggestion: Module = {
4963
name: 'Kamon APM Reporter',
5064
description: 'See your metrics and trace data for free with a Starter account.',
@@ -79,15 +93,29 @@ export default class ModuleList extends Vue {
7993
}
8094
8195
get hasApmModule(): boolean {
82-
const knownApmClasses = [
83-
'kamon.apm.KamonApm'
84-
]
96+
return this.modules.some(m => KNOWN_APM_CLASSES.includes(m.clazz))
97+
}
98+
99+
get missingApmApiKey(): boolean {
100+
if (this.config.isEmpty) {
101+
return false
102+
}
85103
86-
return this.modules.some(m => knownApmClasses.includes(m.clazz))
104+
if (!this.hasApmModule) {
105+
return false
106+
}
107+
108+
const apiKey = this.config.get?.kamon?.apm?.['api-key']
109+
110+
return apiKey == null || !VALID_API_KEY_PATTERN.test(apiKey)
87111
}
88112
89113
private isReporter(module: Module): boolean {
90114
return [ModuleKind.Combined, ModuleKind.Span, ModuleKind.Metric].includes(module.kind)
91115
}
116+
117+
private isMisconfiguredApmModule(m: Module) {
118+
return KNOWN_APM_CLASSES.includes(m.clazz) && this.missingApmApiKey
119+
}
92120
}
93121
</script>

core/kamon-status-page/src/main/vue/src/components/ModuleStatusCard.vue

+28-4
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,24 @@
44
:indicator-icon="runStatus.icon"
55
:indicator-background-color="runStatus.color"
66
:indicator-color="runStatus.indicatorColor"
7-
:content-class="{ 'suggestion-card': isSuggestion }"
7+
:content-class="{ 'suggestion-card': isSuggestion, 'misconfigured-card': isMissingKey }"
88
>
99
<template #default>
1010
<div>
1111
<div class="text-label dark1--text">
1212
{{module.name}}
1313
</div>
1414
<div class="text-sublabel mt-1 dark3--text">
15-
{{module.description}}
15+
<span v-if="isMissingKey">
16+
Setting <strong>kamon.apm.api-key</strong> in <strong>application.conf</strong> invalid or missing
17+
</span>
18+
<span v-else>
19+
{{module.description}}
20+
</span>
1621
</div>
1722
</div>
1823
</template>
19-
<template #status v-if="!isSuggestion">
24+
<template #status v-if="!isSuggestion && !isMissingKey">
2025
<div
2126
class="module-status text-indicator px-2 py-1 rounded"
2227
:class="chipClasses"
@@ -29,6 +34,11 @@
2934
Connect APM
3035
</v-btn>
3136
</template>
37+
<template #action v-else-if="isMissingKey">
38+
<v-btn depressed color="warning" class="px-4 font-weight-bold" @click="showApmApiKey">
39+
Configure API Key
40+
</v-btn>
41+
</template>
3242
</status-card>
3343
</template>
3444

@@ -47,14 +57,17 @@ const isInstrumentationModule = (m: any): m is InstrumentationModule =>
4757
})
4858
export default class ModuleStatusCard extends Vue {
4959
@Prop({ default: false }) private isSuggestion!: boolean
60+
@Prop({ default: false }) private isMissingKey!: boolean
5061
@Prop() private module!: Module | InstrumentationModule
5162
5263
get started(): boolean {
5364
return isInstrumentationModule(this.module) ? this.module.active : this.module.started
5465
}
5566
5667
get status(): string {
57-
if (this.started) {
68+
if (this.isMissingKey) {
69+
return 'Not Configured'
70+
} else if (this.started) {
5871
return 'Enabled'
5972
} else if (this.module.enabled) {
6073
return 'Present'
@@ -68,6 +81,8 @@ export default class ModuleStatusCard extends Vue {
6881
return { message: 'suggested', color: 'primary', icon: 'fa-plug', indicatorColor: 'white' }
6982
} else if (!this.module.enabled) {
7083
return { message: 'disabled', color: 'error', icon: 'fa-stop-circle', indicatorColor: 'white' }
84+
} else if (this.isMissingKey) {
85+
return { message: 'not configured', color: 'warning', icon: 'fa-plug', indicatorColor: 'white'}
7186
} else {
7287
return this.started ?
7388
{ message: 'active', color: 'primary', icon: 'fa-check', indicatorColor: 'white' } :
@@ -84,6 +99,10 @@ export default class ModuleStatusCard extends Vue {
8499
return 'red4 error--text'
85100
}
86101
}
102+
103+
public showApmApiKey() {
104+
this.$emit('show:api-key')
105+
}
87106
}
88107
</script>
89108

@@ -92,4 +111,9 @@ export default class ModuleStatusCard extends Vue {
92111
border: 1px solid #3BC882 !important;
93112
background-color: #E3FFF1 !important;
94113
}
114+
115+
.misconfigured-card {
116+
border: 1px solid #FFCC00 !important;
117+
background-color: #FFF8E5 !important;
118+
}
95119
</style>

core/kamon-status-page/src/main/vue/src/views/Overview.vue

+31-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
</v-col>
1717

1818
<v-col cols="12" class="js-reporters">
19-
<module-list :modules="modules"/>
19+
<module-list :config="config" :modules="modules" @show:api-key="showApmApiKey" />
2020
</v-col>
2121

2222
<v-col cols="12" class="js-instrumentation">
@@ -107,6 +107,32 @@ export default class Overview extends Vue {
107107
return this.settings.map(s => s.environment)
108108
}
109109
110+
get config(): Option<any> {
111+
return this.settings.map(s => s.config)
112+
}
113+
114+
get redirectInfo() {
115+
const serviceName = this.config.map(c => c?.kamon?.environment?.service).orNull
116+
const usedInstrumentation = this.instrumentationModules.filter(m => m.enabled && m.active)
117+
const framework = (() => {
118+
if (usedInstrumentation.some(i => i.name.toLocaleLowerCase().includes('play'))) {
119+
return 'play'
120+
} else if (usedInstrumentation.some(i => i.name.toLocaleLowerCase().includes('akka'))) {
121+
return 'akka'
122+
} else if (usedInstrumentation.some(i => i.name.toLocaleLowerCase().includes('spring'))) {
123+
return 'spring'
124+
} else {
125+
return 'plain'
126+
}
127+
})()
128+
const query = new URLSearchParams()
129+
130+
query.set('continueOnboarding', 'true')
131+
query.set('framework', framework)
132+
query.set('serviceName', serviceName)
133+
return `https://apm.kamon.io?${query.toString()}`
134+
}
135+
110136
public mounted() {
111137
this.refreshData()
112138
}
@@ -123,6 +149,10 @@ export default class Overview extends Vue {
123149
this.$vuetify.goTo('.js-metrics', { offset: 80 })
124150
}
125151
152+
private showApmApiKey() {
153+
window.open(this.redirectInfo)
154+
}
155+
126156
private refreshData(): void {
127157
StatusApi.settings().then(settings => { this.settings = some(settings) })
128158
StatusApi.metricRegistryStatus().then(metricRegistry => { this.metricRegistry = some(metricRegistry) })

0 commit comments

Comments
 (0)