mirror of https://github.com/Budibase/budibase.git
committed by
GitHub
142 changed files with 7343 additions and 4632 deletions
@ -0,0 +1,239 @@ |
|||||
|
<script> |
||||
|
import { createEventDispatcher } from "svelte" |
||||
|
import "@spectrum-css/popover/dist/index-vars.css" |
||||
|
import clickOutside from "../Actions/click_outside" |
||||
|
import { fly } from "svelte/transition" |
||||
|
import Icon from "../Icon/Icon.svelte" |
||||
|
import Input from "../Form/Input.svelte" |
||||
|
import { capitalise } from "../utils/helpers" |
||||
|
|
||||
|
export let value |
||||
|
export let size = "M" |
||||
|
|
||||
|
let open = false |
||||
|
|
||||
|
$: color = value || "transparent" |
||||
|
$: customValue = getCustomValue(value) |
||||
|
$: checkColor = getCheckColor(value) |
||||
|
|
||||
|
const dispatch = createEventDispatcher() |
||||
|
const categories = [ |
||||
|
{ |
||||
|
label: "Grays", |
||||
|
colors: [ |
||||
|
"white", |
||||
|
"gray-100", |
||||
|
"gray-200", |
||||
|
"gray-300", |
||||
|
"gray-400", |
||||
|
"gray-500", |
||||
|
"gray-600", |
||||
|
"gray-700", |
||||
|
"gray-800", |
||||
|
"gray-900", |
||||
|
"black", |
||||
|
], |
||||
|
}, |
||||
|
{ |
||||
|
label: "Colors", |
||||
|
colors: [ |
||||
|
"red-400", |
||||
|
"orange-400", |
||||
|
"yellow-400", |
||||
|
"green-400", |
||||
|
"seafoam-400", |
||||
|
"blue-400", |
||||
|
"indigo-400", |
||||
|
"magenta-400", |
||||
|
|
||||
|
"red-500", |
||||
|
"orange-500", |
||||
|
"yellow-500", |
||||
|
"green-500", |
||||
|
"seafoam-500", |
||||
|
"blue-500", |
||||
|
"indigo-500", |
||||
|
"magenta-500", |
||||
|
|
||||
|
"red-600", |
||||
|
"orange-600", |
||||
|
"yellow-600", |
||||
|
"green-600", |
||||
|
"seafoam-600", |
||||
|
"blue-600", |
||||
|
"indigo-600", |
||||
|
"magenta-600", |
||||
|
|
||||
|
"red-700", |
||||
|
"orange-700", |
||||
|
"yellow-700", |
||||
|
"green-700", |
||||
|
"seafoam-700", |
||||
|
"blue-700", |
||||
|
"indigo-700", |
||||
|
"magenta-700", |
||||
|
], |
||||
|
}, |
||||
|
] |
||||
|
|
||||
|
const onChange = value => { |
||||
|
dispatch("change", value) |
||||
|
open = false |
||||
|
} |
||||
|
|
||||
|
const getCustomValue = value => { |
||||
|
if (!value) { |
||||
|
return value |
||||
|
} |
||||
|
let found = false |
||||
|
const comparisonValue = value.substring(35, value.length - 1) |
||||
|
for (let category of categories) { |
||||
|
found = category.colors.includes(comparisonValue) |
||||
|
if (found) { |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
return found ? null : value |
||||
|
} |
||||
|
|
||||
|
const prettyPrint = color => { |
||||
|
return capitalise(color.split("-").join(" ")) |
||||
|
} |
||||
|
|
||||
|
const getCheckColor = value => { |
||||
|
return /^.*(white|(gray-(50|75|100|200|300|400|500)))\)$/.test(value) |
||||
|
? "black" |
||||
|
: "white" |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<div class="container"> |
||||
|
<div |
||||
|
class="preview size--{size || 'M'}" |
||||
|
style="background: {color};" |
||||
|
on:click={() => (open = true)} |
||||
|
/> |
||||
|
{#if open} |
||||
|
<div |
||||
|
use:clickOutside={() => (open = false)} |
||||
|
transition:fly={{ y: -20, duration: 200 }} |
||||
|
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open" |
||||
|
> |
||||
|
{#each categories as category} |
||||
|
<div class="category"> |
||||
|
<div class="heading">{category.label}</div> |
||||
|
<div class="colors"> |
||||
|
{#each category.colors as color} |
||||
|
<div |
||||
|
on:click={() => { |
||||
|
onChange(`var(--spectrum-global-color-static-${color})`) |
||||
|
}} |
||||
|
class="color" |
||||
|
style="background: var(--spectrum-global-color-static-{color}); color: {checkColor};" |
||||
|
title={prettyPrint(color)} |
||||
|
> |
||||
|
{#if value === `var(--spectrum-global-color-static-${color})`} |
||||
|
<Icon name="Checkmark" size="S" /> |
||||
|
{/if} |
||||
|
</div> |
||||
|
{/each} |
||||
|
</div> |
||||
|
</div> |
||||
|
{/each} |
||||
|
<div class="category category--custom"> |
||||
|
<div class="heading">Custom</div> |
||||
|
<div class="custom"> |
||||
|
<Input |
||||
|
updateOnChange={false} |
||||
|
quiet |
||||
|
placeholder="Hex, RGB, HSL..." |
||||
|
value={customValue} |
||||
|
on:change |
||||
|
/> |
||||
|
<Icon |
||||
|
size="S" |
||||
|
name="Close" |
||||
|
hoverable |
||||
|
on:click={() => onChange(null)} |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
{/if} |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.container { |
||||
|
position: relative; |
||||
|
} |
||||
|
.preview { |
||||
|
width: 32px; |
||||
|
height: 32px; |
||||
|
border-radius: 100%; |
||||
|
transition: border-color 130ms ease-in-out; |
||||
|
box-shadow: 0 0 0 1px var(--spectrum-global-color-gray-300); |
||||
|
} |
||||
|
.preview:hover { |
||||
|
cursor: pointer; |
||||
|
box-shadow: 0 0 2px 2px var(--spectrum-global-color-gray-300); |
||||
|
} |
||||
|
.size--S { |
||||
|
width: 20px; |
||||
|
height: 20px; |
||||
|
} |
||||
|
.size--M { |
||||
|
width: 32px; |
||||
|
height: 32px; |
||||
|
} |
||||
|
.size--L { |
||||
|
width: 48px; |
||||
|
height: 48px; |
||||
|
} |
||||
|
.spectrum-Popover { |
||||
|
width: 210px; |
||||
|
z-index: 999; |
||||
|
top: 100%; |
||||
|
padding: var(--spacing-l) var(--spacing-xl); |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: flex-start; |
||||
|
align-items: stretch; |
||||
|
gap: var(--spacing-xl); |
||||
|
} |
||||
|
.colors { |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; |
||||
|
gap: var(--spacing-xs); |
||||
|
} |
||||
|
.heading { |
||||
|
font-size: var(--font-size-s); |
||||
|
font-weight: 600; |
||||
|
letter-spacing: 0.14px; |
||||
|
flex: 1 1 auto; |
||||
|
text-transform: uppercase; |
||||
|
grid-column: 1 / 5; |
||||
|
margin-bottom: var(--spacing-s); |
||||
|
} |
||||
|
.color { |
||||
|
height: 16px; |
||||
|
width: 16px; |
||||
|
border-radius: 100%; |
||||
|
box-shadow: 0 0 0 1px var(--spectrum-global-color-gray-300); |
||||
|
display: grid; |
||||
|
place-items: center; |
||||
|
} |
||||
|
.color:hover { |
||||
|
cursor: pointer; |
||||
|
box-shadow: 0 0 2px 2px var(--spectrum-global-color-gray-300); |
||||
|
} |
||||
|
.custom { |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr auto; |
||||
|
align-items: center; |
||||
|
gap: var(--spacing-m); |
||||
|
margin-right: var(--spacing-xs); |
||||
|
} |
||||
|
.category--custom .heading { |
||||
|
margin-bottom: var(--spacing-xs); |
||||
|
} |
||||
|
</style> |
||||
@ -1,40 +0,0 @@ |
|||||
<script> |
|
||||
export let categories = [] |
|
||||
export let selectedCategory = {} |
|
||||
export let onClick = () => {} |
|
||||
</script> |
|
||||
|
|
||||
<div class="tabs"> |
|
||||
{#each categories as category} |
|
||||
<li |
|
||||
data-cy={category.name} |
|
||||
on:click={() => onClick(category)} |
|
||||
class:active={selectedCategory === category} |
|
||||
> |
|
||||
{category.name} |
|
||||
</li> |
|
||||
{/each} |
|
||||
</div> |
|
||||
|
|
||||
<style> |
|
||||
.tabs { |
|
||||
display: flex; |
|
||||
flex-direction: row; |
|
||||
justify-content: flex-start; |
|
||||
align-items: center; |
|
||||
list-style: none; |
|
||||
font-size: var(--font-size-m); |
|
||||
font-weight: 600; |
|
||||
height: 24px; |
|
||||
} |
|
||||
|
|
||||
li { |
|
||||
color: var(--grey-5); |
|
||||
cursor: pointer; |
|
||||
margin-right: 20px; |
|
||||
} |
|
||||
|
|
||||
.active { |
|
||||
color: var(--ink); |
|
||||
} |
|
||||
</style> |
|
||||
@ -0,0 +1,60 @@ |
|||||
|
<script> |
||||
|
import { |
||||
|
TextArea, |
||||
|
DetailSummary, |
||||
|
ActionButton, |
||||
|
Drawer, |
||||
|
DrawerContent, |
||||
|
Layout, |
||||
|
Body, |
||||
|
Button, |
||||
|
} from "@budibase/bbui" |
||||
|
import { store } from "builderStore" |
||||
|
|
||||
|
export let componentInstance |
||||
|
|
||||
|
let tempValue |
||||
|
let drawer |
||||
|
|
||||
|
const openDrawer = () => { |
||||
|
tempValue = componentInstance?._styles?.custom |
||||
|
drawer.show() |
||||
|
} |
||||
|
|
||||
|
const save = () => { |
||||
|
store.actions.components.updateCustomStyle(tempValue) |
||||
|
drawer.hide() |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<DetailSummary |
||||
|
name={`Custom CSS${componentInstance?._styles?.custom ? " *" : ""}`} |
||||
|
collapsible={false} |
||||
|
> |
||||
|
<div> |
||||
|
<ActionButton on:click={openDrawer}>Edit custom CSS</ActionButton> |
||||
|
</div> |
||||
|
</DetailSummary> |
||||
|
<Drawer bind:this={drawer} title="Custom CSS"> |
||||
|
<Button cta slot="buttons" on:click={save}>Save</Button> |
||||
|
<DrawerContent slot="body"> |
||||
|
<div class="content"> |
||||
|
<Layout gap="S"> |
||||
|
<Body size="S">Custom CSS overrides all other component styles.</Body> |
||||
|
<TextArea bind:value={tempValue} placeholder="Enter some CSS..." /> |
||||
|
</Layout> |
||||
|
</div> |
||||
|
</DrawerContent> |
||||
|
</Drawer> |
||||
|
|
||||
|
<style> |
||||
|
.content { |
||||
|
max-width: 800px; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
.content :global(textarea) { |
||||
|
font-family: monospace; |
||||
|
min-height: 240px !important; |
||||
|
font-size: var(--font-size-s); |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,34 @@ |
|||||
|
<script> |
||||
|
import StyleSection from "./StyleSection.svelte" |
||||
|
import * as ComponentStyles from "./componentStyles" |
||||
|
|
||||
|
export let componentDefinition |
||||
|
export let componentInstance |
||||
|
|
||||
|
const getStyles = def => { |
||||
|
if (!def?.styles?.length) { |
||||
|
return [...ComponentStyles.all] |
||||
|
} |
||||
|
let styles = [...ComponentStyles.all] |
||||
|
def.styles.forEach(style => { |
||||
|
if (ComponentStyles[style]) { |
||||
|
styles.push(ComponentStyles[style]) |
||||
|
} |
||||
|
}) |
||||
|
return styles |
||||
|
} |
||||
|
|
||||
|
$: styles = getStyles(componentDefinition) |
||||
|
</script> |
||||
|
|
||||
|
{#if styles?.length > 0} |
||||
|
{#each styles as style} |
||||
|
<StyleSection |
||||
|
{style} |
||||
|
name={style.label} |
||||
|
columns={style.columns} |
||||
|
properties={style.settings} |
||||
|
{componentInstance} |
||||
|
/> |
||||
|
{/each} |
||||
|
{/if} |
||||
@ -1,111 +0,0 @@ |
|||||
<script> |
|
||||
import { TextArea, DetailSummary, Button } from "@budibase/bbui" |
|
||||
import PropertyGroup from "./PropertyControls/PropertyGroup.svelte" |
|
||||
import FlatButtonGroup from "./PropertyControls/FlatButtonGroup" |
|
||||
import { allStyles } from "./componentStyles" |
|
||||
|
|
||||
export let componentDefinition = {} |
|
||||
export let componentInstance = {} |
|
||||
export let onStyleChanged = () => {} |
|
||||
export let onCustomStyleChanged = () => {} |
|
||||
export let onResetStyles = () => {} |
|
||||
|
|
||||
let selectedCategory = "normal" |
|
||||
let currentGroup |
|
||||
|
|
||||
function onChange(category) { |
|
||||
selectedCategory = category |
|
||||
} |
|
||||
|
|
||||
const buttonProps = [ |
|
||||
{ value: "normal", text: "Normal" }, |
|
||||
{ value: "hover", text: "Hover" }, |
|
||||
{ value: "active", text: "Active" }, |
|
||||
] |
|
||||
|
|
||||
$: groups = componentDefinition?.styleable ? Object.keys(allStyles) : [] |
|
||||
</script> |
|
||||
|
|
||||
<div class="container"> |
|
||||
<div class="state-categories"> |
|
||||
<FlatButtonGroup value={selectedCategory} {buttonProps} {onChange} /> |
|
||||
</div> |
|
||||
|
|
||||
<div class="positioned-wrapper"> |
|
||||
<div class="property-groups"> |
|
||||
{#if groups.length > 0} |
|
||||
{#each groups as groupName} |
|
||||
<PropertyGroup |
|
||||
name={groupName} |
|
||||
properties={allStyles[groupName]} |
|
||||
styleCategory={selectedCategory} |
|
||||
{onStyleChanged} |
|
||||
{componentInstance} |
|
||||
open={currentGroup === groupName} |
|
||||
on:open={() => (currentGroup = groupName)} |
|
||||
/> |
|
||||
{/each} |
|
||||
<DetailSummary |
|
||||
name={`Custom Styles${componentInstance._styles.custom ? " *" : ""}`} |
|
||||
on:open={() => (currentGroup = "custom")} |
|
||||
show={currentGroup === "custom"} |
|
||||
thin |
|
||||
> |
|
||||
<div class="custom-styles"> |
|
||||
<TextArea |
|
||||
value={componentInstance._styles.custom} |
|
||||
on:change={event => onCustomStyleChanged(event.detail)} |
|
||||
placeholder="Enter some CSS..." |
|
||||
/> |
|
||||
</div> |
|
||||
</DetailSummary> |
|
||||
<Button secondary wide on:click={onResetStyles}>Reset Styles</Button> |
|
||||
{:else} |
|
||||
<div class="no-design"> |
|
||||
This component doesn't have any design properties. |
|
||||
</div> |
|
||||
{/if} |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<style> |
|
||||
.container { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
gap: var(--spacing-l); |
|
||||
} |
|
||||
|
|
||||
.positioned-wrapper { |
|
||||
position: relative; |
|
||||
display: flex; |
|
||||
min-height: 0; |
|
||||
flex: 1 1 auto; |
|
||||
} |
|
||||
|
|
||||
.property-groups { |
|
||||
flex: 1; |
|
||||
overflow-y: auto; |
|
||||
min-height: 0; |
|
||||
margin: 0 -20px; |
|
||||
padding: 0 20px; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
justify-content: flex-start; |
|
||||
align-items: stretch; |
|
||||
gap: var(--spacing-m); |
|
||||
} |
|
||||
|
|
||||
.no-design { |
|
||||
font-size: var(--font-size-xs); |
|
||||
color: var(--grey-5); |
|
||||
} |
|
||||
|
|
||||
.custom-styles :global(textarea) { |
|
||||
font-family: monospace; |
|
||||
min-height: 120px; |
|
||||
font-size: var(--font-size-xs); |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,84 +1,33 @@ |
|||||
<script> |
<script> |
||||
import { get } from "svelte/store" |
import { store, selectedComponent } from "builderStore" |
||||
import { store, selectedComponent, currentAsset } from "builderStore" |
|
||||
import { Tabs, Tab } from "@budibase/bbui" |
import { Tabs, Tab } from "@budibase/bbui" |
||||
import { FrontendTypes } from "constants" |
import ScreenSettingsSection from "./ScreenSettingsSection.svelte" |
||||
import DesignView from "./DesignView.svelte" |
import ComponentSettingsSection from "./ComponentSettingsSection.svelte" |
||||
import SettingsView from "./SettingsView.svelte" |
import DesignSection from "./DesignSection.svelte" |
||||
import { setWith } from "lodash" |
import CustomStylesSection from "./CustomStylesSection.svelte" |
||||
|
|
||||
$: definition = store.actions.components.getDefinition( |
$: componentInstance = $selectedComponent |
||||
|
$: componentDefinition = store.actions.components.getDefinition( |
||||
$selectedComponent?._component |
$selectedComponent?._component |
||||
) |
) |
||||
$: isComponentOrScreen = |
|
||||
$store.currentView === "component" || |
|
||||
$store.currentFrontEndType === FrontendTypes.SCREEN |
|
||||
$: isNotScreenslot = !$selectedComponent._component.endsWith("screenslot") |
|
||||
$: showDisplayName = isComponentOrScreen && isNotScreenslot |
|
||||
|
|
||||
const onStyleChanged = store.actions.components.updateStyle |
|
||||
const onCustomStyleChanged = store.actions.components.updateCustomStyle |
|
||||
const onResetStyles = store.actions.components.resetStyles |
|
||||
|
|
||||
function setAssetProps(name, value) { |
|
||||
const selectedAsset = get(currentAsset) |
|
||||
store.update(state => { |
|
||||
if ( |
|
||||
name === "_instanceName" && |
|
||||
state.currentFrontEndType === FrontendTypes.SCREEN |
|
||||
) { |
|
||||
selectedAsset.props._instanceName = value |
|
||||
} else { |
|
||||
setWith(selectedAsset, name.split("."), value, Object) |
|
||||
} |
|
||||
return state |
|
||||
}) |
|
||||
store.actions.preview.saveSelected() |
|
||||
} |
|
||||
</script> |
</script> |
||||
|
|
||||
<Tabs selected="Settings"> |
<Tabs selected="Settings" noPadding> |
||||
<Tab title="Settings"> |
<Tab title="Settings"> |
||||
<div class="tab-content-padding"> |
<div class="container"> |
||||
{#if definition && definition.name} |
<ScreenSettingsSection {componentInstance} {componentDefinition} /> |
||||
<div class="instance-name">{definition.name}</div> |
<ComponentSettingsSection {componentInstance} {componentDefinition} /> |
||||
{/if} |
<DesignSection {componentInstance} {componentDefinition} /> |
||||
<SettingsView |
<CustomStylesSection {componentInstance} {componentDefinition} /> |
||||
componentInstance={$selectedComponent} |
|
||||
componentDefinition={definition} |
|
||||
{showDisplayName} |
|
||||
onChange={store.actions.components.updateProp} |
|
||||
onScreenPropChange={setAssetProps} |
|
||||
assetInstance={$store.currentView !== "component" && $currentAsset} |
|
||||
/> |
|
||||
</div> |
|
||||
</Tab> |
|
||||
<Tab title="Design"> |
|
||||
<div class="tab-content-padding"> |
|
||||
{#if definition && definition.name} |
|
||||
<div class="instance-name">{definition.name}</div> |
|
||||
{/if} |
|
||||
<DesignView |
|
||||
componentInstance={$selectedComponent} |
|
||||
componentDefinition={definition} |
|
||||
{onStyleChanged} |
|
||||
{onCustomStyleChanged} |
|
||||
{onResetStyles} |
|
||||
/> |
|
||||
</div> |
</div> |
||||
</Tab> |
</Tab> |
||||
</Tabs> |
</Tabs> |
||||
|
|
||||
<style> |
<style> |
||||
.tab-content-padding { |
.container { |
||||
padding: 0 var(--spacing-xl); |
display: flex; |
||||
} |
flex-direction: column; |
||||
|
justify-content: flex-start; |
||||
.instance-name { |
align-items: stretch; |
||||
font-size: var(--spectrum-global-dimension-font-size-75); |
|
||||
margin-bottom: var(--spacing-m); |
|
||||
margin-top: var(--spacing-xs); |
|
||||
font-weight: 600; |
|
||||
color: var(--grey-7); |
|
||||
} |
} |
||||
</style> |
</style> |
||||
|
|||||
@ -1,54 +0,0 @@ |
|||||
<script> |
|
||||
import PropertyControl from "./PropertyControl.svelte" |
|
||||
import { DetailSummary } from "@budibase/bbui" |
|
||||
|
|
||||
export let name = "" |
|
||||
export let styleCategory = "normal" |
|
||||
export let properties = [] |
|
||||
export let componentInstance = {} |
|
||||
export let onStyleChanged = () => {} |
|
||||
export let open = false |
|
||||
|
|
||||
$: style = componentInstance["_styles"][styleCategory] || {} |
|
||||
$: changed = properties.some(prop => hasPropChanged(style, prop)) |
|
||||
|
|
||||
const hasPropChanged = (style, prop) => { |
|
||||
return style[prop.key] != null && style[prop.key] !== "" |
|
||||
} |
|
||||
|
|
||||
const getControlProps = props => { |
|
||||
let controlProps = { ...(props || {}) } |
|
||||
delete controlProps.label |
|
||||
delete controlProps.key |
|
||||
delete controlProps.control |
|
||||
return controlProps |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<DetailSummary name={`${name}${changed ? " *" : ""}`} on:open show={open} thin> |
|
||||
{#if open} |
|
||||
<div> |
|
||||
{#each properties as prop (`${componentInstance._id}-${prop.key}-${prop.label}`)} |
|
||||
<PropertyControl |
|
||||
bindable={false} |
|
||||
label={`${prop.label}${hasPropChanged(style, prop) ? " *" : ""}`} |
|
||||
control={prop.control} |
|
||||
key={prop.key} |
|
||||
value={style[prop.key]} |
|
||||
onChange={value => onStyleChanged(styleCategory, prop.key, value)} |
|
||||
props={getControlProps(prop)} |
|
||||
/> |
|
||||
{/each} |
|
||||
</div> |
|
||||
{/if} |
|
||||
</DetailSummary> |
|
||||
|
|
||||
<style> |
|
||||
div { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
justify-content: flex-start; |
|
||||
align-items: stretch; |
|
||||
gap: var(--spacing-s); |
|
||||
} |
|
||||
</style> |
|
||||
@ -0,0 +1,43 @@ |
|||||
|
<script> |
||||
|
import { ActionButton } from "@budibase/bbui" |
||||
|
import { currentAsset, store } from "builderStore" |
||||
|
import { findClosestMatchingComponent } from "builderStore/storeUtils" |
||||
|
import { makeDatasourceFormComponents } from "builderStore/store/screenTemplates/utils/commonComponents" |
||||
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte" |
||||
|
|
||||
|
export let componentInstance |
||||
|
|
||||
|
let confirmResetFieldsDialog |
||||
|
|
||||
|
const resetFormFields = () => { |
||||
|
const form = findClosestMatchingComponent( |
||||
|
$currentAsset.props, |
||||
|
componentInstance._id, |
||||
|
component => component._component.endsWith("/form") |
||||
|
) |
||||
|
const dataSource = form?.dataSource |
||||
|
const fields = makeDatasourceFormComponents(dataSource) |
||||
|
store.actions.components.updateProp( |
||||
|
"_children", |
||||
|
fields.map(field => field.json()) |
||||
|
) |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<div> |
||||
|
<ActionButton |
||||
|
secondary |
||||
|
wide |
||||
|
on:click={() => confirmResetFieldsDialog?.show()} |
||||
|
> |
||||
|
Update form fields |
||||
|
</ActionButton> |
||||
|
</div> |
||||
|
|
||||
|
<ConfirmDialog |
||||
|
bind:this={confirmResetFieldsDialog} |
||||
|
body={`All components inside this group will be deleted and replaced with fields to match the schema. Are you sure you want to update this Field Group?`} |
||||
|
okText="Update" |
||||
|
onOk={resetFormFields} |
||||
|
title="Confirm Form Field Update" |
||||
|
/> |
||||
@ -0,0 +1,50 @@ |
|||||
|
<script> |
||||
|
import { get } from "svelte/store" |
||||
|
import { get as deepGet, setWith } from "lodash" |
||||
|
import { Input, DetailSummary } from "@budibase/bbui" |
||||
|
import PropertyControl from "./PropertyControls/PropertyControl.svelte" |
||||
|
import LayoutSelect from "./PropertyControls/LayoutSelect.svelte" |
||||
|
import RoleSelect from "./PropertyControls/RoleSelect.svelte" |
||||
|
import { currentAsset, store } from "builderStore" |
||||
|
import { FrontendTypes } from "constants" |
||||
|
|
||||
|
export let componentInstance |
||||
|
|
||||
|
function setAssetProps(name, value) { |
||||
|
const selectedAsset = get(currentAsset) |
||||
|
store.update(state => { |
||||
|
if ( |
||||
|
name === "_instanceName" && |
||||
|
state.currentFrontEndType === FrontendTypes.SCREEN |
||||
|
) { |
||||
|
selectedAsset.props._instanceName = value |
||||
|
} else { |
||||
|
setWith(selectedAsset, name.split("."), value, Object) |
||||
|
} |
||||
|
return state |
||||
|
}) |
||||
|
store.actions.preview.saveSelected() |
||||
|
} |
||||
|
|
||||
|
const screenSettings = [ |
||||
|
// { key: "description", label: "Description", control: Input }, |
||||
|
{ key: "routing.route", label: "Route", control: Input }, |
||||
|
{ key: "routing.roleId", label: "Access", control: RoleSelect }, |
||||
|
{ key: "layoutId", label: "Layout", control: LayoutSelect }, |
||||
|
] |
||||
|
</script> |
||||
|
|
||||
|
{#if $store.currentView !== "component" && $currentAsset && $store.currentFrontEndType === FrontendTypes.SCREEN} |
||||
|
<DetailSummary name="Screen" collapsible={false}> |
||||
|
{#each screenSettings as def (`${componentInstance._id}-${def.key}`)} |
||||
|
<PropertyControl |
||||
|
bindable={false} |
||||
|
control={def.control} |
||||
|
label={def.label} |
||||
|
key={def.key} |
||||
|
value={deepGet($currentAsset, def.key)} |
||||
|
onChange={val => setAssetProps(def.key, val)} |
||||
|
/> |
||||
|
{/each} |
||||
|
</DetailSummary> |
||||
|
{/if} |
||||
@ -0,0 +1,51 @@ |
|||||
|
<script> |
||||
|
import PropertyControl from "./PropertyControls/PropertyControl.svelte" |
||||
|
import { DetailSummary } from "@budibase/bbui" |
||||
|
import { store } from "builderStore" |
||||
|
|
||||
|
export let name |
||||
|
export let columns |
||||
|
export let properties |
||||
|
export let componentInstance |
||||
|
|
||||
|
$: style = componentInstance._styles.normal || {} |
||||
|
$: changed = properties?.some(prop => hasPropChanged(style, prop)) ?? false |
||||
|
|
||||
|
const hasPropChanged = (style, prop) => { |
||||
|
return style[prop.key] != null && style[prop.key] !== "" |
||||
|
} |
||||
|
|
||||
|
const getControlProps = props => { |
||||
|
let controlProps = { ...(props || {}) } |
||||
|
delete controlProps.label |
||||
|
delete controlProps.key |
||||
|
delete controlProps.control |
||||
|
return controlProps |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<DetailSummary collapsible={false} name={`${name}${changed ? " *" : ""}`}> |
||||
|
<div class="group-content" style="grid-template-columns: {columns || '1fr'}"> |
||||
|
{#each properties as prop (`${componentInstance._id}-${prop.key}-${prop.label}`)} |
||||
|
<div style="grid-column: {prop.column || 'auto'}"> |
||||
|
<PropertyControl |
||||
|
bindable={false} |
||||
|
label={`${prop.label}${hasPropChanged(style, prop) ? " *" : ""}`} |
||||
|
control={prop.control} |
||||
|
key={prop.key} |
||||
|
value={style[prop.key]} |
||||
|
onChange={val => store.actions.components.updateStyle(prop.key, val)} |
||||
|
props={getControlProps(prop)} |
||||
|
/> |
||||
|
</div> |
||||
|
{/each} |
||||
|
</div> |
||||
|
</DetailSummary> |
||||
|
|
||||
|
<style> |
||||
|
.group-content { |
||||
|
display: grid; |
||||
|
align-items: stretch; |
||||
|
gap: var(--spacing-l); |
||||
|
} |
||||
|
</style> |
||||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,26 @@ |
|||||
|
<script> |
||||
|
import { ColorPicker } from "@budibase/bbui" |
||||
|
import { builderStore } from "../../store" |
||||
|
|
||||
|
export let prop |
||||
|
|
||||
|
$: currentValue = $builderStore.selectedComponent?.[prop] |
||||
|
</script> |
||||
|
|
||||
|
<div> |
||||
|
<ColorPicker |
||||
|
size="S" |
||||
|
value={currentValue} |
||||
|
on:change={e => { |
||||
|
if (prop) { |
||||
|
builderStore.actions.updateProp(prop, e.detail) |
||||
|
} |
||||
|
}} |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
div { |
||||
|
padding: 0 4px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,31 @@ |
|||||
|
<script> |
||||
|
import { Select } from "@budibase/bbui" |
||||
|
import { builderStore } from "../../store" |
||||
|
|
||||
|
export let prop |
||||
|
export let options |
||||
|
export let label |
||||
|
|
||||
|
$: currentValue = $builderStore.selectedComponent?.[prop] |
||||
|
</script> |
||||
|
|
||||
|
<div> |
||||
|
<Select |
||||
|
quiet |
||||
|
autoWidth |
||||
|
placeholder={label} |
||||
|
{options} |
||||
|
value={currentValue} |
||||
|
on:change={e => { |
||||
|
if (prop) { |
||||
|
builderStore.actions.updateProp(prop, e.detail) |
||||
|
} |
||||
|
}} |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
div { |
||||
|
padding: 0 4px; |
||||
|
} |
||||
|
</style> |
||||
@ -1,4 +1,4 @@ |
|||||
const elastic = {} |
const elastic: any = {} |
||||
|
|
||||
elastic.Client = function () { |
elastic.Client = function () { |
||||
this.index = jest.fn().mockResolvedValue({ body: [] }) |
this.index = jest.fn().mockResolvedValue({ body: [] }) |
||||
@ -1,18 +0,0 @@ |
|||||
class Email { |
|
||||
constructor() { |
|
||||
this.apiKey = null |
|
||||
} |
|
||||
|
|
||||
setApiKey(apiKey) { |
|
||||
this.apiKey = apiKey |
|
||||
} |
|
||||
|
|
||||
async send(msg) { |
|
||||
if (msg.to === "invalid@test.com") { |
|
||||
throw "Invalid" |
|
||||
} |
|
||||
return msg |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
module.exports = new Email() |
|
||||
@ -0,0 +1,22 @@ |
|||||
|
module SendgridMock { |
||||
|
class Email { |
||||
|
constructor() { |
||||
|
// @ts-ignore
|
||||
|
this.apiKey = null |
||||
|
} |
||||
|
|
||||
|
setApiKey(apiKey: any) { |
||||
|
// @ts-ignore
|
||||
|
this.apiKey = apiKey |
||||
|
} |
||||
|
|
||||
|
async send(msg: any) { |
||||
|
if (msg.to === "invalid@test.com") { |
||||
|
throw "Invalid" |
||||
|
} |
||||
|
return msg |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
module.exports = new Email() |
||||
|
} |
||||
@ -1,5 +0,0 @@ |
|||||
function Airtable() { |
|
||||
this.base = jest.fn() |
|
||||
} |
|
||||
|
|
||||
module.exports = Airtable |
|
||||
@ -0,0 +1,8 @@ |
|||||
|
module AirtableMock { |
||||
|
function Airtable() { |
||||
|
// @ts-ignore
|
||||
|
this.base = jest.fn() |
||||
|
} |
||||
|
|
||||
|
module.exports = Airtable |
||||
|
} |
||||
@ -1,21 +0,0 @@ |
|||||
const arangodb = {} |
|
||||
|
|
||||
arangodb.Database = function () { |
|
||||
this.query = jest.fn(() => ({ |
|
||||
all: jest.fn(), |
|
||||
})) |
|
||||
this.collection = jest.fn(() => "collection") |
|
||||
this.close = jest.fn() |
|
||||
} |
|
||||
|
|
||||
arangodb.aql = (strings, ...args) => { |
|
||||
let str = strings.join("{}") |
|
||||
|
|
||||
for (let arg of args) { |
|
||||
str = str.replace("{}", arg) |
|
||||
} |
|
||||
|
|
||||
return str |
|
||||
} |
|
||||
|
|
||||
module.exports = arangodb |
|
||||
@ -0,0 +1,24 @@ |
|||||
|
module ArangoMock { |
||||
|
const arangodb: any = {} |
||||
|
|
||||
|
arangodb.Database = function () { |
||||
|
this.query = jest.fn(() => ({ |
||||
|
all: jest.fn(), |
||||
|
})) |
||||
|
this.collection = jest.fn(() => "collection") |
||||
|
this.close = jest.fn() |
||||
|
} |
||||
|
|
||||
|
// @ts-ignore
|
||||
|
arangodb.aql = (strings, ...args) => { |
||||
|
let str = strings.join("{}") |
||||
|
|
||||
|
for (let arg of args) { |
||||
|
str = str.replace("{}", arg) |
||||
|
} |
||||
|
|
||||
|
return str |
||||
|
} |
||||
|
|
||||
|
module.exports = arangodb |
||||
|
} |
||||
@ -1,38 +0,0 @@ |
|||||
const aws = {} |
|
||||
|
|
||||
const response = body => () => ({ promise: () => body }) |
|
||||
|
|
||||
function DocumentClient() { |
|
||||
this.put = jest.fn(response({})) |
|
||||
this.query = jest.fn( |
|
||||
response({ |
|
||||
Items: [], |
|
||||
}) |
|
||||
) |
|
||||
this.scan = jest.fn( |
|
||||
response({ |
|
||||
Items: [ |
|
||||
{ |
|
||||
Name: "test", |
|
||||
}, |
|
||||
], |
|
||||
}) |
|
||||
) |
|
||||
this.get = jest.fn(response({})) |
|
||||
this.update = jest.fn(response({})) |
|
||||
this.delete = jest.fn(response({})) |
|
||||
} |
|
||||
|
|
||||
function S3() { |
|
||||
this.listObjects = jest.fn( |
|
||||
response({ |
|
||||
Contents: {}, |
|
||||
}) |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
aws.DynamoDB = { DocumentClient } |
|
||||
aws.S3 = S3 |
|
||||
aws.config = { update: jest.fn() } |
|
||||
|
|
||||
module.exports = aws |
|
||||
@ -0,0 +1,47 @@ |
|||||
|
module AwsMock { |
||||
|
const aws: any = {} |
||||
|
|
||||
|
const response = (body: any) => () => ({promise: () => body}) |
||||
|
|
||||
|
function DocumentClient() { |
||||
|
// @ts-ignore
|
||||
|
this.put = jest.fn(response({})) |
||||
|
// @ts-ignore
|
||||
|
this.query = jest.fn( |
||||
|
response({ |
||||
|
Items: [], |
||||
|
}) |
||||
|
) |
||||
|
// @ts-ignore
|
||||
|
this.scan = jest.fn( |
||||
|
response({ |
||||
|
Items: [ |
||||
|
{ |
||||
|
Name: "test", |
||||
|
}, |
||||
|
], |
||||
|
}) |
||||
|
) |
||||
|
// @ts-ignore
|
||||
|
this.get = jest.fn(response({})) |
||||
|
// @ts-ignore
|
||||
|
this.update = jest.fn(response({})) |
||||
|
// @ts-ignore
|
||||
|
this.delete = jest.fn(response({})) |
||||
|
} |
||||
|
|
||||
|
function S3() { |
||||
|
// @ts-ignore
|
||||
|
this.listObjects = jest.fn( |
||||
|
response({ |
||||
|
Contents: {}, |
||||
|
}) |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
aws.DynamoDB = {DocumentClient} |
||||
|
aws.S3 = S3 |
||||
|
aws.config = {update: jest.fn()} |
||||
|
|
||||
|
module.exports = aws |
||||
|
} |
||||
@ -1,19 +0,0 @@ |
|||||
const mongodb = {} |
|
||||
|
|
||||
mongodb.MongoClient = function () { |
|
||||
this.connect = jest.fn() |
|
||||
this.close = jest.fn() |
|
||||
this.insertOne = jest.fn() |
|
||||
this.find = jest.fn(() => ({ toArray: () => [] })) |
|
||||
|
|
||||
this.collection = jest.fn(() => ({ |
|
||||
insertOne: this.insertOne, |
|
||||
find: this.find, |
|
||||
})) |
|
||||
|
|
||||
this.db = () => ({ |
|
||||
collection: this.collection, |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
module.exports = mongodb |
|
||||
@ -0,0 +1,21 @@ |
|||||
|
module MongoMock { |
||||
|
const mongodb: any = {} |
||||
|
|
||||
|
mongodb.MongoClient = function () { |
||||
|
this.connect = jest.fn() |
||||
|
this.close = jest.fn() |
||||
|
this.insertOne = jest.fn() |
||||
|
this.find = jest.fn(() => ({toArray: () => []})) |
||||
|
|
||||
|
this.collection = jest.fn(() => ({ |
||||
|
insertOne: this.insertOne, |
||||
|
find: this.find, |
||||
|
})) |
||||
|
|
||||
|
this.db = () => ({ |
||||
|
collection: this.collection, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
module.exports = mongodb |
||||
|
} |
||||
@ -1,22 +0,0 @@ |
|||||
const mssql = {} |
|
||||
|
|
||||
mssql.query = jest.fn(() => ({ |
|
||||
recordset: [ |
|
||||
{ |
|
||||
a: "string", |
|
||||
b: 1, |
|
||||
}, |
|
||||
], |
|
||||
})) |
|
||||
|
|
||||
// mssql.connect = jest.fn(() => ({ recordset: [] }))
|
|
||||
|
|
||||
mssql.ConnectionPool = jest.fn(() => ({ |
|
||||
connect: jest.fn(() => ({ |
|
||||
request: jest.fn(() => ({ |
|
||||
query: jest.fn(() => ({})), |
|
||||
})), |
|
||||
})), |
|
||||
})) |
|
||||
|
|
||||
module.exports = mssql |
|
||||
@ -0,0 +1,24 @@ |
|||||
|
module MsSqlMock { |
||||
|
const mssql: any = {} |
||||
|
|
||||
|
mssql.query = jest.fn(() => ({ |
||||
|
recordset: [ |
||||
|
{ |
||||
|
a: "string", |
||||
|
b: 1, |
||||
|
}, |
||||
|
], |
||||
|
})) |
||||
|
|
||||
|
// mssql.connect = jest.fn(() => ({ recordset: [] }))
|
||||
|
|
||||
|
mssql.ConnectionPool = jest.fn(() => ({ |
||||
|
connect: jest.fn(() => ({ |
||||
|
request: jest.fn(() => ({ |
||||
|
query: jest.fn(() => ({})), |
||||
|
})), |
||||
|
})), |
||||
|
})) |
||||
|
|
||||
|
module.exports = mssql |
||||
|
} |
||||
@ -1,12 +0,0 @@ |
|||||
const mysql = {} |
|
||||
|
|
||||
const client = { |
|
||||
connect: jest.fn(), |
|
||||
query: jest.fn((query, bindings, fn) => { |
|
||||
fn(null, []) |
|
||||
}), |
|
||||
} |
|
||||
|
|
||||
mysql.createConnection = jest.fn(() => client) |
|
||||
|
|
||||
module.exports = mysql |
|
||||
@ -0,0 +1,14 @@ |
|||||
|
module MySQLMock { |
||||
|
const mysql: any = {} |
||||
|
|
||||
|
const client = { |
||||
|
connect: jest.fn(), |
||||
|
query: jest.fn((query, bindings, fn) => { |
||||
|
fn(null, []) |
||||
|
}), |
||||
|
} |
||||
|
|
||||
|
mysql.createConnection = jest.fn(() => client) |
||||
|
|
||||
|
module.exports = mysql |
||||
|
} |
||||
@ -1,58 +0,0 @@ |
|||||
const fetch = jest.requireActual("node-fetch") |
|
||||
|
|
||||
module.exports = async (url, opts) => { |
|
||||
function json(body, status = 200) { |
|
||||
return { |
|
||||
status, |
|
||||
headers: { |
|
||||
get: () => { |
|
||||
return ["application/json"] |
|
||||
}, |
|
||||
}, |
|
||||
json: async () => { |
|
||||
return body |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (url.includes("/api/admin")) { |
|
||||
return json({ |
|
||||
email: "test@test.com", |
|
||||
_id: "us_test@test.com", |
|
||||
status: "active", |
|
||||
}) |
|
||||
} |
|
||||
// mocked data based on url
|
|
||||
else if (url.includes("api/apps")) { |
|
||||
return json({ |
|
||||
app1: { |
|
||||
url: "/app1", |
|
||||
}, |
|
||||
}) |
|
||||
} else if (url.includes("test.com")) { |
|
||||
return json({ |
|
||||
body: opts.body, |
|
||||
url, |
|
||||
method: opts.method, |
|
||||
}) |
|
||||
} else if (url.includes("invalid.com")) { |
|
||||
return json( |
|
||||
{ |
|
||||
invalid: true, |
|
||||
}, |
|
||||
404 |
|
||||
) |
|
||||
} else if (url.includes("_search")) { |
|
||||
return json({ |
|
||||
rows: [ |
|
||||
{ |
|
||||
doc: { |
|
||||
_id: "test", |
|
||||
}, |
|
||||
}, |
|
||||
], |
|
||||
bookmark: "test", |
|
||||
}) |
|
||||
} |
|
||||
return fetch(url, opts) |
|
||||
} |
|
||||
@ -0,0 +1,60 @@ |
|||||
|
module FetchMock { |
||||
|
const fetch = jest.requireActual("node-fetch") |
||||
|
|
||||
|
module.exports = async (url: any, opts: any) => { |
||||
|
function json(body: any, status = 200) { |
||||
|
return { |
||||
|
status, |
||||
|
headers: { |
||||
|
get: () => { |
||||
|
return ["application/json"] |
||||
|
}, |
||||
|
}, |
||||
|
json: async () => { |
||||
|
return body |
||||
|
}, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (url.includes("/api/admin")) { |
||||
|
return json({ |
||||
|
email: "test@test.com", |
||||
|
_id: "us_test@test.com", |
||||
|
status: "active", |
||||
|
}) |
||||
|
} |
||||
|
// mocked data based on url
|
||||
|
else if (url.includes("api/apps")) { |
||||
|
return json({ |
||||
|
app1: { |
||||
|
url: "/app1", |
||||
|
}, |
||||
|
}) |
||||
|
} else if (url.includes("test.com")) { |
||||
|
return json({ |
||||
|
body: opts.body, |
||||
|
url, |
||||
|
method: opts.method, |
||||
|
}) |
||||
|
} else if (url.includes("invalid.com")) { |
||||
|
return json( |
||||
|
{ |
||||
|
invalid: true, |
||||
|
}, |
||||
|
404 |
||||
|
) |
||||
|
} else if (url.includes("_search")) { |
||||
|
return json({ |
||||
|
rows: [ |
||||
|
{ |
||||
|
doc: { |
||||
|
_id: "test", |
||||
|
}, |
||||
|
}, |
||||
|
], |
||||
|
bookmark: "test", |
||||
|
}) |
||||
|
} |
||||
|
return fetch(url, opts) |
||||
|
} |
||||
|
} |
||||
@ -1,29 +0,0 @@ |
|||||
const pg = {} |
|
||||
|
|
||||
const query = jest.fn(() => ({ |
|
||||
rows: [ |
|
||||
{ |
|
||||
a: "string", |
|
||||
b: 1, |
|
||||
}, |
|
||||
], |
|
||||
})) |
|
||||
|
|
||||
// constructor
|
|
||||
function Client() {} |
|
||||
|
|
||||
Client.prototype.query = query |
|
||||
Client.prototype.connect = jest.fn() |
|
||||
Client.prototype.release = jest.fn() |
|
||||
|
|
||||
function Pool() {} |
|
||||
Pool.prototype.query = query |
|
||||
Pool.prototype.connect = jest.fn(() => { |
|
||||
return new Client() |
|
||||
}) |
|
||||
|
|
||||
pg.Client = Client |
|
||||
pg.Pool = Pool |
|
||||
pg.queryMock = query |
|
||||
|
|
||||
module.exports = pg |
|
||||
@ -0,0 +1,35 @@ |
|||||
|
module PgMock { |
||||
|
const pg: any = {} |
||||
|
|
||||
|
const query = jest.fn(() => ({ |
||||
|
rows: [ |
||||
|
{ |
||||
|
a: "string", |
||||
|
b: 1, |
||||
|
}, |
||||
|
], |
||||
|
})) |
||||
|
|
||||
|
// constructor
|
||||
|
function Client() { |
||||
|
} |
||||
|
|
||||
|
Client.prototype.query = query |
||||
|
Client.prototype.connect = jest.fn() |
||||
|
Client.prototype.release = jest.fn() |
||||
|
|
||||
|
function Pool() { |
||||
|
} |
||||
|
|
||||
|
Pool.prototype.query = query |
||||
|
Pool.prototype.connect = jest.fn(() => { |
||||
|
// @ts-ignore
|
||||
|
return new Client() |
||||
|
}) |
||||
|
|
||||
|
pg.Client = Client |
||||
|
pg.Pool = Pool |
||||
|
pg.queryMock = query |
||||
|
|
||||
|
module.exports = pg |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
export interface Table { |
||||
|
_id: string |
||||
|
_rev?: string |
||||
|
type?: string |
||||
|
views?: {} |
||||
|
name?: string |
||||
|
primary?: string[] |
||||
|
schema: { |
||||
|
[key: string]: { |
||||
|
// TODO: replace with field types enum when done
|
||||
|
type: string |
||||
|
fieldName?: string |
||||
|
name: string |
||||
|
constraints?: { |
||||
|
type?: string |
||||
|
email?: boolean |
||||
|
inclusion?: string[] |
||||
|
length?: { |
||||
|
minimum?: string | number |
||||
|
maximum?: string | number |
||||
|
} |
||||
|
presence?: boolean |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
primaryDisplay?: string |
||||
|
sourceId?: string |
||||
|
} |
||||
@ -1,15 +0,0 @@ |
|||||
exports.QUERY_TYPES = { |
|
||||
SQL: "sql", |
|
||||
JSON: "json", |
|
||||
FIELDS: "fields", |
|
||||
} |
|
||||
|
|
||||
exports.FIELD_TYPES = { |
|
||||
STRING: "string", |
|
||||
BOOLEAN: "boolean", |
|
||||
NUMBER: "number", |
|
||||
PASSWORD: "password", |
|
||||
LIST: "list", |
|
||||
OBJECT: "object", |
|
||||
JSON: "json", |
|
||||
} |
|
||||
@ -1,130 +0,0 @@ |
|||||
const Airtable = require("airtable") |
|
||||
const { FIELD_TYPES, QUERY_TYPES } = require("./Integration") |
|
||||
|
|
||||
const SCHEMA = { |
|
||||
docs: "https://airtable.com/api", |
|
||||
description: |
|
||||
"Airtable is a spreadsheet-database hybrid, with the features of a database but applied to a spreadsheet.", |
|
||||
friendlyName: "Airtable", |
|
||||
datasource: { |
|
||||
apiKey: { |
|
||||
type: FIELD_TYPES.PASSWORD, |
|
||||
default: "enter api key", |
|
||||
required: true, |
|
||||
}, |
|
||||
base: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
default: "mybase", |
|
||||
required: true, |
|
||||
}, |
|
||||
}, |
|
||||
query: { |
|
||||
create: { |
|
||||
type: QUERY_TYPES.FIELDS, |
|
||||
customisable: true, |
|
||||
fields: { |
|
||||
table: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
read: { |
|
||||
type: QUERY_TYPES.FIELDS, |
|
||||
fields: { |
|
||||
table: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
view: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
numRecords: { |
|
||||
type: FIELD_TYPES.NUMBER, |
|
||||
default: 10, |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
update: { |
|
||||
type: QUERY_TYPES.FIELDS, |
|
||||
customisable: true, |
|
||||
fields: { |
|
||||
id: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
delete: { |
|
||||
type: QUERY_TYPES.JSON, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
class AirtableIntegration { |
|
||||
constructor(config) { |
|
||||
this.config = config |
|
||||
this.client = new Airtable(config).base(config.base) |
|
||||
} |
|
||||
|
|
||||
async create(query) { |
|
||||
const { table, json } = query |
|
||||
|
|
||||
try { |
|
||||
const records = await this.client(table).create([ |
|
||||
{ |
|
||||
fields: json, |
|
||||
}, |
|
||||
]) |
|
||||
return records |
|
||||
} catch (err) { |
|
||||
console.error("Error writing to airtable", err) |
|
||||
throw err |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
async read(query) { |
|
||||
try { |
|
||||
const records = await this.client(query.table) |
|
||||
.select({ maxRecords: query.numRecords || 10, view: query.view }) |
|
||||
.firstPage() |
|
||||
return records.map(({ fields }) => fields) |
|
||||
} catch (err) { |
|
||||
console.error("Error writing to airtable", err) |
|
||||
return [] |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
async update(query) { |
|
||||
const { table, id, json } = query |
|
||||
|
|
||||
try { |
|
||||
const records = await this.client(table).update([ |
|
||||
{ |
|
||||
id, |
|
||||
fields: json, |
|
||||
}, |
|
||||
]) |
|
||||
return records |
|
||||
} catch (err) { |
|
||||
console.error("Error writing to airtable", err) |
|
||||
throw err |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
async delete(query) { |
|
||||
try { |
|
||||
const records = await this.client(query.table).destroy(query.ids) |
|
||||
return records |
|
||||
} catch (err) { |
|
||||
console.error("Error writing to airtable", err) |
|
||||
throw err |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
module.exports = { |
|
||||
schema: SCHEMA, |
|
||||
integration: AirtableIntegration, |
|
||||
} |
|
||||
@ -0,0 +1,143 @@ |
|||||
|
import { |
||||
|
Integration, |
||||
|
DatasourceFieldTypes, |
||||
|
QueryTypes, |
||||
|
} from "./base/definitions" |
||||
|
|
||||
|
module AirtableModule { |
||||
|
const Airtable = require("airtable") |
||||
|
|
||||
|
interface AirtableConfig { |
||||
|
apiKey: string |
||||
|
base: string |
||||
|
} |
||||
|
|
||||
|
const SCHEMA: Integration = { |
||||
|
docs: "https://airtable.com/api", |
||||
|
description: |
||||
|
"Airtable is a spreadsheet-database hybrid, with the features of a database but applied to a spreadsheet.", |
||||
|
friendlyName: "Airtable", |
||||
|
datasource: { |
||||
|
apiKey: { |
||||
|
type: DatasourceFieldTypes.PASSWORD, |
||||
|
default: "enter api key", |
||||
|
required: true, |
||||
|
}, |
||||
|
base: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
default: "mybase", |
||||
|
required: true, |
||||
|
}, |
||||
|
}, |
||||
|
query: { |
||||
|
create: { |
||||
|
type: QueryTypes.FIELDS, |
||||
|
customisable: true, |
||||
|
fields: { |
||||
|
table: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
read: { |
||||
|
type: QueryTypes.FIELDS, |
||||
|
fields: { |
||||
|
table: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
view: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
numRecords: { |
||||
|
type: DatasourceFieldTypes.NUMBER, |
||||
|
default: 10, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
update: { |
||||
|
type: QueryTypes.FIELDS, |
||||
|
customisable: true, |
||||
|
fields: { |
||||
|
id: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
delete: { |
||||
|
type: QueryTypes.JSON, |
||||
|
}, |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
class AirtableIntegration { |
||||
|
private config: AirtableConfig |
||||
|
private client: any |
||||
|
|
||||
|
constructor(config: AirtableConfig) { |
||||
|
this.config = config |
||||
|
this.client = new Airtable(config).base(config.base) |
||||
|
} |
||||
|
|
||||
|
async create(query: { table: any; json: any }) { |
||||
|
const { table, json } = query |
||||
|
|
||||
|
try { |
||||
|
return await this.client(table).create([ |
||||
|
{ |
||||
|
fields: json, |
||||
|
}, |
||||
|
]) |
||||
|
} catch (err) { |
||||
|
console.error("Error writing to airtable", err) |
||||
|
throw err |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async read(query: { table: any; numRecords: any; view: any }) { |
||||
|
try { |
||||
|
const records = await this.client(query.table) |
||||
|
.select({ maxRecords: query.numRecords || 10, view: query.view }) |
||||
|
.firstPage() |
||||
|
// @ts-ignore
|
||||
|
return records.map(({ fields }) => fields) |
||||
|
} catch (err) { |
||||
|
console.error("Error writing to airtable", err) |
||||
|
return [] |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async update(query: { table: any; id: any; json: any }) { |
||||
|
const { table, id, json } = query |
||||
|
|
||||
|
try { |
||||
|
return await this.client(table).update([ |
||||
|
{ |
||||
|
id, |
||||
|
fields: json, |
||||
|
}, |
||||
|
]) |
||||
|
} catch (err) { |
||||
|
console.error("Error writing to airtable", err) |
||||
|
throw err |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async delete(query: { table: any; ids: any }) { |
||||
|
try { |
||||
|
return await this.client(query.table).destroy(query.ids) |
||||
|
} catch (err) { |
||||
|
console.error("Error writing to airtable", err) |
||||
|
throw err |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
module.exports = { |
||||
|
schema: SCHEMA, |
||||
|
integration: AirtableIntegration, |
||||
|
} |
||||
|
} |
||||
@ -1,86 +0,0 @@ |
|||||
const { Database, aql } = require("arangojs") |
|
||||
const { FIELD_TYPES, QUERY_TYPES } = require("./Integration") |
|
||||
|
|
||||
const SCHEMA = { |
|
||||
docs: "https://github.com/arangodb/arangojs", |
|
||||
friendlyName: "ArangoDB", |
|
||||
description: |
|
||||
"ArangoDB is a scalable open-source multi-model database natively supporting graph, document and search. All supported data models & access patterns can be combined in queries allowing for maximal flexibility. ", |
|
||||
datasource: { |
|
||||
url: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
default: "http://localhost:8529", |
|
||||
required: true, |
|
||||
}, |
|
||||
username: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
default: "root", |
|
||||
required: true, |
|
||||
}, |
|
||||
password: { |
|
||||
type: FIELD_TYPES.PASSWORD, |
|
||||
required: true, |
|
||||
}, |
|
||||
databaseName: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
default: "_system", |
|
||||
required: true, |
|
||||
}, |
|
||||
collection: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
}, |
|
||||
query: { |
|
||||
read: { |
|
||||
type: QUERY_TYPES.SQL, |
|
||||
}, |
|
||||
create: { |
|
||||
type: QUERY_TYPES.JSON, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
class ArangoDBIntegration { |
|
||||
constructor(config) { |
|
||||
config.auth = { |
|
||||
username: config.username, |
|
||||
password: config.password, |
|
||||
} |
|
||||
|
|
||||
this.config = config |
|
||||
this.client = new Database(config) |
|
||||
} |
|
||||
|
|
||||
async read(query) { |
|
||||
try { |
|
||||
const result = await this.client.query(query.sql) |
|
||||
return result.all() |
|
||||
} catch (err) { |
|
||||
console.error("Error querying arangodb", err.message) |
|
||||
throw err |
|
||||
} finally { |
|
||||
this.client.close() |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
async create(query) { |
|
||||
const clc = this.client.collection(this.config.collection) |
|
||||
try { |
|
||||
const result = await this.client.query( |
|
||||
aql`INSERT ${query.json} INTO ${clc} RETURN NEW` |
|
||||
) |
|
||||
return result.all() |
|
||||
} catch (err) { |
|
||||
console.error("Error querying arangodb", err.message) |
|
||||
throw err |
|
||||
} finally { |
|
||||
this.client.close() |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
module.exports = { |
|
||||
schema: SCHEMA, |
|
||||
integration: ArangoDBIntegration, |
|
||||
} |
|
||||
@ -0,0 +1,106 @@ |
|||||
|
import { |
||||
|
Integration, |
||||
|
DatasourceFieldTypes, |
||||
|
QueryTypes, |
||||
|
} from "./base/definitions" |
||||
|
|
||||
|
module ArangoModule { |
||||
|
const { Database, aql } = require("arangojs") |
||||
|
|
||||
|
interface ArangodbConfig { |
||||
|
url: string |
||||
|
username: string |
||||
|
password: string |
||||
|
databaseName: string |
||||
|
collection: string |
||||
|
} |
||||
|
|
||||
|
const SCHEMA: Integration = { |
||||
|
docs: "https://github.com/arangodb/arangojs", |
||||
|
friendlyName: "ArangoDB", |
||||
|
description: |
||||
|
"ArangoDB is a scalable open-source multi-model database natively supporting graph, document and search. All supported data models & access patterns can be combined in queries allowing for maximal flexibility. ", |
||||
|
datasource: { |
||||
|
url: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
default: "http://localhost:8529", |
||||
|
required: true, |
||||
|
}, |
||||
|
username: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
default: "root", |
||||
|
required: true, |
||||
|
}, |
||||
|
password: { |
||||
|
type: DatasourceFieldTypes.PASSWORD, |
||||
|
required: true, |
||||
|
}, |
||||
|
databaseName: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
default: "_system", |
||||
|
required: true, |
||||
|
}, |
||||
|
collection: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
}, |
||||
|
query: { |
||||
|
read: { |
||||
|
type: QueryTypes.SQL, |
||||
|
}, |
||||
|
create: { |
||||
|
type: QueryTypes.JSON, |
||||
|
}, |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
class ArangoDBIntegration { |
||||
|
private config: ArangodbConfig |
||||
|
private client: any |
||||
|
|
||||
|
constructor(config: ArangodbConfig) { |
||||
|
const newConfig = { |
||||
|
auth: { |
||||
|
username: config.username, |
||||
|
password: config.password, |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
this.config = config |
||||
|
this.client = new Database(newConfig) |
||||
|
} |
||||
|
|
||||
|
async read(query: { sql: any }) { |
||||
|
try { |
||||
|
const result = await this.client.query(query.sql) |
||||
|
return result.all() |
||||
|
} catch (err) { |
||||
|
console.error("Error querying arangodb", err.message) |
||||
|
throw err |
||||
|
} finally { |
||||
|
this.client.close() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async create(query: { json: any }) { |
||||
|
const clc = this.client.collection(this.config.collection) |
||||
|
try { |
||||
|
const result = await this.client.query( |
||||
|
aql`INSERT ${query.json} INTO ${clc} RETURN NEW` |
||||
|
) |
||||
|
return result.all() |
||||
|
} catch (err) { |
||||
|
console.error("Error querying arangodb", err.message) |
||||
|
throw err |
||||
|
} finally { |
||||
|
this.client.close() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
module.exports = { |
||||
|
schema: SCHEMA, |
||||
|
integration: ArangoDBIntegration, |
||||
|
} |
||||
|
} |
||||
@ -1,11 +0,0 @@ |
|||||
exports.Operation = { |
|
||||
CREATE: "CREATE", |
|
||||
READ: "READ", |
|
||||
UPDATE: "UPDATE", |
|
||||
DELETE: "DELETE", |
|
||||
} |
|
||||
|
|
||||
exports.SortDirection = { |
|
||||
ASCENDING: "ASCENDING", |
|
||||
DESCENDING: "DESCENDING", |
|
||||
} |
|
||||
@ -0,0 +1,109 @@ |
|||||
|
export enum Operation { |
||||
|
CREATE = "CREATE", |
||||
|
READ = "READ", |
||||
|
UPDATE = "UPDATE", |
||||
|
DELETE = "DELETE", |
||||
|
} |
||||
|
|
||||
|
export enum SortDirection { |
||||
|
ASCENDING = "ASCENDING", |
||||
|
DESCENDING = "DESCENDING", |
||||
|
} |
||||
|
|
||||
|
export enum QueryTypes { |
||||
|
SQL = "sql", |
||||
|
JSON = "json", |
||||
|
FIELDS = "fields", |
||||
|
} |
||||
|
|
||||
|
export enum DatasourceFieldTypes { |
||||
|
STRING = "string", |
||||
|
BOOLEAN = "boolean", |
||||
|
NUMBER = "number", |
||||
|
PASSWORD = "password", |
||||
|
LIST = "list", |
||||
|
OBJECT = "object", |
||||
|
JSON = "json", |
||||
|
} |
||||
|
|
||||
|
export interface QueryDefinition { |
||||
|
type: QueryTypes |
||||
|
displayName?: string |
||||
|
readable?: boolean |
||||
|
customisable?: boolean |
||||
|
fields?: object |
||||
|
urlDisplay?: boolean |
||||
|
} |
||||
|
|
||||
|
export interface Integration { |
||||
|
docs: string |
||||
|
plus?: boolean |
||||
|
description: string |
||||
|
friendlyName: string |
||||
|
datasource: {} |
||||
|
query: { |
||||
|
[key: string]: QueryDefinition |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export interface SearchFilters { |
||||
|
allOr: boolean |
||||
|
string?: { |
||||
|
[key: string]: string |
||||
|
} |
||||
|
fuzzy?: { |
||||
|
[key: string]: string |
||||
|
} |
||||
|
range?: { |
||||
|
[key: string]: { |
||||
|
high: number | string |
||||
|
low: number | string |
||||
|
} |
||||
|
} |
||||
|
equal?: { |
||||
|
[key: string]: any |
||||
|
} |
||||
|
notEqual?: { |
||||
|
[key: string]: any |
||||
|
} |
||||
|
empty?: { |
||||
|
[key: string]: any |
||||
|
} |
||||
|
notEmpty?: { |
||||
|
[key: string]: any |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export interface QueryJson { |
||||
|
endpoint: { |
||||
|
datasourceId: string |
||||
|
entityId: string |
||||
|
operation: Operation |
||||
|
} |
||||
|
resource: { |
||||
|
fields: string[] |
||||
|
} |
||||
|
filters?: SearchFilters |
||||
|
sort?: { |
||||
|
[key: string]: SortDirection |
||||
|
} |
||||
|
paginate?: { |
||||
|
limit: number |
||||
|
page: string | number |
||||
|
} |
||||
|
body?: object |
||||
|
extra: { |
||||
|
idFilter?: SearchFilters |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export interface SqlQuery { |
||||
|
sql: string |
||||
|
bindings?: { |
||||
|
[key: string]: any |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export interface QueryOptions { |
||||
|
disableReturning?: boolean |
||||
|
} |
||||
@ -1,95 +0,0 @@ |
|||||
const PouchDB = require("pouchdb") |
|
||||
const { FIELD_TYPES, QUERY_TYPES } = require("./Integration") |
|
||||
|
|
||||
const SCHEMA = { |
|
||||
docs: "https://docs.couchdb.org/en/stable/", |
|
||||
friendlyName: "CouchDB", |
|
||||
description: |
|
||||
"Apache CouchDB is an open-source document-oriented NoSQL database, implemented in Erlang.", |
|
||||
datasource: { |
|
||||
url: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
default: "http://localhost:5984", |
|
||||
}, |
|
||||
database: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
}, |
|
||||
query: { |
|
||||
create: { |
|
||||
type: QUERY_TYPES.JSON, |
|
||||
}, |
|
||||
read: { |
|
||||
type: QUERY_TYPES.JSON, |
|
||||
}, |
|
||||
update: { |
|
||||
type: QUERY_TYPES.JSON, |
|
||||
}, |
|
||||
delete: { |
|
||||
type: QUERY_TYPES.FIELDS, |
|
||||
fields: { |
|
||||
id: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
class CouchDBIntegration { |
|
||||
constructor(config) { |
|
||||
this.config = config |
|
||||
this.client = new PouchDB(`${config.url}/${config.database}`) |
|
||||
} |
|
||||
|
|
||||
async create(query) { |
|
||||
try { |
|
||||
const result = await this.client.post(query.json) |
|
||||
return result |
|
||||
} catch (err) { |
|
||||
console.error("Error writing to couchDB", err) |
|
||||
throw err |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
async read(query) { |
|
||||
try { |
|
||||
const result = await this.client.allDocs({ |
|
||||
include_docs: true, |
|
||||
...query.json, |
|
||||
}) |
|
||||
return result.rows.map(row => row.doc) |
|
||||
} catch (err) { |
|
||||
console.error("Error querying couchDB", err) |
|
||||
throw err |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
async update(query) { |
|
||||
try { |
|
||||
const result = await this.client.put(query.json) |
|
||||
return result |
|
||||
} catch (err) { |
|
||||
console.error("Error updating couchDB document", err) |
|
||||
throw err |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
async delete(query) { |
|
||||
try { |
|
||||
const result = await this.client.remove(query.id) |
|
||||
return result |
|
||||
} catch (err) { |
|
||||
console.error("Error deleting couchDB document", err) |
|
||||
throw err |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
module.exports = { |
|
||||
schema: SCHEMA, |
|
||||
integration: CouchDBIntegration, |
|
||||
} |
|
||||
@ -0,0 +1,107 @@ |
|||||
|
import { |
||||
|
Integration, |
||||
|
DatasourceFieldTypes, |
||||
|
QueryTypes, |
||||
|
} from "./base/definitions" |
||||
|
|
||||
|
module CouchDBModule { |
||||
|
const PouchDB = require("pouchdb") |
||||
|
|
||||
|
interface CouchDBConfig { |
||||
|
url: string |
||||
|
database: string |
||||
|
} |
||||
|
|
||||
|
const SCHEMA: Integration = { |
||||
|
docs: "https://docs.couchdb.org/en/stable/", |
||||
|
friendlyName: "CouchDB", |
||||
|
description: |
||||
|
"Apache CouchDB is an open-source document-oriented NoSQL database, implemented in Erlang.", |
||||
|
datasource: { |
||||
|
url: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
default: "http://localhost:5984", |
||||
|
}, |
||||
|
database: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
}, |
||||
|
query: { |
||||
|
create: { |
||||
|
type: QueryTypes.JSON, |
||||
|
}, |
||||
|
read: { |
||||
|
type: QueryTypes.JSON, |
||||
|
}, |
||||
|
update: { |
||||
|
type: QueryTypes.JSON, |
||||
|
}, |
||||
|
delete: { |
||||
|
type: QueryTypes.FIELDS, |
||||
|
fields: { |
||||
|
id: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
class CouchDBIntegration { |
||||
|
private config: CouchDBConfig |
||||
|
private client: any |
||||
|
|
||||
|
constructor(config: CouchDBConfig) { |
||||
|
this.config = config |
||||
|
this.client = new PouchDB(`${config.url}/${config.database}`) |
||||
|
} |
||||
|
|
||||
|
async create(query: { json: object }) { |
||||
|
try { |
||||
|
return this.client.post(query.json) |
||||
|
} catch (err) { |
||||
|
console.error("Error writing to couchDB", err) |
||||
|
throw err |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async read(query: { json: object }) { |
||||
|
try { |
||||
|
const result = await this.client.allDocs({ |
||||
|
include_docs: true, |
||||
|
...query.json, |
||||
|
}) |
||||
|
return result.rows.map((row: { doc: object }) => row.doc) |
||||
|
} catch (err) { |
||||
|
console.error("Error querying couchDB", err) |
||||
|
throw err |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async update(query: { json: object }) { |
||||
|
try { |
||||
|
return this.client.put(query.json) |
||||
|
} catch (err) { |
||||
|
console.error("Error updating couchDB document", err) |
||||
|
throw err |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async delete(query: { id: string }) { |
||||
|
try { |
||||
|
return await this.client.remove(query.id) |
||||
|
} catch (err) { |
||||
|
console.error("Error deleting couchDB document", err) |
||||
|
throw err |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
module.exports = { |
||||
|
schema: SCHEMA, |
||||
|
integration: CouchDBIntegration, |
||||
|
} |
||||
|
} |
||||
@ -1,200 +0,0 @@ |
|||||
const AWS = require("aws-sdk") |
|
||||
const { FIELD_TYPES, QUERY_TYPES } = require("./Integration") |
|
||||
const { AWS_REGION } = require("../db/dynamoClient") |
|
||||
|
|
||||
const SCHEMA = { |
|
||||
docs: "https://github.com/dabit3/dynamodb-documentclient-cheat-sheet", |
|
||||
description: |
|
||||
"Amazon DynamoDB is a key-value and document database that delivers single-digit millisecond performance at any scale.", |
|
||||
friendlyName: "DynamoDB", |
|
||||
datasource: { |
|
||||
region: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
default: "us-east-1", |
|
||||
}, |
|
||||
accessKeyId: { |
|
||||
type: FIELD_TYPES.PASSWORD, |
|
||||
required: true, |
|
||||
}, |
|
||||
secretAccessKey: { |
|
||||
type: FIELD_TYPES.PASSWORD, |
|
||||
required: true, |
|
||||
}, |
|
||||
endpoint: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: false, |
|
||||
default: "https://dynamodb.us-east-1.amazonaws.com", |
|
||||
}, |
|
||||
}, |
|
||||
query: { |
|
||||
create: { |
|
||||
type: QUERY_TYPES.FIELDS, |
|
||||
customisable: true, |
|
||||
fields: { |
|
||||
table: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
read: { |
|
||||
type: QUERY_TYPES.FIELDS, |
|
||||
customisable: true, |
|
||||
readable: true, |
|
||||
fields: { |
|
||||
table: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
index: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
scan: { |
|
||||
type: QUERY_TYPES.FIELDS, |
|
||||
customisable: true, |
|
||||
readable: true, |
|
||||
fields: { |
|
||||
table: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
index: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
get: { |
|
||||
type: QUERY_TYPES.FIELDS, |
|
||||
customisable: true, |
|
||||
readable: true, |
|
||||
fields: { |
|
||||
table: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
update: { |
|
||||
type: QUERY_TYPES.FIELDS, |
|
||||
customisable: true, |
|
||||
fields: { |
|
||||
table: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
delete: { |
|
||||
type: QUERY_TYPES.FIELDS, |
|
||||
customisable: true, |
|
||||
fields: { |
|
||||
table: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
class DynamoDBIntegration { |
|
||||
constructor(config) { |
|
||||
this.config = config |
|
||||
this.connect() |
|
||||
let options = { |
|
||||
correctClockSkew: true, |
|
||||
} |
|
||||
if (config.endpoint) { |
|
||||
options.endpoint = config.endpoint |
|
||||
} |
|
||||
this.client = new AWS.DynamoDB.DocumentClient({ |
|
||||
correctClockSkew: true, |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
end() { |
|
||||
this.disconnect() |
|
||||
} |
|
||||
|
|
||||
connect() { |
|
||||
AWS.config.update(this.config) |
|
||||
} |
|
||||
|
|
||||
disconnect() { |
|
||||
AWS.config.update({ |
|
||||
secretAccessKey: undefined, |
|
||||
accessKeyId: undefined, |
|
||||
region: AWS_REGION, |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
async create(query) { |
|
||||
const params = { |
|
||||
TableName: query.table, |
|
||||
...query.json, |
|
||||
} |
|
||||
return this.client.put(params).promise() |
|
||||
} |
|
||||
|
|
||||
async read(query) { |
|
||||
const params = { |
|
||||
TableName: query.table, |
|
||||
...query.json, |
|
||||
} |
|
||||
if (query.index) { |
|
||||
params.IndexName = query.index |
|
||||
} |
|
||||
const response = await this.client.query(params).promise() |
|
||||
if (response.Items) { |
|
||||
return response.Items |
|
||||
} |
|
||||
return response |
|
||||
} |
|
||||
|
|
||||
async scan(query) { |
|
||||
const params = { |
|
||||
TableName: query.table, |
|
||||
...query.json, |
|
||||
} |
|
||||
if (query.index) { |
|
||||
params.IndexName = query.index |
|
||||
} |
|
||||
const response = await this.client.scan(params).promise() |
|
||||
if (response.Items) { |
|
||||
return response.Items |
|
||||
} |
|
||||
return response |
|
||||
} |
|
||||
|
|
||||
async get(query) { |
|
||||
const params = { |
|
||||
TableName: query.table, |
|
||||
...query.json, |
|
||||
} |
|
||||
return this.client.get(params).promise() |
|
||||
} |
|
||||
|
|
||||
async update(query) { |
|
||||
const params = { |
|
||||
TableName: query.table, |
|
||||
...query.json, |
|
||||
} |
|
||||
return this.client.update(params).promise() |
|
||||
} |
|
||||
|
|
||||
async delete(query) { |
|
||||
const params = { |
|
||||
TableName: query.table, |
|
||||
...query.json, |
|
||||
} |
|
||||
return this.client.delete(params).promise() |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
module.exports = { |
|
||||
schema: SCHEMA, |
|
||||
integration: DynamoDBIntegration, |
|
||||
} |
|
||||
@ -0,0 +1,211 @@ |
|||||
|
import { |
||||
|
Integration, |
||||
|
DatasourceFieldTypes, |
||||
|
QueryTypes, |
||||
|
} from "./base/definitions" |
||||
|
|
||||
|
module DynamoModule { |
||||
|
const AWS = require("aws-sdk") |
||||
|
const { AWS_REGION } = require("../db/dynamoClient") |
||||
|
|
||||
|
interface DynamoDBConfig { |
||||
|
region: string |
||||
|
accessKeyId: string |
||||
|
secretAccessKey: string |
||||
|
endpoint: string |
||||
|
} |
||||
|
|
||||
|
const SCHEMA: Integration = { |
||||
|
docs: "https://github.com/dabit3/dynamodb-documentclient-cheat-sheet", |
||||
|
description: |
||||
|
"Amazon DynamoDB is a key-value and document database that delivers single-digit millisecond performance at any scale.", |
||||
|
friendlyName: "DynamoDB", |
||||
|
datasource: { |
||||
|
region: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
default: "us-east-1", |
||||
|
}, |
||||
|
accessKeyId: { |
||||
|
type: DatasourceFieldTypes.PASSWORD, |
||||
|
required: true, |
||||
|
}, |
||||
|
secretAccessKey: { |
||||
|
type: DatasourceFieldTypes.PASSWORD, |
||||
|
required: true, |
||||
|
}, |
||||
|
endpoint: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: false, |
||||
|
default: "https://dynamodb.us-east-1.amazonaws.com", |
||||
|
}, |
||||
|
}, |
||||
|
query: { |
||||
|
create: { |
||||
|
type: QueryTypes.FIELDS, |
||||
|
customisable: true, |
||||
|
fields: { |
||||
|
table: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
read: { |
||||
|
type: QueryTypes.FIELDS, |
||||
|
customisable: true, |
||||
|
readable: true, |
||||
|
fields: { |
||||
|
table: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
index: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
scan: { |
||||
|
type: QueryTypes.FIELDS, |
||||
|
customisable: true, |
||||
|
readable: true, |
||||
|
fields: { |
||||
|
table: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
index: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
get: { |
||||
|
type: QueryTypes.FIELDS, |
||||
|
customisable: true, |
||||
|
readable: true, |
||||
|
fields: { |
||||
|
table: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
update: { |
||||
|
type: QueryTypes.FIELDS, |
||||
|
customisable: true, |
||||
|
fields: { |
||||
|
table: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
delete: { |
||||
|
type: QueryTypes.FIELDS, |
||||
|
customisable: true, |
||||
|
fields: { |
||||
|
table: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
class DynamoDBIntegration { |
||||
|
private config: DynamoDBConfig |
||||
|
private client: any |
||||
|
|
||||
|
constructor(config: DynamoDBConfig) { |
||||
|
this.config = config |
||||
|
this.connect() |
||||
|
let options = { |
||||
|
correctClockSkew: true, |
||||
|
endpoint: config.endpoint ? config.endpoint : undefined, |
||||
|
} |
||||
|
this.client = new AWS.DynamoDB.DocumentClient(options) |
||||
|
} |
||||
|
|
||||
|
end() { |
||||
|
this.disconnect() |
||||
|
} |
||||
|
|
||||
|
connect() { |
||||
|
AWS.config.update(this.config) |
||||
|
} |
||||
|
|
||||
|
disconnect() { |
||||
|
AWS.config.update({ |
||||
|
secretAccessKey: undefined, |
||||
|
accessKeyId: undefined, |
||||
|
region: AWS_REGION, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
async create(query: { table: string; json: object }) { |
||||
|
const params = { |
||||
|
TableName: query.table, |
||||
|
...query.json, |
||||
|
} |
||||
|
return this.client.put(params).promise() |
||||
|
} |
||||
|
|
||||
|
async read(query: { table: string; json: object; index: null | string }) { |
||||
|
const params = { |
||||
|
TableName: query.table, |
||||
|
IndexName: query.index ? query.index : undefined, |
||||
|
...query.json, |
||||
|
} |
||||
|
if (query.index) { |
||||
|
const response = await this.client.query(params).promise() |
||||
|
if (response.Items) { |
||||
|
return response.Items |
||||
|
} |
||||
|
return response |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async scan(query: { table: string; json: object; index: null | string }) { |
||||
|
const params = { |
||||
|
TableName: query.table, |
||||
|
IndexName: query.index ? query.index : undefined, |
||||
|
...query.json, |
||||
|
} |
||||
|
const response = await this.client.scan(params).promise() |
||||
|
if (response.Items) { |
||||
|
return response.Items |
||||
|
} |
||||
|
return response |
||||
|
} |
||||
|
|
||||
|
async get(query: { table: string; json: object }) { |
||||
|
const params = { |
||||
|
TableName: query.table, |
||||
|
...query.json, |
||||
|
} |
||||
|
return this.client.get(params).promise() |
||||
|
} |
||||
|
|
||||
|
async update(query: { table: string; json: object }) { |
||||
|
const params = { |
||||
|
TableName: query.table, |
||||
|
...query.json, |
||||
|
} |
||||
|
return this.client.update(params).promise() |
||||
|
} |
||||
|
|
||||
|
async delete(query: { table: string; json: object }) { |
||||
|
const params = { |
||||
|
TableName: query.table, |
||||
|
...query.json, |
||||
|
} |
||||
|
return this.client.delete(params).promise() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
module.exports = { |
||||
|
schema: SCHEMA, |
||||
|
integration: DynamoDBIntegration, |
||||
|
} |
||||
|
} |
||||
@ -1,139 +0,0 @@ |
|||||
const { Client } = require("@elastic/elasticsearch") |
|
||||
const { QUERY_TYPES, FIELD_TYPES } = require("./Integration") |
|
||||
|
|
||||
const SCHEMA = { |
|
||||
docs: "https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/index.html", |
|
||||
description: |
|
||||
"Elasticsearch is a search engine based on the Lucene library. It provides a distributed, multitenant-capable full-text search engine with an HTTP web interface and schema-free JSON documents.", |
|
||||
friendlyName: "ElasticSearch", |
|
||||
datasource: { |
|
||||
url: { |
|
||||
type: "string", |
|
||||
required: true, |
|
||||
default: "http://localhost:9200", |
|
||||
}, |
|
||||
}, |
|
||||
query: { |
|
||||
create: { |
|
||||
type: QUERY_TYPES.FIELDS, |
|
||||
customisable: true, |
|
||||
fields: { |
|
||||
index: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
read: { |
|
||||
type: QUERY_TYPES.FIELDS, |
|
||||
customisable: true, |
|
||||
fields: { |
|
||||
index: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
update: { |
|
||||
type: QUERY_TYPES.FIELDS, |
|
||||
customisable: true, |
|
||||
fields: { |
|
||||
id: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
index: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
delete: { |
|
||||
type: QUERY_TYPES.FIELDS, |
|
||||
fields: { |
|
||||
index: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
id: { |
|
||||
type: FIELD_TYPES.STRING, |
|
||||
required: true, |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
class ElasticSearchIntegration { |
|
||||
constructor(config) { |
|
||||
this.config = config |
|
||||
this.client = new Client({ node: config.url }) |
|
||||
} |
|
||||
|
|
||||
async create(query) { |
|
||||
const { index, json } = query |
|
||||
|
|
||||
try { |
|
||||
const result = await this.client.index({ |
|
||||
index, |
|
||||
body: json, |
|
||||
}) |
|
||||
return result.body |
|
||||
} catch (err) { |
|
||||
console.error("Error writing to elasticsearch", err) |
|
||||
throw err |
|
||||
} finally { |
|
||||
await this.client.close() |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
async read(query) { |
|
||||
const { index, json } = query |
|
||||
try { |
|
||||
const result = await this.client.search({ |
|
||||
index: index, |
|
||||
body: json, |
|
||||
}) |
|
||||
return result.body.hits.hits.map(({ _source }) => _source) |
|
||||
} catch (err) { |
|
||||
console.error("Error querying elasticsearch", err) |
|
||||
throw err |
|
||||
} finally { |
|
||||
await this.client.close() |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
async update(query) { |
|
||||
const { id, index, json } = query |
|
||||
try { |
|
||||
const result = await this.client.update({ |
|
||||
id, |
|
||||
index, |
|
||||
body: json, |
|
||||
}) |
|
||||
return result.body |
|
||||
} catch (err) { |
|
||||
console.error("Error querying elasticsearch", err) |
|
||||
throw err |
|
||||
} finally { |
|
||||
await this.client.close() |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
async delete(query) { |
|
||||
try { |
|
||||
const result = await this.client.delete(query) |
|
||||
return result.body |
|
||||
} catch (err) { |
|
||||
console.error("Error deleting from elasticsearch", err) |
|
||||
throw err |
|
||||
} finally { |
|
||||
await this.client.close() |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
module.exports = { |
|
||||
schema: SCHEMA, |
|
||||
integration: ElasticSearchIntegration, |
|
||||
} |
|
||||
@ -0,0 +1,153 @@ |
|||||
|
import { |
||||
|
Integration, |
||||
|
DatasourceFieldTypes, |
||||
|
QueryTypes, |
||||
|
} from "./base/definitions" |
||||
|
|
||||
|
module ElasticsearchModule { |
||||
|
const { Client } = require("@elastic/elasticsearch") |
||||
|
|
||||
|
interface ElasticsearchConfig { |
||||
|
url: string |
||||
|
} |
||||
|
|
||||
|
const SCHEMA: Integration = { |
||||
|
docs: "https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/index.html", |
||||
|
description: |
||||
|
"Elasticsearch is a search engine based on the Lucene library. It provides a distributed, multitenant-capable full-text search engine with an HTTP web interface and schema-free JSON documents.", |
||||
|
friendlyName: "ElasticSearch", |
||||
|
datasource: { |
||||
|
url: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
default: "http://localhost:9200", |
||||
|
}, |
||||
|
}, |
||||
|
query: { |
||||
|
create: { |
||||
|
type: QueryTypes.FIELDS, |
||||
|
customisable: true, |
||||
|
fields: { |
||||
|
index: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
read: { |
||||
|
type: QueryTypes.FIELDS, |
||||
|
customisable: true, |
||||
|
fields: { |
||||
|
index: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
update: { |
||||
|
type: QueryTypes.FIELDS, |
||||
|
customisable: true, |
||||
|
fields: { |
||||
|
id: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
index: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
delete: { |
||||
|
type: QueryTypes.FIELDS, |
||||
|
fields: { |
||||
|
index: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
id: { |
||||
|
type: DatasourceFieldTypes.STRING, |
||||
|
required: true, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
class ElasticSearchIntegration { |
||||
|
private config: ElasticsearchConfig |
||||
|
private client: any |
||||
|
|
||||
|
constructor(config: ElasticsearchConfig) { |
||||
|
this.config = config |
||||
|
this.client = new Client({ node: config.url }) |
||||
|
} |
||||
|
|
||||
|
async create(query: { index: string; json: object }) { |
||||
|
const { index, json } = query |
||||
|
|
||||
|
try { |
||||
|
const result = await this.client.index({ |
||||
|
index, |
||||
|
body: json, |
||||
|
}) |
||||
|
return result.body |
||||
|
} catch (err) { |
||||
|
console.error("Error writing to elasticsearch", err) |
||||
|
throw err |
||||
|
} finally { |
||||
|
await this.client.close() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async read(query: { index: string; json: object }) { |
||||
|
const { index, json } = query |
||||
|
try { |
||||
|
const result = await this.client.search({ |
||||
|
index: index, |
||||
|
body: json, |
||||
|
}) |
||||
|
return result.body.hits.hits.map(({ _source }: any) => _source) |
||||
|
} catch (err) { |
||||
|
console.error("Error querying elasticsearch", err) |
||||
|
throw err |
||||
|
} finally { |
||||
|
await this.client.close() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async update(query: { id: string; index: string; json: object }) { |
||||
|
const { id, index, json } = query |
||||
|
try { |
||||
|
const result = await this.client.update({ |
||||
|
id, |
||||
|
index, |
||||
|
body: json, |
||||
|
}) |
||||
|
return result.body |
||||
|
} catch (err) { |
||||
|
console.error("Error querying elasticsearch", err) |
||||
|
throw err |
||||
|
} finally { |
||||
|
await this.client.close() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async delete(query: object) { |
||||
|
try { |
||||
|
const result = await this.client.delete(query) |
||||
|
return result.body |
||||
|
} catch (err) { |
||||
|
console.error("Error deleting from elasticsearch", err) |
||||
|
throw err |
||||
|
} finally { |
||||
|
await this.client.close() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
module.exports = { |
||||
|
schema: SCHEMA, |
||||
|
integration: ElasticSearchIntegration, |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue