2
0

Add NocoDB block (#1365)

#970 #997

Fully integrate NocoDB.

Added all API Functions:

- List Table Records
- Create Table Records
- Update Table Records
- Delete Table Records
- Read Table Record
- Count Table Records
- List Linked Records
- Link Records
- Unlink Records

Optional Todo:
- Save responses of non-get requests in a variable (error validation
try-catch is added and logged so i do not think so it is much needed)

You are free to implement any extra validation/function :D

---------

Co-authored-by: Baptiste Arnaud <baptiste.arnaud95@gmail.com>
This commit is contained in:
Abdullah bin Amir
2024-05-27 12:46:42 +04:00
committed by GitHub
parent 3e4e7531f6
commit a17781dfa6
35 changed files with 1158 additions and 38 deletions

View File

@@ -6,12 +6,14 @@ import { ZodObjectLayout } from './zodLayouts/ZodObjectLayout'
import { ZodActionDiscriminatedUnion } from './zodLayouts/ZodActionDiscriminatedUnion'
import { useForgedBlock } from '../hooks/useForgedBlock'
import { ForgedBlock } from '@typebot.io/forge-repository/types'
import { useState } from 'react'
type Props = {
block: ForgedBlock
onOptionsChange: (options: BlockOptions) => void
}
export const ForgedBlockSettings = ({ block, onOptionsChange }: Props) => {
const [keySuffix, setKeySuffix] = useState<number>(0)
const { blockDef, blockSchema, actionDef } = useForgedBlock(
block.type,
block.options?.action
@@ -32,7 +34,10 @@ export const ForgedBlockSettings = ({ block, onOptionsChange }: Props) => {
const actionOptions = actionOptionsKeys.reduce(
(acc, key) => ({
...acc,
[key]: undefined,
[key]:
block.options[key] && typeof block.options[key] !== 'object'
? block.options[key]
: undefined,
}),
{}
)
@@ -40,6 +45,7 @@ export const ForgedBlockSettings = ({ block, onOptionsChange }: Props) => {
...updates,
...actionOptions,
})
setKeySuffix((prev) => prev + 1)
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -85,6 +91,7 @@ export const ForgedBlockSettings = ({ block, onOptionsChange }: Props) => {
/>
)}
<ZodActionDiscriminatedUnion
key={block.id + keySuffix}
schema={blockSchema.shape.options}
blockDef={blockDef}
blockOptions={block.options}

View File

@@ -1,6 +1,7 @@
import { NumberInput, TextInput, Textarea } from '@/components/inputs'
import { z } from '@typebot.io/forge/zod'
import { ZodLayoutMetadata } from '@typebot.io/forge/zod'
import { evaluateIsHidden } from '@typebot.io/forge/zod/helpers/evaluateIsHidden'
import Markdown, { Components } from 'react-markdown'
import { ZodTypeAny } from 'zod'
import { ForgeSelectInput } from '../ForgeSelectInput'
@@ -64,7 +65,7 @@ export const ZodFieldLayout = ({
const innerSchema = getZodInnerSchema(schema)
const layout = innerSchema._def.layout
if (layout?.isHidden) return null
if (evaluateIsHidden(layout?.isHidden, blockOptions)) return null
switch (innerSchema._def.typeName) {
case 'ZodObject':

View File

@@ -19,6 +19,7 @@ import {
ForgedBlock,
} from '@typebot.io/forge-repository/types'
import { getZodInnerSchema } from '../../helpers/getZodInnerSchema'
import { evaluateIsHidden } from '@typebot.io/forge/zod/helpers/evaluateIsHidden'
export const ZodObjectLayout = ({
schema,
@@ -37,20 +38,23 @@ export const ZodObjectLayout = ({
blockOptions?: ForgedBlock['options']
onDataChange: (value: any) => void
}): ReactNode[] => {
const layout = getZodInnerSchema(schema)._def.layout
if (layout?.isHidden) return []
return Object.keys(schema.shape).reduce<{
const innerSchema = getZodInnerSchema(schema)
const shape =
'shape' in innerSchema ? innerSchema.shape : innerSchema._def.shape()
const layout = innerSchema._def.layout
if (evaluateIsHidden(layout?.isHidden, blockOptions)) return []
return Object.keys(shape).reduce<{
nodes: ReactNode[]
accordionsCreated: string[]
}>(
(nodes, key, index) => {
if (ignoreKeys?.includes(key)) return nodes
const keySchema = getZodInnerSchema(schema.shape[key])
const keySchema = getZodInnerSchema(shape[key])
const layout = keySchema._def.layout as
| ZodLayoutMetadata<ZodTypeAny>
| undefined
if (layout?.isHidden) return nodes
if (evaluateIsHidden(layout?.isHidden, blockOptions)) return nodes
if (
layout &&
layout.accordion &&
@@ -60,7 +64,7 @@ export const ZodObjectLayout = ({
if (nodes.accordionsCreated.includes(layout.accordion)) return nodes
const accordionKeys = getObjectKeysWithSameAccordionAttr(
layout.accordion,
schema
shape
)
return {
nodes: [
@@ -77,7 +81,7 @@ export const ZodObjectLayout = ({
{accordionKeys.map((accordionKey, idx) => (
<ZodFieldLayout
key={accordionKey + idx}
schema={schema.shape[accordionKey]}
schema={shape[accordionKey]}
data={data?.[accordionKey]}
onDataChange={(val) =>
onDataChange({ ...data, [accordionKey]: val })
@@ -118,12 +122,9 @@ export const ZodObjectLayout = ({
).nodes
}
const getObjectKeysWithSameAccordionAttr = (
accordion: string,
schema: z.ZodObject<any>
) =>
Object.keys(schema.shape).reduce<string[]>((keys, currentKey) => {
const l = schema.shape[currentKey]._def.layout as
const getObjectKeysWithSameAccordionAttr = (accordion: string, shape: any) =>
Object.keys(shape).reduce<string[]>((keys, currentKey) => {
const l = shape[currentKey]._def.layout as
| ZodLayoutMetadata<ZodTypeAny>
| undefined
return !l?.accordion || l.accordion !== accordion

View File

@@ -2,11 +2,6 @@
title: Anthropic
---
<Warning>
There is an ongoing issue with Anthropic block streaming capabilities. We are
working on a fix and will update this page once the issue is resolved.
</Warning>
## Create Message
With the Anthropic block, you can create chat messages based on your user queries and display the answer back to your typebot using Claude AI.

View File

@@ -0,0 +1,31 @@
---
title: NocoDB
---
With the NocoDB block, you can create, update or get data from your NocoDB tables.
## How to find my `Table ID`?
To find your `Table ID`, you need to go to your NocoDB dashboard and click on the 3 dots button next to your table name.
<Frame>
<img
src="/images/blocks/integrations/nocodb-table-id.jpg"
alt="NocoDB table ID"
/>
</Frame>
## Search Records
This action allows you to search for existing records in a table. It requires your `Table ID` and can optionally take a `View ID` to search in a specific view.
<Frame>
<img
src="/images/blocks/integrations/nocodb.jpg"
alt="NocoDB block example"
/>
</Frame>
You can configure the filter to return `All`, `First`, `Last` or `Random` found records.
Then all you need to do is to map the found fields to variables that you can re-use on your bot.

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View File

@@ -126,7 +126,8 @@
"editor/blocks/integrations/mistral",
"editor/blocks/integrations/elevenlabs",
"editor/blocks/integrations/anthropic",
"editor/blocks/integrations/dify-ai"
"editor/blocks/integrations/dify-ai",
"editor/blocks/integrations/nocodb"
]
}
]

View File

@@ -20532,6 +20532,242 @@
"id",
"type"
]
},
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"outgoingEdgeId": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"nocodb"
]
},
"options": {
"oneOf": [
{
"type": "object",
"properties": {
"credentialsId": {
"type": "string"
}
}
},
{
"type": "object",
"properties": {
"credentialsId": {
"type": "string"
},
"action": {
"type": "string",
"enum": [
"Search Records"
]
},
"tableId": {
"type": "string"
},
"viewId": {
"type": "string"
},
"returnType": {
"type": "string",
"enum": [
"All",
"First",
"Last",
"Random"
]
},
"filter": {
"type": "object",
"properties": {
"comparisons": {
"type": "array",
"items": {
"type": "object",
"properties": {
"input": {
"type": "string"
},
"operator": {
"type": "string",
"enum": [
"Equal to",
"Not equal",
"Contains",
"Greater than",
"Less than",
"Is set",
"Is empty",
"Starts with",
"Ends with"
]
},
"value": {
"type": "string"
}
}
}
},
"joiner": {
"type": "string",
"enum": [
"AND",
"OR"
]
}
},
"required": [
"comparisons"
]
},
"responseMapping": {
"type": "array",
"items": {
"type": "object",
"properties": {
"fieldName": {
"type": "string"
},
"variableId": {
"type": "string"
}
}
}
}
},
"required": [
"action"
]
},
{
"type": "object",
"properties": {
"credentialsId": {
"type": "string"
},
"action": {
"type": "string",
"enum": [
"Create Record"
]
},
"tableId": {
"type": "string"
},
"fields": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
}
},
"required": [
"action"
]
},
{
"type": "object",
"properties": {
"credentialsId": {
"type": "string"
},
"action": {
"type": "string",
"enum": [
"Update Existing Record"
]
},
"tableId": {
"type": "string"
},
"viewId": {
"type": "string"
},
"filter": {
"type": "object",
"properties": {
"comparisons": {
"type": "array",
"items": {
"type": "object",
"properties": {
"input": {
"type": "string"
},
"operator": {
"type": "string",
"enum": [
"Equal to",
"Not equal",
"Contains",
"Greater than",
"Less than",
"Is set",
"Is empty",
"Starts with",
"Ends with"
]
},
"value": {
"type": "string"
}
}
}
},
"joiner": {
"type": "string",
"enum": [
"AND",
"OR"
]
}
},
"required": [
"comparisons"
]
},
"updates": {
"type": "array",
"items": {
"type": "object",
"properties": {
"fieldName": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
}
},
"required": [
"action"
]
}
]
}
},
"required": [
"id",
"type"
]
}
],
"title": "Block"

View File

@@ -11513,6 +11513,242 @@
"id",
"type"
]
},
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"outgoingEdgeId": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"nocodb"
]
},
"options": {
"oneOf": [
{
"type": "object",
"properties": {
"credentialsId": {
"type": "string"
}
}
},
{
"type": "object",
"properties": {
"credentialsId": {
"type": "string"
},
"action": {
"type": "string",
"enum": [
"Search Records"
]
},
"tableId": {
"type": "string"
},
"viewId": {
"type": "string"
},
"returnType": {
"type": "string",
"enum": [
"All",
"First",
"Last",
"Random"
]
},
"filter": {
"type": "object",
"properties": {
"comparisons": {
"type": "array",
"items": {
"type": "object",
"properties": {
"input": {
"type": "string"
},
"operator": {
"type": "string",
"enum": [
"Equal to",
"Not equal",
"Contains",
"Greater than",
"Less than",
"Is set",
"Is empty",
"Starts with",
"Ends with"
]
},
"value": {
"type": "string"
}
}
}
},
"joiner": {
"type": "string",
"enum": [
"AND",
"OR"
]
}
},
"required": [
"comparisons"
]
},
"responseMapping": {
"type": "array",
"items": {
"type": "object",
"properties": {
"fieldName": {
"type": "string"
},
"variableId": {
"type": "string"
}
}
}
}
},
"required": [
"action"
]
},
{
"type": "object",
"properties": {
"credentialsId": {
"type": "string"
},
"action": {
"type": "string",
"enum": [
"Create Record"
]
},
"tableId": {
"type": "string"
},
"fields": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
}
},
"required": [
"action"
]
},
{
"type": "object",
"properties": {
"credentialsId": {
"type": "string"
},
"action": {
"type": "string",
"enum": [
"Update Existing Record"
]
},
"tableId": {
"type": "string"
},
"viewId": {
"type": "string"
},
"filter": {
"type": "object",
"properties": {
"comparisons": {
"type": "array",
"items": {
"type": "object",
"properties": {
"input": {
"type": "string"
},
"operator": {
"type": "string",
"enum": [
"Equal to",
"Not equal",
"Contains",
"Greater than",
"Less than",
"Is set",
"Is empty",
"Starts with",
"Ends with"
]
},
"value": {
"type": "string"
}
}
}
},
"joiner": {
"type": "string",
"enum": [
"AND",
"OR"
]
}
},
"required": [
"comparisons"
]
},
"updates": {
"type": "array",
"items": {
"type": "object",
"properties": {
"fieldName": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
}
},
"required": [
"action"
]
}
]
}
},
"required": [
"id",
"type"
]
}
],
"title": "Block"