Adding all response capabilities.
This commit is contained in:
parent
e064237981
commit
5936fe0f5f
18 changed files with 212 additions and 189 deletions
|
@ -8,6 +8,7 @@
|
||||||
export let error = null
|
export let error = null
|
||||||
export let id = null
|
export let id = null
|
||||||
export let height = null
|
export let height = null
|
||||||
|
export let minHeight = null
|
||||||
export const getCaretPosition = () => ({
|
export const getCaretPosition = () => ({
|
||||||
start: textarea.selectionStart,
|
start: textarea.selectionStart,
|
||||||
end: textarea.selectionEnd,
|
end: textarea.selectionEnd,
|
||||||
|
@ -23,7 +24,8 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={height ? `height: ${height}px;` : ""}
|
style={(height ? `height: ${height}px;` : "") +
|
||||||
|
(minHeight ? `min-height: ${minHeight}px` : "")}
|
||||||
class="spectrum-Textfield spectrum-Textfield--multiline"
|
class="spectrum-Textfield spectrum-Textfield--multiline"
|
||||||
class:is-invalid={!!error}
|
class:is-invalid={!!error}
|
||||||
class:is-disabled={disabled}
|
class:is-disabled={disabled}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
export let error = null
|
export let error = null
|
||||||
export let getCaretPosition = null
|
export let getCaretPosition = null
|
||||||
export let height = null
|
export let height = null
|
||||||
|
export let minHeight = null
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const onChange = e => {
|
const onChange = e => {
|
||||||
|
@ -27,6 +28,7 @@
|
||||||
{value}
|
{value}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
{height}
|
{height}
|
||||||
|
{minHeight}
|
||||||
on:change={onChange}
|
on:change={onChange}
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { datasources, tables } from "../stores/backend"
|
import { datasources, tables } from "../stores/backend"
|
||||||
import { IntegrationNames } from "../constants"
|
import { IntegrationNames } from "../constants/backend"
|
||||||
import analytics, { Events } from "../analytics"
|
import analytics, { Events } from "../analytics"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import cloneDeep from "lodash/cloneDeepWith"
|
import cloneDeep from "lodash/cloneDeepWith"
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
$: editRowComponent = isUsersTable ? CreateEditUser : CreateEditRow
|
$: editRowComponent = isUsersTable ? CreateEditUser : CreateEditRow
|
||||||
$: {
|
$: {
|
||||||
UNSORTABLE_TYPES.forEach(type => {
|
UNSORTABLE_TYPES.forEach(type => {
|
||||||
Object.values(schema).forEach(col => {
|
Object.values(schema || {}).forEach(col => {
|
||||||
if (col.type === type) {
|
if (col.type === type) {
|
||||||
col.sortable = false
|
col.sortable = false
|
||||||
}
|
}
|
||||||
|
@ -113,16 +113,16 @@
|
||||||
|
|
||||||
<Layout noPadding gap="S">
|
<Layout noPadding gap="S">
|
||||||
<div>
|
<div>
|
||||||
<div class="table-title">
|
{#if title}
|
||||||
{#if title}
|
<div class="table-title">
|
||||||
<Heading size="S">{title}</Heading>
|
<Heading size="S">{title}</Heading>
|
||||||
{/if}
|
{#if loading}
|
||||||
{#if loading}
|
<div transition:fade|local>
|
||||||
<div transition:fade|local>
|
<Spinner size="10" />
|
||||||
<Spinner size="10" />
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
{/if}
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
<div class="popovers">
|
<div class="popovers">
|
||||||
<slot />
|
<slot />
|
||||||
{#if !isUsersTable && selectedRows.length > 0}
|
{#if !isUsersTable && selectedRows.length > 0}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
|
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
|
||||||
import { capitalise } from "helpers"
|
import { capitalise } from "helpers"
|
||||||
import { IntegrationTypes } from "constants"
|
import { IntegrationTypes } from "constants/backend"
|
||||||
|
|
||||||
export let datasource
|
export let datasource
|
||||||
export let schema
|
export let schema
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import ICONS from "../icons"
|
import ICONS from "../icons"
|
||||||
import api from "builderStore/api"
|
import api from "builderStore/api"
|
||||||
import { IntegrationNames, IntegrationTypes } from "constants"
|
import { IntegrationNames, IntegrationTypes } from "constants/backend"
|
||||||
import CreateTableModal from "components/backend/TableNavigator/modals/CreateTableModal.svelte"
|
import CreateTableModal from "components/backend/TableNavigator/modals/CreateTableModal.svelte"
|
||||||
import DatasourceConfigModal from "components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte"
|
import DatasourceConfigModal from "components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte"
|
||||||
import { createRestDatasource } from "builderStore/datasource"
|
import { createRestDatasource } from "builderStore/datasource"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import { goto } from "@roxi/routify"
|
import { goto } from "@roxi/routify"
|
||||||
import { ModalContent, notifications, Body, Layout } from "@budibase/bbui"
|
import { ModalContent, notifications, Body, Layout } from "@budibase/bbui"
|
||||||
import IntegrationConfigForm from "components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte"
|
import IntegrationConfigForm from "components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte"
|
||||||
import { IntegrationNames } from "constants"
|
import { IntegrationNames } from "constants/backend"
|
||||||
import cloneDeep from "lodash/cloneDeepWith"
|
import cloneDeep from "lodash/cloneDeepWith"
|
||||||
import { saveDatasource as save } from "builderStore/datasource"
|
import { saveDatasource as save } from "builderStore/datasource"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script>
|
||||||
|
import { TextArea } from "@budibase/bbui"
|
||||||
|
|
||||||
|
export let data
|
||||||
|
export let height
|
||||||
|
export let minHeight = "120"
|
||||||
|
|
||||||
|
$: string = JSON.stringify(data || {}, null, 2)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<TextArea disabled value={string} {height} {minHeight} />
|
|
@ -1,5 +1,12 @@
|
||||||
<script>
|
<script>
|
||||||
import { Icon, ActionButton, Input, Label, Toggle } from "@budibase/bbui"
|
import {
|
||||||
|
Icon,
|
||||||
|
ActionButton,
|
||||||
|
Input,
|
||||||
|
Label,
|
||||||
|
Toggle,
|
||||||
|
Select,
|
||||||
|
} from "@budibase/bbui"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { lowercase } from "helpers"
|
import { lowercase } from "helpers"
|
||||||
|
|
||||||
|
@ -12,6 +19,7 @@
|
||||||
export let name
|
export let name
|
||||||
export let headings = false
|
export let headings = false
|
||||||
export let activity = false
|
export let activity = false
|
||||||
|
export let options
|
||||||
|
|
||||||
let fields = Object.entries(object).map(([name, value]) => ({ name, value }))
|
let fields = Object.entries(object).map(([name, value]) => ({ name, value }))
|
||||||
|
|
||||||
|
@ -50,7 +58,15 @@
|
||||||
<div class="container" class:container-active={activity} class:readOnly>
|
<div class="container" class:container-active={activity} class:readOnly>
|
||||||
{#each fields as field, idx}
|
{#each fields as field, idx}
|
||||||
<Input placeholder="Key" bind:value={field.name} on:change={changed} />
|
<Input placeholder="Key" bind:value={field.name} on:change={changed} />
|
||||||
<Input placeholder="Value" bind:value={field.value} on:change={changed} />
|
{#if options}
|
||||||
|
<Select bind:value={field.value} on:change={changed} {options} />
|
||||||
|
{:else}
|
||||||
|
<Input
|
||||||
|
placeholder="Value"
|
||||||
|
bind:value={field.value}
|
||||||
|
on:change={changed}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
{#if activity}
|
{#if activity}
|
||||||
<Toggle />
|
<Toggle />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
Tab,
|
Tab,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { notifications, Divider } from "@budibase/bbui"
|
import { notifications, Divider } from "@budibase/bbui"
|
||||||
import api from "builderStore/api"
|
|
||||||
import ExtraQueryConfig from "./ExtraQueryConfig.svelte"
|
import ExtraQueryConfig from "./ExtraQueryConfig.svelte"
|
||||||
import IntegrationQueryEditor from "components/integration/index.svelte"
|
import IntegrationQueryEditor from "components/integration/index.svelte"
|
||||||
import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte"
|
import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte"
|
||||||
|
@ -28,23 +27,20 @@
|
||||||
} from "stores/backend"
|
} from "stores/backend"
|
||||||
import { capitalise } from "../../helpers"
|
import { capitalise } from "../../helpers"
|
||||||
import CodeMirrorEditor from "components/common/CodeMirrorEditor.svelte"
|
import CodeMirrorEditor from "components/common/CodeMirrorEditor.svelte"
|
||||||
import { Roles } from "constants/backend"
|
import JSONPreview from "./JSONPreview.svelte"
|
||||||
|
import { Roles, SchemaTypeOptions } from "constants/backend"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
|
import KeyValueBuilder from "./KeyValueBuilder.svelte"
|
||||||
|
import { fieldsToSchema, schemaToFields } from "helpers/data/utils"
|
||||||
|
|
||||||
export let query
|
export let query
|
||||||
|
|
||||||
let fields = query.schema ? schemaToFields(query.schema) : []
|
let fields = query?.schema ? schemaToFields(query.schema) : []
|
||||||
let parameters
|
let parameters
|
||||||
let data = []
|
let data = []
|
||||||
let roleId
|
let roleId
|
||||||
const transformerDocs =
|
const transformerDocs =
|
||||||
"https://docs.budibase.com/building-apps/data/transformers"
|
"https://docs.budibase.com/building-apps/data/transformers"
|
||||||
const typeOptions = [
|
|
||||||
{ label: "Text", value: "string" },
|
|
||||||
{ label: "Number", value: "number" },
|
|
||||||
{ label: "Boolean", value: "boolean" },
|
|
||||||
{ label: "Datetime", value: "datetime" },
|
|
||||||
]
|
|
||||||
|
|
||||||
$: datasource = $datasources.list.find(ds => ds._id === query.datasourceId)
|
$: datasource = $datasources.list.find(ds => ds._id === query.datasourceId)
|
||||||
$: query.schema = fieldsToSchema(fields)
|
$: query.schema = fieldsToSchema(fields)
|
||||||
|
@ -60,15 +56,6 @@
|
||||||
query.transformer = "return data"
|
query.transformer = "return data"
|
||||||
}
|
}
|
||||||
|
|
||||||
function newField() {
|
|
||||||
fields = [...fields, {}]
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteField(idx) {
|
|
||||||
fields.splice(idx, 1)
|
|
||||||
fields = fields
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetDependentFields() {
|
function resetDependentFields() {
|
||||||
if (query.fields.extra) {
|
if (query.fields.extra) {
|
||||||
query.fields.extra = {}
|
query.fields.extra = {}
|
||||||
|
@ -94,43 +81,18 @@
|
||||||
|
|
||||||
async function previewQuery() {
|
async function previewQuery() {
|
||||||
try {
|
try {
|
||||||
const response = await api.post(`/api/queries/preview`, {
|
const response = await queries.preview(query)
|
||||||
fields: query.fields,
|
if (response.rows.length === 0) {
|
||||||
queryVerb: query.queryVerb,
|
|
||||||
transformer: query.transformer,
|
|
||||||
parameters: query.parameters.reduce(
|
|
||||||
(acc, next) => ({
|
|
||||||
...acc,
|
|
||||||
[next.name]: next.default,
|
|
||||||
}),
|
|
||||||
{}
|
|
||||||
),
|
|
||||||
datasourceId: datasource._id,
|
|
||||||
})
|
|
||||||
const json = await response.json()
|
|
||||||
|
|
||||||
if (response.status !== 200) throw new Error(json.message)
|
|
||||||
|
|
||||||
data = json.rows || []
|
|
||||||
|
|
||||||
if (data.length === 0) {
|
|
||||||
notifications.info(
|
notifications.info(
|
||||||
"Query results empty. Please execute a query with results to create your schema."
|
"Query results empty. Please execute a query with results to create your schema."
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
data = response.rows
|
||||||
|
fields = response.schema
|
||||||
notifications.success("Query executed successfully.")
|
notifications.success("Query executed successfully.")
|
||||||
|
|
||||||
// Assume all the fields are strings and create a basic schema from the
|
|
||||||
// unique fields returned by the server
|
|
||||||
fields = json.schemaFields.map(field => ({
|
|
||||||
name: field,
|
|
||||||
type: "string",
|
|
||||||
}))
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notifications.error(`Query Error: ${err.message}`)
|
notifications.error(err)
|
||||||
console.error(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,26 +108,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function schemaToFields(schema) {
|
|
||||||
return Object.keys(schema).map(key => ({
|
|
||||||
name: key,
|
|
||||||
type: query.schema[key].type,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
function fieldsToSchema(fieldsToConvert) {
|
|
||||||
return fieldsToConvert.reduce(
|
|
||||||
(acc, next) => ({
|
|
||||||
...acc,
|
|
||||||
[next.name]: {
|
|
||||||
name: next.name,
|
|
||||||
type: next.type,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (!query || !query._id) {
|
if (!query || !query._id) {
|
||||||
roleId = Roles.BASIC
|
roleId = Roles.BASIC
|
||||||
|
@ -271,29 +213,15 @@
|
||||||
{#if data}
|
{#if data}
|
||||||
<Tabs selected="JSON">
|
<Tabs selected="JSON">
|
||||||
<Tab title="JSON">
|
<Tab title="JSON">
|
||||||
<pre
|
<JSONPreview data={data[0]} minHeight="120" />
|
||||||
class="preview">
|
|
||||||
<!-- prettier-ignore -->
|
|
||||||
{#if !data[0]}
|
|
||||||
Please run your query to fetch some data.
|
|
||||||
{:else}
|
|
||||||
{JSON.stringify(data[0], undefined, 2)}
|
|
||||||
{/if}
|
|
||||||
</pre>
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="Schema">
|
<Tab title="Schema">
|
||||||
<Layout gap="S">
|
<KeyValueBuilder
|
||||||
{#each fields as field, idx}
|
bind:object={fields}
|
||||||
<div class="field">
|
name="field"
|
||||||
<Input placeholder="Field Name" bind:value={field.name} />
|
headings
|
||||||
<Select bind:value={field.type} options={typeOptions} />
|
options={SchemaTypeOptions}
|
||||||
<Icon name="bleClose" on:click={() => deleteField(idx)} />
|
/>
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
<div>
|
|
||||||
<Button secondary on:click={newField}>Add Field</Button>
|
|
||||||
</div>
|
|
||||||
</Layout>
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="Preview">
|
<Tab title="Preview">
|
||||||
<ExternalDataSourceTable {query} {data} />
|
<ExternalDataSourceTable {query} {data} />
|
||||||
|
@ -322,29 +250,11 @@
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.field {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr 5%;
|
|
||||||
gap: var(--spacing-l);
|
|
||||||
}
|
|
||||||
|
|
||||||
.viewer {
|
.viewer {
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
width: 640px;
|
width: 640px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview {
|
|
||||||
height: 100%;
|
|
||||||
min-height: 120px;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
background-color: var(--grey-2);
|
|
||||||
padding: var(--spacing-m);
|
|
||||||
border-radius: 8px;
|
|
||||||
color: var(--ink);
|
|
||||||
}
|
|
||||||
|
|
||||||
.viewer-controls {
|
.viewer-controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
|
@ -154,3 +154,58 @@ export const ALLOWABLE_NUMBER_TYPES = ALLOWABLE_NUMBER_OPTIONS.map(
|
||||||
export const SWITCHABLE_TYPES = ALLOWABLE_NUMBER_TYPES.concat(
|
export const SWITCHABLE_TYPES = ALLOWABLE_NUMBER_TYPES.concat(
|
||||||
ALLOWABLE_STRING_TYPES
|
ALLOWABLE_STRING_TYPES
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export const IntegrationTypes = {
|
||||||
|
POSTGRES: "POSTGRES",
|
||||||
|
MONGODB: "MONGODB",
|
||||||
|
COUCHDB: "COUCHDB",
|
||||||
|
S3: "S3",
|
||||||
|
MYSQL: "MYSQL",
|
||||||
|
REST: "REST",
|
||||||
|
DYNAMODB: "DYNAMODB",
|
||||||
|
ELASTICSEARCH: "ELASTICSEARCH",
|
||||||
|
SQL_SERVER: "SQL_SERVER",
|
||||||
|
AIRTABLE: "AIRTABLE",
|
||||||
|
ARANGODB: "ARANGODB",
|
||||||
|
ORACLE: "ORACLE",
|
||||||
|
INTERNAL: "INTERNAL",
|
||||||
|
}
|
||||||
|
|
||||||
|
export const IntegrationNames = {
|
||||||
|
[IntegrationTypes.POSTGRES]: "PostgreSQL",
|
||||||
|
[IntegrationTypes.MONGODB]: "MongoDB",
|
||||||
|
[IntegrationTypes.COUCHDB]: "CouchDB",
|
||||||
|
[IntegrationTypes.S3]: "S3",
|
||||||
|
[IntegrationTypes.MYSQL]: "MySQL",
|
||||||
|
[IntegrationTypes.REST]: "REST",
|
||||||
|
[IntegrationTypes.DYNAMODB]: "DynamoDB",
|
||||||
|
[IntegrationTypes.ELASTICSEARCH]: "ElasticSearch",
|
||||||
|
[IntegrationTypes.SQL_SERVER]: "SQL Server",
|
||||||
|
[IntegrationTypes.AIRTABLE]: "Airtable",
|
||||||
|
[IntegrationTypes.ARANGODB]: "ArangoDB",
|
||||||
|
[IntegrationTypes.ORACLE]: "Oracle",
|
||||||
|
[IntegrationTypes.INTERNAL]: "Internal",
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SchemaTypeOptions = [
|
||||||
|
{ label: "Text", value: "string" },
|
||||||
|
{ label: "Number", value: "number" },
|
||||||
|
{ label: "Boolean", value: "boolean" },
|
||||||
|
{ label: "Datetime", value: "datetime" },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const RawRestBodyTypes = {
|
||||||
|
NONE: "none",
|
||||||
|
FORM: "form",
|
||||||
|
ENCODED: "encoded",
|
||||||
|
JSON: "json",
|
||||||
|
TEXT: "text",
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RestBodyTypes = [
|
||||||
|
{ name: "none", value: "none" },
|
||||||
|
{ name: "form-data", value: "form" },
|
||||||
|
{ name: "x-www-form-encoded", value: "encoded" },
|
||||||
|
{ name: "raw (JSON)", value: "json" },
|
||||||
|
{ name: "raw (Text)", value: "text" },
|
||||||
|
]
|
||||||
|
|
|
@ -15,38 +15,6 @@ export const AppStatus = {
|
||||||
DEPLOYED: "published",
|
DEPLOYED: "published",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IntegrationTypes = {
|
|
||||||
POSTGRES: "POSTGRES",
|
|
||||||
MONGODB: "MONGODB",
|
|
||||||
COUCHDB: "COUCHDB",
|
|
||||||
S3: "S3",
|
|
||||||
MYSQL: "MYSQL",
|
|
||||||
REST: "REST",
|
|
||||||
DYNAMODB: "DYNAMODB",
|
|
||||||
ELASTICSEARCH: "ELASTICSEARCH",
|
|
||||||
SQL_SERVER: "SQL_SERVER",
|
|
||||||
AIRTABLE: "AIRTABLE",
|
|
||||||
ARANGODB: "ARANGODB",
|
|
||||||
ORACLE: "ORACLE",
|
|
||||||
INTERNAL: "INTERNAL",
|
|
||||||
}
|
|
||||||
|
|
||||||
export const IntegrationNames = {
|
|
||||||
[IntegrationTypes.POSTGRES]: "PostgreSQL",
|
|
||||||
[IntegrationTypes.MONGODB]: "MongoDB",
|
|
||||||
[IntegrationTypes.COUCHDB]: "CouchDB",
|
|
||||||
[IntegrationTypes.S3]: "S3",
|
|
||||||
[IntegrationTypes.MYSQL]: "MySQL",
|
|
||||||
[IntegrationTypes.REST]: "REST",
|
|
||||||
[IntegrationTypes.DYNAMODB]: "DynamoDB",
|
|
||||||
[IntegrationTypes.ELASTICSEARCH]: "ElasticSearch",
|
|
||||||
[IntegrationTypes.SQL_SERVER]: "SQL Server",
|
|
||||||
[IntegrationTypes.AIRTABLE]: "Airtable",
|
|
||||||
[IntegrationTypes.ARANGODB]: "ArangoDB",
|
|
||||||
[IntegrationTypes.ORACLE]: "Oracle",
|
|
||||||
[IntegrationTypes.INTERNAL]: "Internal",
|
|
||||||
}
|
|
||||||
|
|
||||||
// fields on the user table that cannot be edited
|
// fields on the user table that cannot be edited
|
||||||
export const UNEDITABLE_USER_FIELDS = [
|
export const UNEDITABLE_USER_FIELDS = [
|
||||||
"email",
|
"email",
|
||||||
|
@ -66,22 +34,6 @@ export const LAYOUT_NAMES = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RawRestBodyTypes = {
|
|
||||||
NONE: "none",
|
|
||||||
FORM: "form",
|
|
||||||
ENCODED: "encoded",
|
|
||||||
JSON: "json",
|
|
||||||
TEXT: "text",
|
|
||||||
}
|
|
||||||
|
|
||||||
export const RestBodyTypes = [
|
|
||||||
{ name: "none", value: "none" },
|
|
||||||
{ name: "form-data", value: "form" },
|
|
||||||
{ name: "x-www-form-encoded", value: "encoded" },
|
|
||||||
{ name: "raw (JSON)", value: "json" },
|
|
||||||
{ name: "raw (Text)", value: "text" },
|
|
||||||
]
|
|
||||||
|
|
||||||
export const BUDIBASE_INTERNAL_DB = "bb_internal"
|
export const BUDIBASE_INTERNAL_DB = "bb_internal"
|
||||||
|
|
||||||
export const APP_NAME_REGEX = /^[\w\s]+$/
|
export const APP_NAME_REGEX = /^[\w\s]+$/
|
||||||
|
|
19
packages/builder/src/helpers/data/utils.js
Normal file
19
packages/builder/src/helpers/data/utils.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
export function schemaToFields(schema) {
|
||||||
|
const response = {}
|
||||||
|
if (schema && typeof schema === "object") {
|
||||||
|
for (let [field, value] of Object.entries(schema)) {
|
||||||
|
response[field] = value?.type || "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fieldsToSchema(fields) {
|
||||||
|
const response = {}
|
||||||
|
if (fields && typeof fields === "object") {
|
||||||
|
for (let [name, type] of Object.entries(fields)) {
|
||||||
|
response[name] = { name, type }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { params } from "@roxi/routify"
|
import { params } from "@roxi/routify"
|
||||||
import { queries, datasources } from "stores/backend"
|
import { queries, datasources } from "stores/backend"
|
||||||
import { IntegrationTypes } from "constants"
|
import { IntegrationTypes } from "constants/backend"
|
||||||
import { goto } from "@roxi/routify"
|
import { goto } from "@roxi/routify"
|
||||||
|
|
||||||
if ($params.query) {
|
if ($params.query) {
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
const datasource = $datasources.list.find(
|
const datasource = $datasources.list.find(
|
||||||
ds => ds._id === $datasources.selected
|
ds => ds._id === $datasources.selected
|
||||||
)
|
)
|
||||||
if (datasource.source === IntegrationTypes.REST) {
|
if (datasource?.source === IntegrationTypes.REST) {
|
||||||
$goto("../rest")
|
$goto("../rest")
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { Body } from "@budibase/bbui"
|
import { Body } from "@budibase/bbui"
|
||||||
import { RawRestBodyTypes } from "constants"
|
import { RawRestBodyTypes } from "constants/backend"
|
||||||
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
|
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
|
||||||
import CodeMirrorEditor, {
|
import CodeMirrorEditor, {
|
||||||
EditorModes,
|
EditorModes,
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
import PlusConfigForm from "components/backend/DatasourceNavigator/TableIntegrationMenu/PlusConfigForm.svelte"
|
import PlusConfigForm from "components/backend/DatasourceNavigator/TableIntegrationMenu/PlusConfigForm.svelte"
|
||||||
import ICONS from "components/backend/DatasourceNavigator/icons"
|
import ICONS from "components/backend/DatasourceNavigator/icons"
|
||||||
import VerbRenderer from "./_components/VerbRenderer.svelte"
|
import VerbRenderer from "./_components/VerbRenderer.svelte"
|
||||||
import { IntegrationTypes } from "constants"
|
import { IntegrationTypes } from "constants/backend"
|
||||||
import { isEqual } from "lodash"
|
import { isEqual } from "lodash"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
Label,
|
Label,
|
||||||
TextArea,
|
TextArea,
|
||||||
Table,
|
Table,
|
||||||
|
notifications,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
|
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
|
||||||
import EditableLabel from "components/common/inputs/EditableLabel.svelte"
|
import EditableLabel from "components/common/inputs/EditableLabel.svelte"
|
||||||
|
@ -24,13 +25,19 @@
|
||||||
import RestBodyInput from "../_components/RestBodyInput.svelte"
|
import RestBodyInput from "../_components/RestBodyInput.svelte"
|
||||||
import { capitalise } from "helpers"
|
import { capitalise } from "helpers"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { RestBodyTypes as bodyTypes } from "constants"
|
import { fieldsToSchema, schemaToFields } from "helpers/data/utils"
|
||||||
|
import {
|
||||||
|
RestBodyTypes as bodyTypes,
|
||||||
|
SchemaTypeOptions,
|
||||||
|
} from "constants/backend"
|
||||||
|
import JSONPreview from "components/integration/JSONPreview.svelte"
|
||||||
|
|
||||||
let query
|
let query
|
||||||
let breakQs = {}
|
let breakQs = {}
|
||||||
let url = ""
|
let url = ""
|
||||||
// test - { info: { code: 500, time: "455ms", size: "2.09KB" }}
|
// test - { info: { code: 500, time: "455ms", size: "2.09KB" }}
|
||||||
let response
|
let response
|
||||||
|
let schema
|
||||||
|
|
||||||
$: datasource = $datasources.list.find(ds => ds._id === query?.datasourceId)
|
$: datasource = $datasources.list.find(ds => ds._id === query?.datasourceId)
|
||||||
$: datasourceType = datasource?.source
|
$: datasourceType = datasource?.source
|
||||||
|
@ -102,15 +109,38 @@
|
||||||
|
|
||||||
function learnMoreBanner() {}
|
function learnMoreBanner() {}
|
||||||
|
|
||||||
function saveQuery() {}
|
function buildQuery() {
|
||||||
|
const newQuery = { ...query }
|
||||||
|
const queryString = buildQueryString(breakQs)
|
||||||
|
newQuery.fields.path = url.split("?")[0]
|
||||||
|
newQuery.fields.queryString = queryString
|
||||||
|
return newQuery
|
||||||
|
}
|
||||||
|
|
||||||
function sendQuery() {}
|
function saveQuery() {
|
||||||
|
query.schema = fieldsToSchema(schema)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runQuery() {
|
||||||
|
try {
|
||||||
|
response = await queries.preview(buildQuery(query))
|
||||||
|
if (response.rows.length === 0) {
|
||||||
|
notifications.info("Request did not return any data.")
|
||||||
|
} else {
|
||||||
|
response.info = response.info || { code: 200 }
|
||||||
|
notifications.success("Request sent successfully.")
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
notifications.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
query = getSelectedQuery()
|
query = getSelectedQuery()
|
||||||
const qs = query?.fields.queryString
|
const qs = query?.fields.queryString
|
||||||
breakQs = breakQueryString(qs)
|
breakQs = breakQueryString(qs)
|
||||||
url = buildUrl(query.fields.path, qs)
|
url = buildUrl(query.fields.path, qs)
|
||||||
|
schema = schemaToFields(query.schema)
|
||||||
if (query && !query.transformer) {
|
if (query && !query.transformer) {
|
||||||
query.transformer = "return data"
|
query.transformer = "return data"
|
||||||
}
|
}
|
||||||
|
@ -149,7 +179,7 @@
|
||||||
<div class="url">
|
<div class="url">
|
||||||
<Input bind:value={url} />
|
<Input bind:value={url} />
|
||||||
</div>
|
</div>
|
||||||
<Button cta disabled={!url} on:click={sendQuery}>Send</Button>
|
<Button cta disabled={!url} on:click={runQuery}>Send</Button>
|
||||||
</div>
|
</div>
|
||||||
<Tabs selected="Params" quiet noPadding noHorizPadding>
|
<Tabs selected="Params" quiet noPadding noHorizPadding>
|
||||||
<Tab title="Params">
|
<Tab title="Params">
|
||||||
|
@ -203,20 +233,17 @@
|
||||||
{:else}
|
{:else}
|
||||||
<Tabs selected="JSON" quiet noPadding noHorizPadding>
|
<Tabs selected="JSON" quiet noPadding noHorizPadding>
|
||||||
<Tab title="JSON">
|
<Tab title="JSON">
|
||||||
<CodeMirrorEditor
|
<div>
|
||||||
height={300}
|
<JSONPreview height="300" data={response.rows[0]} />
|
||||||
value={response.text}
|
</div>
|
||||||
resize="vertical"
|
|
||||||
readonly
|
|
||||||
on:change={e => (query.transformer = e.detail)}
|
|
||||||
/>
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="Schema">
|
<Tab title="Schema">
|
||||||
<KeyValueBuilder
|
<KeyValueBuilder
|
||||||
bind:object={response.schemaFields}
|
bind:object={response.schema}
|
||||||
name="header"
|
name="header"
|
||||||
headings
|
headings
|
||||||
activity
|
activity
|
||||||
|
options={SchemaTypeOptions}
|
||||||
/>
|
/>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="Raw">
|
<Tab title="Raw">
|
||||||
|
@ -225,7 +252,7 @@
|
||||||
<Tab title="Preview">
|
<Tab title="Preview">
|
||||||
{#if response}
|
{#if response}
|
||||||
<Table
|
<Table
|
||||||
schema={response?.schemaFields}
|
schema={response?.schema}
|
||||||
data={response?.rows}
|
data={response?.rows}
|
||||||
allowEditColumns={false}
|
allowEditColumns={false}
|
||||||
allowEditRows={false}
|
allowEditRows={false}
|
||||||
|
|
|
@ -62,6 +62,35 @@ export function createQueriesStore() {
|
||||||
unselect: () => {
|
unselect: () => {
|
||||||
update(state => ({ ...state, selected: null }))
|
update(state => ({ ...state, selected: null }))
|
||||||
},
|
},
|
||||||
|
preview: async query => {
|
||||||
|
const response = await api.post("/api/queries/preview", {
|
||||||
|
fields: query.fields,
|
||||||
|
queryVerb: query.queryVerb,
|
||||||
|
transformer: query.transformer,
|
||||||
|
parameters: query.parameters.reduce(
|
||||||
|
(acc, next) => ({
|
||||||
|
...acc,
|
||||||
|
[next.name]: next.default,
|
||||||
|
}),
|
||||||
|
{}
|
||||||
|
),
|
||||||
|
datasourceId: query.datasourceId,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.status !== 200) {
|
||||||
|
const error = await response.text()
|
||||||
|
throw `Query error: ${error}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const json = await response.json()
|
||||||
|
// Assume all the fields are strings and create a basic schema from the
|
||||||
|
// unique fields returned by the server
|
||||||
|
const schema = {}
|
||||||
|
for (let field of json.schemaFields) {
|
||||||
|
schema[field] = "string"
|
||||||
|
}
|
||||||
|
return { ...json, schema, rows: json.rows || [] }
|
||||||
|
},
|
||||||
delete: async query => {
|
delete: async query => {
|
||||||
const response = await api.delete(
|
const response = await api.delete(
|
||||||
`/api/queries/${query._id}/${query._rev}`
|
`/api/queries/${query._id}/${query._rev}`
|
||||||
|
|
Loading…
Reference in a new issue