refactor: add filtering to html output
parent
5daca2eb46
commit
4b9af2b57b
|
@ -1,3 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Driftctl scan report</title>
|
||||
|
@ -5,97 +6,136 @@
|
|||
<style>{{.Stylesheet}}</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 class="heading title">Driftctl scan report</h1>
|
||||
<span class="heading subtitle">Coverage {{.Coverage}}%</span>
|
||||
<span class="heading date">{{.ScanDate}}</span>
|
||||
<form action="#">
|
||||
<input type="text">
|
||||
</form>
|
||||
<h2>Managed resources ({{ len .Managed }})</h2>
|
||||
{{ if (eq (len .Managed) 0) }}
|
||||
<p>There's nothing to see there...</p>
|
||||
{{end}}
|
||||
{{range $val := .Managed}}
|
||||
<div>
|
||||
<p class="list-item resource-id">{{$val.TerraformId}}</p>
|
||||
<span class="list-item resource-type">{{$val.TerraformType}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<h2>Unmanaged resources ({{ len .Unmanaged }})</h2>
|
||||
{{ if (eq (len .Unmanaged) 0) }}
|
||||
<p>There's nothing to see there...</p>
|
||||
{{end}}
|
||||
{{range $val := .Unmanaged}}
|
||||
<div>
|
||||
<p class="list-item resource-id">{{$val.TerraformId}}</p>
|
||||
<span class="list-item resource-type">{{$val.TerraformType}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
<div id="app">
|
||||
<h1 class="heading title">Driftctl scan report {{ if (eq .Coverage 100) }}✅{{else}}❌{{end}}</h1>
|
||||
<span class="heading subtitle">Coverage 8%</span>
|
||||
<span class="heading date">Apr 21, 2021</span>
|
||||
<hr>
|
||||
<form id="filter-form" action="#">
|
||||
<input type="text" name="resource-id-filter" placeholder="Search resources..." onkeyup="refreshState()">
|
||||
<select name="resource-type-filter" onchange="refreshState()">
|
||||
<option value="">Resource type</option>
|
||||
{{ range $type := getResourceTypes }}
|
||||
<option value="{{$type}}">{{ $type }}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<h2>Changed resources ({{ len .Differences }})</h2>
|
||||
{{ if (eq (len .Differences) 0) }}
|
||||
<h2>Managed resources <span class="resource-count-managed">({{len .Managed}})</span></h2>
|
||||
|
||||
{{ if (eq (len .Managed) 0) }}
|
||||
<p>There's nothing to see there...</p>
|
||||
{{end}}
|
||||
{{range $diff := .Differences}}
|
||||
<div>
|
||||
<p class="list-item resource-id">{{$diff.Res.TerraformId}}</p>
|
||||
<span class="list-item resource-type">{{$diff.Res.TerraformType}}</span>
|
||||
{{end}}
|
||||
{{range $res := .Managed}}
|
||||
<div class="resource-item">
|
||||
<span class="resource-item-id">{{$res.TerraformId}}</span>
|
||||
<span class="resource-item-type">{{$res.TerraformType}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<h2>Unmanaged resources <span class="resource-count-unmanaged">({{len .Unmanaged}})</span></h2>
|
||||
|
||||
{{ if (eq (len .Unmanaged) 0) }}
|
||||
<p>There's nothing to see there...</p>
|
||||
{{end}}
|
||||
{{range $res := .Unmanaged}}
|
||||
<div class="resource-item">
|
||||
<span class="resource-item-id">{{$res.TerraformId}}</span>
|
||||
<span class="resource-item-type">{{$res.TerraformType}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<h2>Changed resources <span class="resource-count-changed">({{len .Differences}})</span></h2>
|
||||
|
||||
{{ if (eq (len .Differences) 0) }}
|
||||
<p>There's nothing to see there...</p>
|
||||
{{end}}
|
||||
{{range $diff := .Differences}}
|
||||
<div class="resource-item">
|
||||
<span class="resource-item-id">{{$diff.Res.TerraformId}}</span>
|
||||
<span class="resource-item-type">{{$diff.Res.TerraformType}}</span>
|
||||
<div>
|
||||
{{range $change := $diff.Changelog}}
|
||||
<div>{{ formatChange $change }}</div>
|
||||
<div>{{ formatChange $change }}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
<h2>Missing resources ({{ len .Deleted }})</h2>
|
||||
{{ if (eq (len .Deleted) 0) }}
|
||||
<p>There's nothing to see there...</p>
|
||||
{{end}}
|
||||
{{range $val := .Deleted}}
|
||||
<div>
|
||||
<p class="list-item resource-id">{{$val.TerraformId}}</p>
|
||||
<span class="list-item resource-type">{{$val.TerraformType}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
<h2>Missing resources <span class="resource-count-deleted">({{len .Deleted}})</span></h2>
|
||||
|
||||
<h2>Alerts ({{ len .Alerts }})</h2>
|
||||
{{ if (eq (len .Alerts) 0) }}
|
||||
{{ if (eq (len .Deleted) 0) }}
|
||||
<p>There's nothing to see there...</p>
|
||||
{{end}}
|
||||
{{range $key, $val := .Alerts}}
|
||||
<div>
|
||||
<p class="list-item resource-id">{{$key}}</p>
|
||||
<span class="list-item resource-type">{{$val}}</span>
|
||||
{{end}}
|
||||
{{range $res := .Deleted}}
|
||||
<div class="resource-item">
|
||||
<span class="resource-item-id">{{$res.TerraformId}}</span>
|
||||
<span class="resource-item-type">{{$res.TerraformType}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
<h2>Alerts <span class="resource-count-alerts">({{len .Alerts}})</span></h2>
|
||||
|
||||
{{ if (eq (len .Alerts) 0) }}
|
||||
<p>There's nothing to see there...</p>
|
||||
{{end}}
|
||||
{{range $type, $messages := .Alerts}}
|
||||
<div class="resource-item">
|
||||
<span class="resource-item-type">{{ $type }}</span>
|
||||
<div>
|
||||
{{range $msg := $messages}}
|
||||
<div>- {{ $msg }}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
const managed = {{if .Managed}} {{.Managed}} {{else}} [] {{end}}
|
||||
const unmanaged = {{if .Unmanaged}} {{.Unmanaged}} {{else}} [] {{end}}
|
||||
const deleted = {{if .Deleted}} {{.Deleted}} {{else}} [] {{end}}
|
||||
const allResources = [
|
||||
...managed,
|
||||
...unmanaged,
|
||||
...deleted,
|
||||
]
|
||||
const resourceTypes = [
|
||||
{{range $val := .Managed}}
|
||||
'{{$val.TerraformType}}',
|
||||
{{end}}
|
||||
{{range $val := .Unmanaged}}
|
||||
'{{$val.TerraformType}}',
|
||||
{{end}}
|
||||
{{range $val := .Deleted}}
|
||||
'{{$val.TerraformType}}',
|
||||
{{end}}
|
||||
{{range $val := .Differences}}
|
||||
'{{$val.Res.TerraformType}}',
|
||||
{{end}}
|
||||
].filter((value, index, self) => self.indexOf(value) === index)
|
||||
<script lang="js">
|
||||
const resources = document.querySelectorAll('.resource-item')
|
||||
|
||||
function search() {
|
||||
function hideResource(res) {
|
||||
res.classList.add('hide')
|
||||
}
|
||||
|
||||
function displayResource(res) {
|
||||
res.classList.remove('hide')
|
||||
}
|
||||
|
||||
function resourceIdContains(res, query) {
|
||||
const el = res.querySelector('.resource-item-id')
|
||||
if (!el) {
|
||||
return
|
||||
}
|
||||
return el.innerText.toLowerCase().includes(query.toLowerCase())
|
||||
}
|
||||
|
||||
function resourceTypeEqual(res, type) {
|
||||
const el = res.querySelector('.resource-item-type')
|
||||
if (!el) {
|
||||
return
|
||||
}
|
||||
return el.innerText === type
|
||||
}
|
||||
|
||||
function refreshState() {
|
||||
const queryFilterInput = document.querySelector('input[name=resource-id-filter]').value
|
||||
const typeFilterInput = document.querySelector('select[name=resource-type-filter]').value
|
||||
|
||||
for (const res of resources) {
|
||||
const matchId = !queryFilterInput.length || resourceIdContains(res, queryFilterInput)
|
||||
const matchType = !typeFilterInput.length || resourceTypeEqual(res, typeFilterInput)
|
||||
|
||||
if (matchId && matchType) {
|
||||
displayResource(res)
|
||||
continue
|
||||
}
|
||||
|
||||
hideResource(res)
|
||||
}
|
||||
}
|
||||
|
||||
refreshState()
|
||||
</script>
|
||||
</html>
|
|
@ -0,0 +1,189 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Driftctl scan report</title>
|
||||
<meta charset="utf-8">
|
||||
<style>[[.Stylesheet]]</style>
|
||||
<script>[[.VueScript]]</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<h1 class="heading title">Driftctl scan report</h1>
|
||||
<span class="heading subtitle">Coverage 8%</span>
|
||||
<span class="heading date">Apr 21, 2021</span>
|
||||
<hr>
|
||||
<form action="#">
|
||||
<input v-model="searchQuery" type="text" placeholder="Search resources...">
|
||||
<select v-model="resourceTypeQuery">
|
||||
<option value="">Resource type</option>
|
||||
<option v-for="type of getResourceTypes" :value="type">{{ type }}</option>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<h2>Managed resources ({{Managed.length}})</h2>
|
||||
|
||||
<div v-for="res of Managed">
|
||||
<p class="list-item resource-id">{{ res.id }}</p>
|
||||
<span class="list-item resource-type">{{ res.type }}</span>
|
||||
</div>
|
||||
<p v-if="!Managed.length">There's nothing to see there...</p>
|
||||
|
||||
<h2>Unmanaged resources ({{Unmanaged.length}})</h2>
|
||||
|
||||
<div v-for="res of Unmanaged">
|
||||
<p class="list-item resource-id">{{ res.id }}</p>
|
||||
<span class="list-item resource-type">{{ res.type }}</span>
|
||||
</div>
|
||||
<p v-if="!Unmanaged.length">There's nothing to see there...</p>
|
||||
|
||||
<h2>Changed resources ({{Changed.length}})</h2>
|
||||
|
||||
<div v-for="ch of Changed">
|
||||
<p class="list-item resource-id">{{ ch.res.id }}</p>
|
||||
<span class="list-item resource-type">{{ ch.res.type }}</span>
|
||||
<div v-for="diff of ch.changelog">
|
||||
<div>{{ formatDifference(diff) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="!Changed.length">There's nothing to see there...</p>
|
||||
|
||||
<h2>Missing resources ({{Missing.length}})</h2>
|
||||
|
||||
<div v-for="res of Missing">
|
||||
<p class="list-item resource-id">{{ res.id }}</p>
|
||||
<span class="list-item resource-type">{{ res.type }}</span>
|
||||
</div>
|
||||
<p v-if="!Missing.length">There's nothing to see there...</p>
|
||||
|
||||
<h2>Alerts ({{alerts.length}})</h2>
|
||||
|
||||
<div v-for="alert of alerts">
|
||||
<p class="list-item resource-id">{{ alert.type }}</p>
|
||||
<div v-for="msg of alert.messages">
|
||||
<div>{{ msg }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="!alerts.length">There's nothing to see there...</p>
|
||||
</div>
|
||||
</body>
|
||||
<script lang="js">
|
||||
Vue.createApp({
|
||||
data() {
|
||||
return {
|
||||
searchQuery: '',
|
||||
resourceTypeQuery: '',
|
||||
managed: [
|
||||
[[range $res := .Managed]]
|
||||
{
|
||||
id: [[$res.TerraformId]],
|
||||
type: [[$res.TerraformType]],
|
||||
},
|
||||
[[end]]
|
||||
],
|
||||
unmanaged: [
|
||||
[[range $res := .Unmanaged]]
|
||||
{
|
||||
id: [[$res.TerraformId]],
|
||||
type: [[$res.TerraformType]],
|
||||
},
|
||||
[[end]]
|
||||
],
|
||||
missing: [
|
||||
[[range $res := .Deleted]]
|
||||
{
|
||||
id: [[$res.TerraformId]],
|
||||
type: [[$res.TerraformType]],
|
||||
},
|
||||
[[end]]
|
||||
],
|
||||
changed: [
|
||||
[[range $ch := .Differences]]
|
||||
{
|
||||
res: {
|
||||
id: [[$ch.Res.TerraformId]],
|
||||
type: [[$ch.Res.TerraformType]],
|
||||
},
|
||||
changelog: [[$ch.Changelog]],
|
||||
},
|
||||
[[end]]
|
||||
],
|
||||
alerts: [
|
||||
[[range $type, $alerts := .Alerts]]
|
||||
{
|
||||
type: [[$type]],
|
||||
messages: [
|
||||
[[range $alert := $alerts]] [[$alert.Message]] [[end]],
|
||||
]
|
||||
},
|
||||
[[end]]
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
Managed() {
|
||||
return this.filterResources(this.managed)
|
||||
},
|
||||
Unmanaged() {
|
||||
return this.filterResources(this.unmanaged)
|
||||
},
|
||||
Missing() {
|
||||
return this.filterResources(this.missing)
|
||||
},
|
||||
Changed() {
|
||||
return this.filterDifferences(this.changed)
|
||||
},
|
||||
getResourceTypes() {
|
||||
return [...this.managed, ...this.unmanaged, ...this.missing]
|
||||
.map(res => res.type)
|
||||
.filter((value, index, self) => self.indexOf(value) === index)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
matchSearch(str) {
|
||||
return str.toLowerCase().includes(this.searchQuery.toLowerCase())
|
||||
},
|
||||
filterResources(resources) {
|
||||
let result = resources.filter(res => this.matchSearch(res.id))
|
||||
|
||||
if (this.resourceTypeQuery.length) {
|
||||
result = result.filter(res => res.type === this.resourceTypeQuery)
|
||||
}
|
||||
|
||||
return result
|
||||
},
|
||||
filterDifferences(diffs) {
|
||||
let result = diffs.filter(d => this.matchSearch(d.res.id))
|
||||
|
||||
if (this.resourceTypeQuery.length) {
|
||||
result = result.filter(d => d.res.type === this.resourceTypeQuery)
|
||||
}
|
||||
|
||||
return result
|
||||
},
|
||||
formatDifference(diff) {
|
||||
let prefix = ""
|
||||
let suffix = ""
|
||||
|
||||
switch (diff.type) {
|
||||
case "create":
|
||||
prefix = "+"
|
||||
case "update":
|
||||
prefix = "~"
|
||||
case "delete":
|
||||
prefix = "-"
|
||||
}
|
||||
|
||||
if (diff.computed) {
|
||||
suffix = "(computed)"
|
||||
}
|
||||
|
||||
return `${prefix} ${diff.path.join('.')}: ${this.prettifyValue(diff.from)} => ${this.prettifyValue(diff.to)} ${suffix}`
|
||||
},
|
||||
prettifyValue(value) {
|
||||
return value
|
||||
}
|
||||
},
|
||||
}).mount('#app')
|
||||
</script>
|
||||
</html>
|
|
@ -1,6 +1,11 @@
|
|||
* {
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
div {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -63,6 +64,35 @@ func (c *HTML) Write(analysis *analyser.Analysis) error {
|
|||
}
|
||||
|
||||
funcMap := template.FuncMap{
|
||||
"getResourceTypes": func() []string {
|
||||
resources := []resource.Resource{}
|
||||
list := []string{}
|
||||
|
||||
resources = append(resources, analysis.Unmanaged()...)
|
||||
resources = append(resources, analysis.Managed()...)
|
||||
resources = append(resources, analysis.Deleted()...)
|
||||
|
||||
for _, res := range resources {
|
||||
if i := sort.SearchStrings(list, res.TerraformType()); i <= len(list)-1 {
|
||||
continue
|
||||
}
|
||||
list = append(list, res.TerraformType())
|
||||
}
|
||||
for _, d := range analysis.Differences() {
|
||||
if i := sort.SearchStrings(list, d.Res.TerraformType()); i <= len(list)-1 {
|
||||
continue
|
||||
}
|
||||
list = append(list, d.Res.TerraformType())
|
||||
}
|
||||
for kind := range analysis.Alerts() {
|
||||
if i := sort.SearchStrings(list, kind); i <= len(list)-1 {
|
||||
continue
|
||||
}
|
||||
list = append(list, kind)
|
||||
}
|
||||
|
||||
return list
|
||||
},
|
||||
"formatChange": func(ch analyser.Change) string {
|
||||
prefix := ""
|
||||
suffix := ""
|
||||
|
|
|
@ -3,6 +3,7 @@ package output
|
|||
import (
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -25,7 +26,7 @@ func TestHTML_Write(t *testing.T) {
|
|||
name: "test html output",
|
||||
goldenfile: "output.html",
|
||||
args: args{
|
||||
analysis: fakeAnalysis(),
|
||||
analysis: fakeAnalysisWithAlerts(),
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
|
@ -47,14 +48,14 @@ func TestHTML_Write(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
result, err := ioutil.ReadFile(tempFile.Name())
|
||||
got, err := ioutil.ReadFile(tempFile.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedFilePath := path.Join("./testdata/", tt.goldenfile)
|
||||
if *goldenfile.Update == tt.goldenfile {
|
||||
if err := ioutil.WriteFile(expectedFilePath, result, 0600); err != nil {
|
||||
if err := ioutil.WriteFile(expectedFilePath, got, 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +65,10 @@ func TestHTML_Write(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, string(expected), string(result))
|
||||
prettifiedExpected := strings.ReplaceAll(string(expected), " ", "")
|
||||
prettifiedGot := strings.ReplaceAll(string(got), " ", "")
|
||||
|
||||
assert.Equal(t, prettifiedExpected, prettifiedGot)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,6 +79,18 @@ func fakeAnalysis() *analyser.Analysis {
|
|||
return &a
|
||||
}
|
||||
|
||||
func fakeAnalysisWithAlerts() *analyser.Analysis {
|
||||
a := fakeAnalysis()
|
||||
a.SetAlerts(alerter.Alerts{
|
||||
"": []alerter.Alert{
|
||||
remote.NewEnumerationAccessDeniedAlert(aws.RemoteAWSTerraform, "aws_vpc", "aws_vpc"),
|
||||
remote.NewEnumerationAccessDeniedAlert(aws.RemoteAWSTerraform, "aws_sqs", "aws_sqs"),
|
||||
remote.NewEnumerationAccessDeniedAlert(aws.RemoteAWSTerraform, "aws_sns", "aws_sns"),
|
||||
},
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
func fakeAnalysisNoDrift() *analyser.Analysis {
|
||||
a := analyser.Analysis{}
|
||||
for i := 0; i < 5; i++ {
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Driftctl scan report</title>
|
||||
<meta charset="utf-8">
|
||||
<style>body {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
div {
|
||||
display: block;
|
||||
}</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<h1 class="heading title">Driftctl scan report ❌</h1>
|
||||
<span class="heading subtitle">Coverage 8%</span>
|
||||
<span class="heading date">Apr 21, 2021</span>
|
||||
<hr>
|
||||
<form id="filter-form" action="#">
|
||||
<input type="text" name="resource-id-filter" placeholder="Search resources..." onkeyup="refreshState()">
|
||||
<select name="resource-type-filter" onchange="refreshState()">
|
||||
<option value="">Resource type</option>
|
||||
|
||||
<option value="aws_unmanaged_resource">aws_unmanaged_resource</option>
|
||||
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<h2>Managed resources <span class="resource-count-managed">(2)</span></h2>
|
||||
|
||||
|
||||
|
||||
<div class="resource-item">
|
||||
<span class="resource-item-id">diff-id-1</span>
|
||||
<span class="resource-item-type">aws_diff_resource</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-item">
|
||||
<span class="resource-item-id">no-diff-id-1</span>
|
||||
<span class="resource-item-type">aws_no_diff_resource</span>
|
||||
</div>
|
||||
|
||||
|
||||
<h2>Unmanaged resources <span class="resource-count-unmanaged">(2)</span></h2>
|
||||
|
||||
|
||||
|
||||
<div class="resource-item">
|
||||
<span class="resource-item-id">unmanaged-id-1</span>
|
||||
<span class="resource-item-type">aws_unmanaged_resource</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-item">
|
||||
<span class="resource-item-id">unmanaged-id-2</span>
|
||||
<span class="resource-item-type">aws_unmanaged_resource</span>
|
||||
</div>
|
||||
|
||||
|
||||
<h2>Changed resources <span class="resource-count-changed">(1)</span></h2>
|
||||
|
||||
|
||||
|
||||
<div class="resource-item">
|
||||
<span class="resource-item-id">diff-id-1</span>
|
||||
<span class="resource-item-type">aws_diff_resource</span>
|
||||
<div>
|
||||
|
||||
<div>~ updated.field: "foobar" => "barfoo" </div>
|
||||
|
||||
<div>+ new.field: <nil> => "newValue" </div>
|
||||
|
||||
<div>- a: "oldValue" => <nil> </div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<h2>Missing resources <span class="resource-count-deleted">(2)</span></h2>
|
||||
|
||||
|
||||
|
||||
<div class="resource-item">
|
||||
<span class="resource-item-id">deleted-id-1</span>
|
||||
<span class="resource-item-type">aws_deleted_resource</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-item">
|
||||
<span class="resource-item-id">deleted-id-2</span>
|
||||
<span class="resource-item-type">aws_deleted_resource</span>
|
||||
</div>
|
||||
|
||||
|
||||
<h2>Alerts <span class="resource-count-alerts">(1)</span></h2>
|
||||
|
||||
|
||||
|
||||
<div class="resource-item">
|
||||
<span class="resource-item-type"></span>
|
||||
<div>
|
||||
|
||||
<div>- {Ignoring aws_vpc from drift calculation: Listing aws_vpc is forbidden. aws+tf}</div>
|
||||
|
||||
<div>- {Ignoring aws_sqs from drift calculation: Listing aws_sqs is forbidden. aws+tf}</div>
|
||||
|
||||
<div>- {Ignoring aws_sns from drift calculation: Listing aws_sns is forbidden. aws+tf}</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
<script lang="js">
|
||||
const resources = document.querySelectorAll('.resource-item')
|
||||
|
||||
function hideResource(res) {
|
||||
res.setAttribute('style', 'display:none;')
|
||||
}
|
||||
|
||||
function displayResource(res) {
|
||||
res.setAttribute('style', '')
|
||||
}
|
||||
|
||||
function resourceIdContains(res, query) {
|
||||
const el = res.querySelector('.resource-item-id')
|
||||
if (!el) {
|
||||
return
|
||||
}
|
||||
return el.innerText.toLowerCase().includes(query.toLowerCase())
|
||||
}
|
||||
|
||||
function resourceTypeEqual(res, type) {
|
||||
const el = res.querySelector('.resource-item-type')
|
||||
if (!el) {
|
||||
return
|
||||
}
|
||||
return el.innerText === type
|
||||
}
|
||||
|
||||
function refreshState() {
|
||||
const queryFilterInput = document.querySelector('input[name=resource-id-filter]').value
|
||||
const typeFilterInput = document.querySelector('select[name=resource-type-filter]').value
|
||||
|
||||
for (const res of resources) {
|
||||
const matchId = !queryFilterInput.length || resourceIdContains(res, queryFilterInput)
|
||||
const matchType = !typeFilterInput.length || resourceTypeEqual(res, typeFilterInput)
|
||||
|
||||
if (matchId && matchType) {
|
||||
displayResource(res)
|
||||
continue
|
||||
}
|
||||
|
||||
hideResource(res)
|
||||
}
|
||||
}
|
||||
|
||||
refreshState()
|
||||
</script>
|
||||
</html>
|
Loading…
Reference in New Issue