Feat: Add collection fields as "extra" query config for MongoDB

This commit is contained in:
Christo 2021-07-08 14:38:49 +02:00
parent aa2691a452
commit 4096f4857c
5 changed files with 191 additions and 11 deletions

View File

@ -0,0 +1,48 @@
<script>
import { Select, Label, Input } from "@budibase/bbui"
/**
* This component takes the query object and populates the 'extra' property
* when a datasource has specified a configuration for these fields in SCHEMA.extra
*/
export let populateExtraQuery
export let config
export let query
$: extraFields = Object.keys(config).map(key => ({
...config[key],
key,
}))
$: extraQueryFields = query.fields.extra || {}
</script>
{#each extraFields as { key, displayName, type }}
<div class="config-field">
<Label>{displayName}</Label>
{#if type === "string"}
<Input
on:change={() => populateExtraQuery(extraQueryFields)}
bind:value={extraQueryFields[key]}
/>
{/if}
{#if type === "list"}
<Select
on:change={() => populateExtraQuery(extraQueryFields)}
bind:value={extraQueryFields[key]}
options={config[key].data[query.queryVerb]}
getOptionLabel={current => current}
/>
{/if}
</div>
{/each}
<style>
.config-field {
display: grid;
grid-template-columns: 20% 1fr;
grid-gap: var(--spacing-l);
align-items: center;
}
</style>

View File

@ -15,6 +15,7 @@
} from "@budibase/bbui"
import { notifications, Divider } from "@budibase/bbui"
import api from "builderStore/api"
import ExtraQueryConfig from "./ExtraQueryConfig.svelte"
import IntegrationQueryEditor from "components/integration/index.svelte"
import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte"
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
@ -60,6 +61,14 @@
fields = fields
}
function resetDependentFields() {
if (query.fields.extra) query.fields.extra = {}
}
function populateExtraQuery(extraQueryFields) {
query.fields.extra = extraQueryFields
}
async function previewQuery() {
try {
const response = await api.post(`/api/queries/preview`, {
@ -127,11 +136,19 @@
<Label>Function</Label>
<Select
bind:value={query.queryVerb}
on:change={resetDependentFields}
options={Object.keys(queryConfig)}
getOptionLabel={verb =>
queryConfig[verb]?.displayName || capitalise(verb)}
/>
</div>
{#if integrationInfo?.extra && query.queryVerb}
<ExtraQueryConfig
{query}
{populateExtraQuery}
config={integrationInfo.extra}
/>
{/if}
<ParameterBuilder bind:parameters={query.parameters} bindable={false} />
{/if}
</div>

View File

@ -30,6 +30,7 @@ function generateQueryValidation() {
default: Joi.string().allow(""),
})),
queryVerb: Joi.string().allow().required(),
extra: Joi.object().optional(),
schema: Joi.object({}).required().unknown(true)
}))
}
@ -39,6 +40,7 @@ function generateQueryPreviewValidation() {
return joiValidator.body(Joi.object({
fields: Joi.object().required(),
queryVerb: Joi.string().allow().required(),
extra: Joi.object().optional(),
datasourceId: Joi.string().required(),
parameters: Joi.object({}).required().unknown(true)
}))

View File

@ -49,6 +49,15 @@ export interface QueryDefinition {
urlDisplay?: boolean
}
export interface ExtraQueryConfig {
[key: string]: {
displayName: string,
type: string,
required: boolean
data?: object
}
}
export interface Integration {
docs: string
plus?: boolean
@ -58,6 +67,7 @@ export interface Integration {
query: {
[key: string]: QueryDefinition
}
extra?: ExtraQueryConfig
}
export interface SearchFilters {

View File

@ -10,7 +10,7 @@ module MongoDBModule {
interface MongoDBConfig {
connectionString: string
db: string
collection: string
// collection: string
}
const SCHEMA: Integration = {
@ -28,10 +28,6 @@ module MongoDBModule {
type: DatasourceFieldTypes.STRING,
required: true,
},
collection: {
type: DatasourceFieldTypes.STRING,
required: true,
},
},
query: {
create: {
@ -40,7 +36,31 @@ module MongoDBModule {
read: {
type: QueryTypes.JSON,
},
update: {
type: QueryTypes.JSON,
},
delete: {
type: QueryTypes.JSON,
}
},
extra: {
collection: {
displayName: "Collection",
type: DatasourceFieldTypes.STRING,
required: true,
},
actionTypes: {
displayName: "Action Types",
type: DatasourceFieldTypes.LIST,
required: true,
data: {
read: ['find', 'findOne', 'findOneAndUpdate', "count", "distinct"],
create: ['insertOne', 'insertMany'],
update: ['updateOne', 'updateMany'],
delete: ['deleteOne', 'deleteMany']
}
}
}
}
class MongoIntegration {
@ -56,12 +76,25 @@ module MongoDBModule {
return this.client.connect()
}
async create(query: { json: object }) {
async create(query: { json: object, extra: { [key: string]: string } }) {
try {
await this.connect()
const db = this.client.db(this.config.db)
const collection = db.collection(this.config.collection)
return collection.insertOne(query.json)
const collection = db.collection(query.extra.collection)
// For mongodb we add an extra actionType to specify
// which method we want to call on the collection
switch(query.extra.actionTypes) {
case 'insertOne': {
return collection.insertOne(query.json)
}
case 'insertMany': {
return collection.insertOne(query.json).toArray()
}
default: {
throw new Error(`actionType ${query.extra.actionTypes} does not exist on DB for create`)
}
}
} catch (err) {
console.error("Error writing to mongodb", err)
throw err
@ -70,12 +103,32 @@ module MongoDBModule {
}
}
async read(query: { json: object }) {
async read(query: { json: object, extra: { [key: string]: string } }) {
try {
await this.connect()
const db = this.client.db(this.config.db)
const collection = db.collection(this.config.collection)
return collection.find(query.json).toArray()
const collection = db.collection(query.extra.collection)
switch(query.extra.actionTypes) {
case 'find': {
return collection.find(query.json).toArray()
}
case 'findOne': {
return collection.findOne(query.json)
}
case 'findOneAndUpdate': {
return collection.findOneAndUpdate(query.json)
}
case 'count': {
return collection.countDocuments(query.json)
}
case 'distinct': {
return collection.distinct(query.json)
}
default: {
throw new Error(`actionType ${query.extra.actionTypes} does not exist on DB for read`)
}
}
} catch (err) {
console.error("Error querying mongodb", err)
throw err
@ -83,6 +136,56 @@ module MongoDBModule {
await this.client.close()
}
}
async update(query: { json: object, extra: { [key: string]: string } }) {
try {
await this.connect()
const db = this.client.db(this.config.db)
const collection = db.collection(query.extra.collection)
switch(query.extra.actionTypes) {
case 'updateOne': {
return collection.updateOne(query.json)
}
case 'updateMany': {
return collection.updateMany(query.json).toArray()
}
default: {
throw new Error(`actionType ${query.extra.actionTypes} does not exist on DB for update`)
}
}
} catch (err) {
console.error("Error writing to mongodb", err)
throw err
} finally {
await this.client.close()
}
}
async delete(query: { json: object, extra: { [key: string]: string } }) {
try {
await this.connect()
const db = this.client.db(this.config.db)
const collection = db.collection(query.extra.collection)
switch(query.extra.actionTypes) {
case 'deleteOne': {
return collection.deleteOne(query.json)
}
case 'deleteMany': {
return collection.deleteMany(query.json).toArray()
}
default: {
throw new Error(`actionType ${query.extra.actionTypes} does not exist on DB for delete`)
}
}
} catch (err) {
console.error("Error writing to mongodb", err)
throw err
} finally {
await this.client.close()
}
}
}
module.exports = {