Skip to content

Commit

Permalink
adds support for date math in index names, fixes #267
Browse files Browse the repository at this point in the history
  • Loading branch information
cars10 committed Dec 21, 2024
1 parent 8f693a1 commit 2cc65cf
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 25 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

# 1.2.0

* adds support for date math in index names. You can use something like `<kube-{now/d}>` as your index name, when
searching and also in the `REST` view. fixes [#267](https://github.com/cars10/elasticvue/issues/267)

## 1.1.2

This version re-adds the automatic updater for the desktop app. Since the update to tauri 2.0
Expand Down
9 changes: 5 additions & 4 deletions src/composables/components/rest/RestQueryForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { fetchMethod } from '../../../helpers/fetch'
import { IdbRestQueryTab, IdbRestQueryTabRequest } from '../../../db/types.ts'
import { debounce } from '../../../helpers/debounce.ts'
import { parseKibana } from '../../../helpers/parseKibana.ts'
import { cleanIndexName } from '../../../helpers/cleanIndexName.ts'

type RestQueryFormProps = {
tab: IdbRestQueryTab
Expand Down Expand Up @@ -48,7 +49,7 @@ export const useRestQueryForm = (props: RestQueryFormProps, emit: any) => {

let url = connectionStore.activeCluster.uri
if (!url.endsWith('/') && !props.tab.request.path.startsWith('/')) url += '/'
url += props.tab.request.path
url += cleanIndexName(props.tab.request.path)

try {
const fetchResponse = await fetchMethod(url, options)
Expand Down Expand Up @@ -143,11 +144,11 @@ export const useRestQueryForm = (props: RestQueryFormProps, emit: any) => {
const onPaste = (data: string) => {
const kibanaRequest = parseKibana(data)

nextTick(() => {
nextTick(() => {
if (kibanaRequest.method) {
ownTab.value.request.method = kibanaRequest.method
ownTab.value.request.path = kibanaRequest.path || ''
ownTab.value.request.body = kibanaRequest.body || ''
ownTab.value.request.path = kibanaRequest.path || ''
ownTab.value.request.body = kibanaRequest.body || ''
}
})
}
Expand Down
14 changes: 14 additions & 0 deletions src/helpers/cleanIndexName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const cleanIndexName = (index: string) => {
return index.replace(/%/g, '%25').replace(/<.*?>/g, (match) => {
return match
.replace(/</g, '%3C')
.replace(/>/g, '%3E')
.replace(/\//g, '%2F')
.replace(/\{/g, '%7B')
.replace(/\}/g, '%7D')
.replace(/\|/g, '%7C')
.replace(/\+/g, '%2B')
.replace(/:/g, '%3A')
.replace(/,/g, '%2C')
})
}
28 changes: 7 additions & 21 deletions src/services/ElasticsearchAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { REQUEST_DEFAULT_HEADERS } from '../consts'
import { fetchMethod } from '../helpers/fetch'
import { stringifyJson } from '../helpers/json/stringify.ts'
import { ElasticsearchClusterCredentials } from '../store/connection.ts'
import { cleanIndexName } from '../helpers/cleanIndexName.ts'

interface IndexGetArgs {
routing?: string
Expand Down Expand Up @@ -131,47 +132,47 @@ export default class ElasticsearchAdapter {
if (indices.length > MAX_INDICES_PER_REQUEST) {
return this.callInChunks({ method: 'indexClose', indices })
} else {
return this.request(`${indices.join(',')}/_close`, 'POST')
return this.request(`${cleanIndexName(indices.join(','))}/_close`, 'POST')
}
}

indexOpen ({ indices }: { indices: string[] }) {
if (indices.length > MAX_INDICES_PER_REQUEST) {
return this.callInChunks({ method: 'indexOpen', indices })
} else {
return this.request(`${indices.join(',')}/_open`, 'POST')
return this.request(`${cleanIndexName(indices.join(','))}/_open`, 'POST')
}
}

indexForcemerge ({ indices }: { indices: string[] }) {
if (indices.length > MAX_INDICES_PER_REQUEST) {
return this.callInChunks({ method: 'indexForcemerge', indices })
} else {
return this.request(`${indices.join(',')}/_forcemerge`, 'POST')
return this.request(`${cleanIndexName(indices.join(','))}/_forcemerge`, 'POST')
}
}

indexRefresh ({ indices }: { indices: string[] }) {
if (indices.length > MAX_INDICES_PER_REQUEST) {
return this.callInChunks({ method: 'indexRefresh', indices })
} else {
return this.request(`${indices.join(',')}/_refresh`, 'POST')
return this.request(`${cleanIndexName(indices.join(','))}/_refresh`, 'POST')
}
}

indexClearCache ({ indices }: { indices: string[] }) {
if (indices.length > MAX_INDICES_PER_REQUEST) {
return this.callInChunks({ method: 'indexClearCache', indices })
} else {
return this.request(`${indices.join(',')}/_cache/clear`, 'POST')
return this.request(`${cleanIndexName(indices.join(','))}/_cache/clear`, 'POST')
}
}

indexFlush ({ indices }: { indices: string[] }) {
if (indices.length > MAX_INDICES_PER_REQUEST) {
return this.callInChunks({ method: 'indexFlush', indices })
} else {
return this.request(`${indices.join(',')}/_flush`, 'POST')
return this.request(`${cleanIndexName(indices.join(','))}/_flush`, 'POST')
}
}

Expand Down Expand Up @@ -316,19 +317,4 @@ export default class ElasticsearchAdapter {
return Promise.reject(e)
}
}

/********/

/**
* Creates multiple indices, one for each word. Only creates if they do not already exist
* @param names {Array}
*/
async createIndices (names: string[]) {
for (const name of [...new Set(names)]) {
const exists = await this.indexExists({ index: name })
if (!exists) await this.indexCreate({ index: name })
}
}
}

const cleanIndexName = (index: string) => (index.replace(/%/g, '%25'))
39 changes: 39 additions & 0 deletions tests/unit/helpers/cleanIndexName.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { describe, it, expect } from 'vitest'
import { cleanIndexName } from '../../../src/helpers/cleanIndexName'

describe.concurrent('helpers/cleanIndexName.ts', () => {
it('should do nothing when no special characters are present', () => {
const indexNames = [
'',
'movies',
'kube-2024.12.21'
]
indexNames.forEach(indexName => {
expect(cleanIndexName(indexName)).toBe(indexName)
})
})

it('should encode special characters inside < and >', () => {
const indexName = 'foo/<kube-{now/d}>/_doc'
const cleanedName = 'foo/%3Ckube-%7Bnow%2Fd%7D%3E/_doc'
expect(cleanIndexName(indexName)).toBe(cleanedName)
})

it('should encode multiple <...> segments correctly', () => {
const indexName = 'foo/<kube-{now/d}>/bar/<baz+foo>'
const cleanedName = 'foo/%3Ckube-%7Bnow%2Fd%7D%3E/bar/%3Cbaz%2Bfoo%3E'
expect(cleanIndexName(indexName)).toBe(cleanedName)
})

it('should encode % everywhere, but other characters only inside < and >', () => {
const indexName = 'foo/%/<kube%+>/bar'
const cleanedName = 'foo/%25/%3Ckube%25%2B%3E/bar'
expect(cleanIndexName(indexName)).toBe(cleanedName)
})

it('should not encode characters outside of < and >', () => {
const indexName = 'movies/kube+foo/bar'
const cleanedName = 'movies/kube+foo/bar'
expect(cleanIndexName(indexName)).toBe(cleanedName)
})
})

0 comments on commit 2cc65cf

Please sign in to comment.