Merge pull request #3702 from Budibase/duplicate-query
Duplicate queries
This commit is contained in:
commit
83a28eb4bb
|
@ -11,6 +11,14 @@
|
||||||
await queries.delete(query)
|
await queries.delete(query)
|
||||||
notifications.success("Query deleted")
|
notifications.success("Query deleted")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function duplicateQuery() {
|
||||||
|
try {
|
||||||
|
await queries.duplicate(query)
|
||||||
|
} catch (e) {
|
||||||
|
notifications.error(e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ActionMenu>
|
<ActionMenu>
|
||||||
|
@ -18,6 +26,7 @@
|
||||||
<Icon size="S" hoverable name="MoreSmallList" />
|
<Icon size="S" hoverable name="MoreSmallList" />
|
||||||
</div>
|
</div>
|
||||||
<MenuItem icon="Delete" on:click={confirmDeleteDialog.show}>Delete</MenuItem>
|
<MenuItem icon="Delete" on:click={confirmDeleteDialog.show}>Delete</MenuItem>
|
||||||
|
<MenuItem icon="Duplicate" on:click={duplicateQuery}>Duplicate</MenuItem>
|
||||||
</ActionMenu>
|
</ActionMenu>
|
||||||
|
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/**
|
||||||
|
* Duplicates a name with respect to a collection of existing names
|
||||||
|
* e.g.
|
||||||
|
* name all names result
|
||||||
|
* ------ ----------- --------
|
||||||
|
* ("foo") ["foo"] "foo (1)"
|
||||||
|
* ("foo") ["foo", "foo (1)"] "foo (2)"
|
||||||
|
* ("foo (1)") ["foo", "foo (1)"] "foo (2)"
|
||||||
|
* ("foo") ["foo", "foo (2)"] "foo (1)"
|
||||||
|
*
|
||||||
|
* Repl
|
||||||
|
*/
|
||||||
|
export const duplicateName = (name, allNames) => {
|
||||||
|
const baseName = name.split(" (")[0]
|
||||||
|
const isDuplicate = new RegExp(`${baseName}\\s\\((\\d+)\\)$`)
|
||||||
|
|
||||||
|
// get the sequence from matched names
|
||||||
|
const sequence = []
|
||||||
|
allNames.filter(n => {
|
||||||
|
if (n === baseName) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const match = n.match(isDuplicate)
|
||||||
|
if (match) {
|
||||||
|
sequence.push(parseInt(match[1]))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
sequence.sort((a, b) => a - b)
|
||||||
|
|
||||||
|
// get the next number in the sequence
|
||||||
|
let number
|
||||||
|
if (sequence.length === 0) {
|
||||||
|
number = 1
|
||||||
|
} else {
|
||||||
|
// get the next number in the sequence
|
||||||
|
for (let i = 0; i < sequence.length; i++) {
|
||||||
|
if (sequence[i] !== i + 1) {
|
||||||
|
number = i + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!number) {
|
||||||
|
number = sequence.length + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${baseName} (${number})`
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
const { duplicateName } = require("../duplicate")
|
||||||
|
|
||||||
|
describe("duplicate", () => {
|
||||||
|
|
||||||
|
describe("duplicates a name ", () => {
|
||||||
|
it("with a single existing", async () => {
|
||||||
|
const names = ["foo"]
|
||||||
|
const name = "foo"
|
||||||
|
|
||||||
|
const duplicate = duplicateName(name, names)
|
||||||
|
|
||||||
|
expect(duplicate).toBe("foo (1)")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("with multiple existing", async () => {
|
||||||
|
const names = ["foo", "foo (1)", "foo (2)"]
|
||||||
|
const name = "foo"
|
||||||
|
|
||||||
|
const duplicate = duplicateName(name, names)
|
||||||
|
|
||||||
|
expect(duplicate).toBe("foo (3)")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("with mixed multiple existing", async () => {
|
||||||
|
const names = ["foo", "foo (1)", "foo (2)", "bar", "bar (1)", "bar (2)"]
|
||||||
|
const name = "foo"
|
||||||
|
|
||||||
|
const duplicate = duplicateName(name, names)
|
||||||
|
|
||||||
|
expect(duplicate).toBe("foo (3)")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("with incomplete sequence", async () => {
|
||||||
|
const names = ["foo", "foo (2)", "foo (3)"]
|
||||||
|
const name = "foo"
|
||||||
|
|
||||||
|
const duplicate = duplicateName(name, names)
|
||||||
|
|
||||||
|
expect(duplicate).toBe("foo (1)")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,14 +1,19 @@
|
||||||
import { writable, get } from "svelte/store"
|
import { writable, get } from "svelte/store"
|
||||||
import { datasources, integrations, tables, views } from "./"
|
import { datasources, integrations, tables, views } from "./"
|
||||||
import api from "builderStore/api"
|
import api from "builderStore/api"
|
||||||
|
import { duplicateName } from "../../helpers/duplicate"
|
||||||
|
|
||||||
|
const sortQueries = queryList => {
|
||||||
|
queryList.sort((q1, q2) => {
|
||||||
|
return q1.name.localeCompare(q2.name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function createQueriesStore() {
|
export function createQueriesStore() {
|
||||||
const { subscribe, set, update } = writable({ list: [], selected: null })
|
const store = writable({ list: [], selected: null })
|
||||||
|
const { subscribe, set, update } = store
|
||||||
|
|
||||||
return {
|
const actions = {
|
||||||
subscribe,
|
|
||||||
set,
|
|
||||||
update,
|
|
||||||
init: async () => {
|
init: async () => {
|
||||||
const response = await api.get(`/api/queries`)
|
const response = await api.get(`/api/queries`)
|
||||||
const json = await response.json()
|
const json = await response.json()
|
||||||
|
@ -17,6 +22,7 @@ export function createQueriesStore() {
|
||||||
fetch: async () => {
|
fetch: async () => {
|
||||||
const response = await api.get(`/api/queries`)
|
const response = await api.get(`/api/queries`)
|
||||||
const json = await response.json()
|
const json = await response.json()
|
||||||
|
sortQueries(json)
|
||||||
update(state => ({ ...state, list: json }))
|
update(state => ({ ...state, list: json }))
|
||||||
return json
|
return json
|
||||||
},
|
},
|
||||||
|
@ -49,6 +55,7 @@ export function createQueriesStore() {
|
||||||
} else {
|
} else {
|
||||||
queries.push(json)
|
queries.push(json)
|
||||||
}
|
}
|
||||||
|
sortQueries(queries)
|
||||||
return { list: queries, selected: json._id }
|
return { list: queries, selected: json._id }
|
||||||
})
|
})
|
||||||
return json
|
return json
|
||||||
|
@ -76,6 +83,27 @@ export function createQueriesStore() {
|
||||||
})
|
})
|
||||||
return response
|
return response
|
||||||
},
|
},
|
||||||
|
duplicate: async query => {
|
||||||
|
let list = get(store).list
|
||||||
|
const newQuery = { ...query }
|
||||||
|
const datasourceId = query.datasourceId
|
||||||
|
|
||||||
|
delete newQuery._id
|
||||||
|
delete newQuery._rev
|
||||||
|
newQuery.name = duplicateName(
|
||||||
|
query.name,
|
||||||
|
list.map(q => q.name)
|
||||||
|
)
|
||||||
|
|
||||||
|
actions.save(datasourceId, newQuery)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
subscribe,
|
||||||
|
set,
|
||||||
|
update,
|
||||||
|
...actions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ jest.mock('builderStore/api');
|
||||||
import { SOME_QUERY, SAVE_QUERY_RESPONSE } from './fixtures/queries'
|
import { SOME_QUERY, SAVE_QUERY_RESPONSE } from './fixtures/queries'
|
||||||
|
|
||||||
import { createQueriesStore } from "../queries"
|
import { createQueriesStore } from "../queries"
|
||||||
import { datasources } from '../datasources'
|
|
||||||
|
|
||||||
describe("Queries Store", () => {
|
describe("Queries Store", () => {
|
||||||
let store = createQueriesStore()
|
let store = createQueriesStore()
|
||||||
|
|
Loading…
Reference in New Issue