forked from tsai/budibase
59 changed files with 1988 additions and 580 deletions
@ -1,15 +1,37 @@ |
|||
<script> |
|||
import { Input, TextArea, Spacer } from "@budibase/bbui" |
|||
import { Label, Input, TextArea, Spacer } from "@budibase/bbui" |
|||
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte" |
|||
|
|||
export let integration |
|||
|
|||
let unsaved = false |
|||
</script> |
|||
|
|||
<form> |
|||
{#each Object.keys(integration) as configKey} |
|||
<Input |
|||
type={integration[configKey].type} |
|||
label={configKey} |
|||
bind:value={integration[configKey]} /> |
|||
<Spacer large /> |
|||
{#if typeof integration[configKey] === 'object'} |
|||
<Label small>{configKey}</Label> |
|||
<Spacer small /> |
|||
<KeyValueBuilder bind:object={integration[configKey]} on:change /> |
|||
{:else} |
|||
<div class="form-row"> |
|||
<Label small>{configKey}</Label> |
|||
<Input |
|||
outline |
|||
type={integration[configKey].type} |
|||
on:change |
|||
bind:value={integration[configKey]} /> |
|||
</div> |
|||
{/if} |
|||
{/each} |
|||
</form> |
|||
|
|||
<style> |
|||
.form-row { |
|||
display: grid; |
|||
grid-template-columns: 20% 1fr; |
|||
grid-gap: var(--spacing-l); |
|||
align-items: center; |
|||
margin-bottom: var(--spacing-m); |
|||
} |
|||
</style> |
|||
|
|||
@ -0,0 +1,36 @@ |
|||
<script> |
|||
export let width = "100" |
|||
export let height = "100" |
|||
</script> |
|||
|
|||
<svg |
|||
{width} |
|||
{height} |
|||
viewBox="0 0 120 120" |
|||
fill="none" |
|||
xmlns="http://www.w3.org/2000/svg"> |
|||
<path |
|||
d="M103.125 20.625H68.25L60.375 |
|||
36.375H16.5V99.375H111V20.625H103.125ZM103.125 36.375H72.375L76.5 |
|||
28.5H103.125V36.375Z" |
|||
fill="#FFBA58" /> |
|||
<path |
|||
d="M75 46.875V52.5H60C58.0127 52.5059 56.1085 53.298 54.7033 54.7033C53.298 |
|||
56.1085 52.5059 58.0127 52.5 60V75H46.875C44.3886 75 42.004 75.9877 40.2459 |
|||
77.7459C38.4877 79.504 37.5 81.8886 37.5 84.375C37.5 86.8614 38.4877 89.246 |
|||
40.2459 91.0041C42.004 92.7623 44.3886 93.75 46.875 |
|||
93.75H52.5V108.75C52.5059 110.737 53.298 112.642 54.7033 114.047C56.1085 |
|||
115.452 58.0127 116.244 60 116.25H74.25V110.625C74.25 107.94 75.3167 105.364 |
|||
77.2155 103.466C79.1143 101.567 81.6897 100.5 84.375 100.5C87.0603 100.5 |
|||
89.6357 101.567 91.5345 103.466C93.4333 105.364 94.5 107.94 94.5 |
|||
110.625V116.25H108.75C110.737 116.244 112.642 115.452 114.047 |
|||
114.047C115.452 112.642 116.244 110.737 116.25 108.75V94.5H110.625C107.94 |
|||
94.5 105.364 93.4333 103.466 91.5345C101.567 89.6357 100.5 87.0603 100.5 |
|||
84.375C100.5 81.6897 101.567 79.1143 103.466 77.2155C105.364 75.3167 107.94 |
|||
74.25 110.625 74.25H116.25V60C116.244 58.0127 115.452 56.1085 114.047 |
|||
54.7033C112.642 53.298 110.737 52.5059 108.75 52.5H93.75V46.875C93.75 |
|||
44.3886 92.7623 42.004 91.0041 40.2459C89.246 38.4877 86.8614 37.5 84.375 |
|||
37.5C81.8886 37.5 79.504 38.4877 77.7459 40.2459C75.9877 42.004 75 44.3886 |
|||
75 46.875Z" |
|||
fill="#E76A00" /> |
|||
</svg> |
|||
@ -1,61 +0,0 @@ |
|||
<script> |
|||
import { goto, params } from "@sveltech/routify" |
|||
import { backendUiStore, store } from "builderStore" |
|||
import { notifier } from "builderStore/store/notifications" |
|||
import { Input, Label, ModalContent, Button, Spacer } from "@budibase/bbui" |
|||
import TableIntegrationMenu from "../TableIntegrationMenu/index.svelte" |
|||
import analytics from "analytics" |
|||
|
|||
let modal |
|||
let error = "" |
|||
|
|||
let name |
|||
let source |
|||
let integration |
|||
let datasource |
|||
|
|||
function checkValid(evt) { |
|||
const datasourceName = evt.target.value |
|||
if ( |
|||
$backendUiStore.datasources?.some( |
|||
datasource => datasource.name === datasourceName |
|||
) |
|||
) { |
|||
error = `Datasource with name ${tableName} already exists. Please choose another name.` |
|||
return |
|||
} |
|||
error = "" |
|||
} |
|||
|
|||
async function saveDatasource() { |
|||
const { type, ...config } = integration |
|||
|
|||
// Create datasource |
|||
await backendUiStore.actions.datasources.save({ |
|||
name, |
|||
source: type, |
|||
config, |
|||
}) |
|||
notifier.success(`Datasource ${name} created successfully.`) |
|||
analytics.captureEvent("Datasource Created", { name }) |
|||
|
|||
// Navigate to new datasource |
|||
$goto(`./datasource/${datasource._id}`) |
|||
} |
|||
</script> |
|||
|
|||
<ModalContent |
|||
title="Create Datasource" |
|||
confirmText="Create" |
|||
onConfirm={saveDatasource} |
|||
disabled={error || !name}> |
|||
<Input |
|||
data-cy="datasource-name-input" |
|||
thin |
|||
label="Datasource Name" |
|||
on:input={checkValid} |
|||
bind:value={name} |
|||
{error} /> |
|||
<Label grey extraSmall>Create Integrated Table from External Source</Label> |
|||
<TableIntegrationMenu bind:integration /> |
|||
</ModalContent> |
|||
@ -1,39 +0,0 @@ |
|||
<script> |
|||
import { backendUiStore, store, allScreens } from "builderStore" |
|||
import { notifier } from "builderStore/store/notifications" |
|||
import { DropdownMenu, Button, Input, TextButton, Icon } from "@budibase/bbui" |
|||
import ConfirmDialog from "components/common/ConfirmDialog.svelte" |
|||
import IntegrationConfigForm from "../TableIntegrationMenu//IntegrationConfigForm.svelte" |
|||
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns" |
|||
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte" |
|||
|
|||
export let bindable |
|||
export let parameters |
|||
|
|||
let anchor |
|||
let dropdown |
|||
let confirmDeleteDialog |
|||
|
|||
function hideEditor() { |
|||
dropdown?.hide() |
|||
} |
|||
</script> |
|||
|
|||
<div on:click|stopPropagation bind:this={anchor}> |
|||
<TextButton text on:click={dropdown.show} active={false}> |
|||
<Icon name="add" /> |
|||
Add Parameters |
|||
</TextButton> |
|||
<DropdownMenu align="right" {anchor} bind:this={dropdown}> |
|||
<div class="wrapper"> |
|||
<ParameterBuilder bind:parameters {bindable} /> |
|||
</div> |
|||
</DropdownMenu> |
|||
</div> |
|||
|
|||
<style> |
|||
.wrapper { |
|||
padding: var(--spacing-xl); |
|||
min-width: 600px; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,50 @@ |
|||
<script> |
|||
import { Button, Input } from "@budibase/bbui" |
|||
|
|||
export let object = {} |
|||
export let readOnly |
|||
|
|||
let fields = Object.entries(object).map(([name, value]) => ({ name, value })) |
|||
|
|||
$: object = fields.reduce( |
|||
(acc, next) => ({ ...acc, [next.name]: next.value }), |
|||
{} |
|||
) |
|||
|
|||
function addEntry() { |
|||
fields = [...fields, {}] |
|||
} |
|||
|
|||
function deleteEntry(idx) { |
|||
fields.splice(idx, 1) |
|||
fields = fields |
|||
} |
|||
</script> |
|||
|
|||
<!-- Builds Objects with Key Value Pairs. Useful for building things like Request Headers. --> |
|||
<div class="container" class:readOnly> |
|||
{#each fields as field, idx} |
|||
<Input placeholder="Key" thin outline bind:value={field.name} /> |
|||
<Input placeholder="Value" thin outline bind:value={field.value} /> |
|||
{#if !readOnly} |
|||
<i class="ri-close-circle-fill" on:click={() => deleteEntry(idx)} /> |
|||
{/if} |
|||
{/each} |
|||
</div> |
|||
{#if !readOnly} |
|||
<Button secondary thin outline on:click={addEntry}>Add</Button> |
|||
{/if} |
|||
|
|||
<style> |
|||
.container { |
|||
display: grid; |
|||
grid-template-columns: 1fr 1fr 20px; |
|||
grid-gap: var(--spacing-m); |
|||
align-items: center; |
|||
margin-bottom: var(--spacing-m); |
|||
} |
|||
|
|||
.ri-close-circle-fill { |
|||
cursor: pointer; |
|||
} |
|||
</style> |
|||
File diff suppressed because it is too large
@ -0,0 +1,175 @@ |
|||
const fetch = require("node-fetch") |
|||
const { FIELD_TYPES, QUERY_TYPES } = require("./Integration") |
|||
|
|||
const SCHEMA = { |
|||
docs: "https://github.com/node-fetch/node-fetch", |
|||
description: |
|||
"Representational state transfer (REST) is a de-facto standard for a software architecture for interactive applications that typically use multiple Web services. ", |
|||
friendlyName: "REST API", |
|||
datasource: { |
|||
url: { |
|||
type: FIELD_TYPES.STRING, |
|||
default: "localhost", |
|||
required: true, |
|||
}, |
|||
defaultHeaders: { |
|||
type: FIELD_TYPES.OBJECT, |
|||
required: false, |
|||
default: {}, |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
displayName: "POST", |
|||
type: QUERY_TYPES.FIELDS, |
|||
urlDisplay: true, |
|||
fields: { |
|||
path: { |
|||
type: FIELD_TYPES.STRING, |
|||
}, |
|||
queryString: { |
|||
type: FIELD_TYPES.STRING, |
|||
}, |
|||
headers: { |
|||
type: FIELD_TYPES.OBJECT, |
|||
}, |
|||
requestBody: { |
|||
type: FIELD_TYPES.JSON, |
|||
}, |
|||
}, |
|||
}, |
|||
read: { |
|||
displayName: "GET", |
|||
type: QUERY_TYPES.FIELDS, |
|||
urlDisplay: true, |
|||
fields: { |
|||
path: { |
|||
type: FIELD_TYPES.STRING, |
|||
}, |
|||
queryString: { |
|||
type: FIELD_TYPES.STRING, |
|||
}, |
|||
headers: { |
|||
type: FIELD_TYPES.OBJECT, |
|||
}, |
|||
}, |
|||
}, |
|||
update: { |
|||
displayName: "PUT", |
|||
type: QUERY_TYPES.FIELDS, |
|||
urlDisplay: true, |
|||
fields: { |
|||
path: { |
|||
type: FIELD_TYPES.STRING, |
|||
}, |
|||
queryString: { |
|||
type: FIELD_TYPES.STRING, |
|||
}, |
|||
headers: { |
|||
type: FIELD_TYPES.OBJECT, |
|||
}, |
|||
requestBody: { |
|||
type: FIELD_TYPES.JSON, |
|||
}, |
|||
}, |
|||
}, |
|||
delete: { |
|||
displayName: "DELETE", |
|||
type: QUERY_TYPES.FIELDS, |
|||
urlDisplay: true, |
|||
fields: { |
|||
path: { |
|||
type: FIELD_TYPES.STRING, |
|||
}, |
|||
queryString: { |
|||
type: FIELD_TYPES.STRING, |
|||
}, |
|||
headers: { |
|||
type: FIELD_TYPES.OBJECT, |
|||
}, |
|||
requestBody: { |
|||
type: FIELD_TYPES.JSON, |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
class RestIntegration { |
|||
constructor(config) { |
|||
this.config = config |
|||
} |
|||
|
|||
async parseResponse(response) { |
|||
switch (this.headers.Accept) { |
|||
case "application/json": |
|||
return await response.json() |
|||
case "text/html": |
|||
return await response.text() |
|||
default: |
|||
return await response.json() |
|||
} |
|||
} |
|||
|
|||
async create({ path, queryString, headers = {}, json }) { |
|||
this.headers = { |
|||
...this.config.defaultHeaders, |
|||
...headers, |
|||
} |
|||
|
|||
const response = await fetch(this.config.url + path + queryString, { |
|||
method: "POST", |
|||
headers: this.headers, |
|||
body: JSON.stringify(json), |
|||
}) |
|||
|
|||
return await this.parseResponse(response) |
|||
} |
|||
|
|||
async read({ path, queryString, headers = {} }) { |
|||
this.headers = { |
|||
...this.config.defaultHeaders, |
|||
...headers, |
|||
} |
|||
|
|||
const response = await fetch(this.config.url + path + queryString, { |
|||
headers: this.headers, |
|||
}) |
|||
|
|||
return await this.parseResponse(response) |
|||
} |
|||
|
|||
async update({ path, queryString, headers = {}, json }) { |
|||
this.headers = { |
|||
...this.config.defaultHeaders, |
|||
...headers, |
|||
} |
|||
|
|||
const response = await fetch(this.config.url + path + queryString, { |
|||
method: "POST", |
|||
headers: this.headers, |
|||
body: JSON.stringify(json), |
|||
}) |
|||
|
|||
return await this.parseResponse(response) |
|||
} |
|||
|
|||
async delete({ path, queryString, headers = {} }) { |
|||
this.headers = { |
|||
...this.config.defaultHeaders, |
|||
...headers, |
|||
} |
|||
|
|||
const response = await fetch(this.config.url + path + queryString, { |
|||
method: "DELETE", |
|||
headers: this.headers, |
|||
}) |
|||
|
|||
return await this.parseResponse(response) |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: RestIntegration, |
|||
} |
|||
Loading…
Reference in new issue