diff --git a/bun.lock b/bun.lock index 38e62bb042b..594c1d23871 100644 --- a/bun.lock +++ b/bun.lock @@ -481,7 +481,7 @@ }, "packages/pieces/community/ai": { "name": "@activepieces/piece-ai", - "version": "0.3.9", + "version": "0.4.0", "dependencies": { "@activepieces/pieces-common": "0.12.3", "@activepieces/pieces-framework": "0.26.2", @@ -6243,7 +6243,7 @@ }, "packages/pieces/community/slack": { "name": "@activepieces/piece-slack", - "version": "0.16.5", + "version": "0.17.0", "dependencies": { "@activepieces/pieces-common": "workspace:*", "@activepieces/pieces-framework": "workspace:*", diff --git a/docs/admin-guide/guides/sso.mdx b/docs/admin-guide/guides/sso.mdx index de35f945ac1..07eeaf6a6f3 100644 --- a/docs/admin-guide/guides/sso.mdx +++ b/docs/admin-guide/guides/sso.mdx @@ -131,6 +131,47 @@ Activepieces supports multiple SSO providers to integrate with your existing ide +### SAML with Microsoft Entra ID (Azure AD) + + + + Go to the [Azure Portal](https://portal.azure.com/) → **Microsoft Entra ID** → **Enterprise applications** → **New application** → **Create your own application**. + + Name it (e.g., "Activepieces") and select **Integrate any other application you don't find in the gallery (Non-gallery)**. + + + Open the application → **Single sign-on** → select **SAML**. + + + Edit **Basic SAML Configuration**: + - **Identifier (Entity ID)**: `Activepieces` + - **Reply URL (Assertion Consumer Service URL)**: paste the SSO URL from the Activepieces configuration screen + + + Edit **Attributes & Claims** and add these additional claims (leave **Namespace** empty): + + | Claim name | Source attribute | + |------------|------------------| + | `firstName` | `user.givenname` | + | `lastName` | `user.surname` | + | `email` | `user.mail` | + + + In the **SAML Certificates** section, copy the **App Federation Metadata Url**. + + You can paste this URL directly into the **IdP Metadata** field in Activepieces — Activepieces will fetch the metadata XML automatically. Alternatively, open the URL in a browser, save the XML, and paste its contents. + + + Download the **Certificate (Base64)** from the **SAML Certificates** section. Open the file and copy its contents (including the `-----BEGIN CERTIFICATE-----` / `-----END CERTIFICATE-----` markers) into the **Signing Key** field in Activepieces. + + + Go to **Users and groups** in the application and assign the users or groups that should be allowed to sign in. + + + Click **Save** in Activepieces to complete the setup. + + + ### SAML with JumpCloud @@ -208,6 +249,7 @@ Activepieces supports multiple SSO providers to integrate with your existing ide - Confirm the IdP metadata is complete and correctly formatted + - If you pasted a metadata URL, make sure it is publicly reachable (Activepieces fetches it server-side) - Verify the signing certificate is properly formatted with BEGIN/END markers - Ensure all required attributes (firstName, lastName, email) are mapped diff --git a/packages/pieces/community/ai/package.json b/packages/pieces/community/ai/package.json index 846a9931609..c9bc5904a91 100644 --- a/packages/pieces/community/ai/package.json +++ b/packages/pieces/community/ai/package.json @@ -1,6 +1,6 @@ { "name": "@activepieces/piece-ai", - "version": "0.3.9", + "version": "0.4.0", "type": "commonjs", "main": "./dist/src/index.js", "types": "./dist/src/index.d.ts", diff --git a/packages/pieces/community/ai/src/i18n/translation.json b/packages/pieces/community/ai/src/i18n/translation.json index fcffb0506d8..932ecadfbff 100644 --- a/packages/pieces/community/ai/src/i18n/translation.json +++ b/packages/pieces/community/ai/src/i18n/translation.json @@ -21,6 +21,9 @@ "Web Search Options": "Web Search Options", "Text": "Text", "Advanced Options": "Advanced Options", + "Input Images": "Input Images", + "Image File": "Image File", + "Provide images for editing, variation, or merging. Support depends on the selected model.": "Provide images for editing, variation, or merging. Support depends on the selected model.", "Text to Classify": "Text to Classify", "Categories": "Categories", "Files": "Files", diff --git a/packages/pieces/community/ai/src/lib/actions/image/generate-image.ts b/packages/pieces/community/ai/src/lib/actions/image/generate-image.ts index 070959a9759..ec975a4afc8 100644 --- a/packages/pieces/community/ai/src/lib/actions/image/generate-image.ts +++ b/packages/pieces/community/ai/src/lib/actions/image/generate-image.ts @@ -32,6 +32,18 @@ export const generateImageAction = createAction({ displayName: 'Prompt', required: true, }), + inputImages: Property.Array({ + displayName: 'Input Images', + description: + 'Provide images for editing, variation, or merging. Support depends on the selected model.', + required: false, + properties: { + file: Property.File({ + displayName: 'Image File', + required: true, + }), + }, + }), advancedOptions: Property.DynamicProperties({ displayName: 'Advanced Options', required: false, @@ -120,25 +132,6 @@ export const generateImageAction = createAction({ return options; } - if ( - providerId === AIProviderName.GOOGLE && - modelId === 'gemini-2.5-flash-image-preview' - ) { - options = { - image: Property.Array({ - displayName: 'Images', - required: false, - properties: { - file: Property.File({ - displayName: 'Image File', - required: true, - }), - }, - description: 'The image(s) you want to edit/merge', - }), - }; - } - return options; }, }), @@ -147,12 +140,18 @@ export const generateImageAction = createAction({ const provider = context.propsValue.provider; const modelId = context.propsValue.model; + const inputImages = collectInputImages({ + inputImages: context.propsValue.inputImages, + advancedOptions: context.propsValue.advancedOptions, + }); + const image = await getGeneratedImage({ provider: provider as AIProviderName, modelId, engineToken: context.server.token, apiUrl: context.server.apiUrl, prompt: context.propsValue.prompt, + inputImages, projectId: context.project.id, flowId: context.flows.current.id, runId: context.run.id, @@ -171,12 +170,44 @@ export const generateImageAction = createAction({ }, }); +const collectInputImages = ({ + inputImages, + advancedOptions, +}: { + inputImages?: unknown; + advancedOptions?: DynamicPropsValue; +}): ApFile[] => { + const fromTopLevel = extractImageFiles(inputImages); + if (fromTopLevel.length > 0) { + return fromTopLevel; + } + return extractImageFiles(advancedOptions?.['image']); +}; + +const extractImageFiles = (value: unknown): ApFile[] => { + if (!Array.isArray(value)) { + return []; + } + return value.flatMap((entry) => { + if ( + entry && + typeof entry === 'object' && + 'file' in entry && + entry.file + ) { + return [entry.file as ApFile]; + } + return []; + }); +}; + const getGeneratedImage = async ({ provider, modelId, engineToken, apiUrl, prompt, + inputImages, projectId, flowId, runId, @@ -187,6 +218,7 @@ const getGeneratedImage = async ({ engineToken: string; apiUrl: string; prompt: string; + inputImages: ApFile[]; projectId: string; flowId: string; runId: string; @@ -206,49 +238,78 @@ const getGeneratedImage = async ({ const { provider: effectiveProvider } = getEffectiveProviderAndModel({ provider, model: modelId }); const resolvedProvider = (effectiveProvider ?? provider) as AIProviderName; - switch (resolvedProvider) { - case AIProviderName.GOOGLE: - case AIProviderName.ACTIVEPIECES: - case AIProviderName.OPENROUTER: - case AIProviderName.CLOUDFLARE_GATEWAY: - return generateImageUsingGenerateText({ - model: model as unknown as LanguageModel, - prompt, - advancedOptions, - }); - default: { - const { image } = await generateImage({ - model, - prompt, - providerOptions: { - [resolvedProvider]: { ...advancedOptions }, - }, - }); - return image - }; + const hasInputImages = inputImages.length > 0; + + return withImageInputErrorContext({ modelId, hasInputImages }, async () => { + switch (resolvedProvider) { + case AIProviderName.GOOGLE: + case AIProviderName.ACTIVEPIECES: + case AIProviderName.OPENROUTER: + case AIProviderName.CLOUDFLARE_GATEWAY: + return generateImageUsingGenerateText({ + model: model as unknown as LanguageModel, + prompt, + inputImages, + }); + default: { + const sanitizedAdvancedOptions = stripLegacyImageField(advancedOptions); + const sdkImages = inputImages.map((file) => + Buffer.from(file.base64, 'base64'), + ); + const { image } = await generateImage({ + model, + prompt: hasInputImages + ? { text: prompt, images: sdkImages } + : prompt, + providerOptions: { + [resolvedProvider]: { ...sanitizedAdvancedOptions }, + }, + }); + return image; + } + } + }); +}; + +const withImageInputErrorContext = async ( + { modelId, hasInputImages }: { modelId: string; hasInputImages: boolean }, + run: () => Promise, +): Promise => { + try { + return await run(); + } catch (error) { + if (!hasInputImages) { + throw error; + } + const original = error instanceof Error ? error.message : String(error); + throw new Error( + `Image generation failed for model "${modelId}". ` + + `This model may not support input images. Try a model that supports image editing — ` + + `for example gpt-image-1, dall-e-2, or a Gemini Nano Banana model — or remove the input images. ` + + `Original error: ${original}`, + ); } }; const generateImageUsingGenerateText = async ({ model, prompt, - advancedOptions, + inputImages, }: { model: LanguageModel; prompt: string; - advancedOptions?: DynamicPropsValue; + inputImages: ApFile[]; }): Promise => { - const images = - (advancedOptions?.['image'] as Array<{ file: ApFile }> | undefined) ?? []; - - const imageFiles = images.map((image) => { - const fileType = image.file.extension - ? mime.lookup(image.file.extension) - : 'image/jpeg'; + const imageFiles = inputImages.map((file) => { + const detected = file.extension ? mime.lookup(file.extension) : false; + const fileType = + detected && ALLOWED_IMAGE_MIME_TYPES.has(detected) + ? detected + : 'image/jpeg'; return { type: 'image', - image: `data:${fileType || 'image/jpeg'};base64,${image.file.base64}`, + image: `data:${fileType};base64,${file.base64}`, }; }); @@ -271,6 +332,24 @@ const generateImageUsingGenerateText = async ({ return result.files[0]; }; +const stripLegacyImageField = ( + advancedOptions: DynamicPropsValue | undefined, +): DynamicPropsValue | undefined => { + if (isNil(advancedOptions)) { + return advancedOptions; + } + const { image: _legacy, ...rest } = advancedOptions as Record; + return rest as DynamicPropsValue; +}; + +const ALLOWED_IMAGE_MIME_TYPES: ReadonlySet = new Set([ + 'image/jpeg', + 'image/png', + 'image/gif', + 'image/webp', + 'image/avif', +]); + const assertImageGenerationSuccess = ( result: GenerateTextResult ): void => { diff --git a/packages/pieces/community/baserow/package.json b/packages/pieces/community/baserow/package.json index eee1fe5db90..fa77fc0a115 100644 --- a/packages/pieces/community/baserow/package.json +++ b/packages/pieces/community/baserow/package.json @@ -1,6 +1,6 @@ { "name": "@activepieces/piece-baserow", - "version": "0.7.0", + "version": "0.8.0", "main": "./dist/src/index.js", "types": "./dist/src/index.d.ts", "scripts": { diff --git a/packages/pieces/community/baserow/src/i18n/ca.json b/packages/pieces/community/baserow/src/i18n/ca.json index 842eca099a7..bfbfeb419a9 100644 --- a/packages/pieces/community/baserow/src/i18n/ca.json +++ b/packages/pieces/community/baserow/src/i18n/ca.json @@ -3,7 +3,6 @@ "Open-source online database tool, alternative to Airtable": "Open-source online database tool, alternative to Airtable", "API URL": "API URL", "Database Token": "Database Token", - "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.": "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.", "Create Row": "Create Row", "Delete Row": "Delete Row", "Get Row": "Get Row", @@ -111,5 +110,15 @@ "OR": "OR", "Filters": "Filters", "List of filters. Each filter is an object with \"field\" (field ID as number), \"type\" (operator), and \"value\" (filter value).": "List of filters. Each filter is an object with \"field\" (field ID as number), \"type\" (operator), and \"value\" (filter value).", - "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead.": "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead." + "Row Event": "Row Event", + "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.": "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.", + "Create missing select options": "Create missing select options", + "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.": "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.", + "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.": "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.", + "Authentication": "Authentication", + "Authentication Method": "Authentication Method", + "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).": "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).", + "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.": "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.", + "Required if Authentication Method is **Database Token**. Leave empty for JWT.": "Required if Authentication Method is **Database Token**. Leave empty for JWT.", + "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token.": "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token." } diff --git a/packages/pieces/community/baserow/src/i18n/de.json b/packages/pieces/community/baserow/src/i18n/de.json index 7e9a84e426f..43d59960c20 100644 --- a/packages/pieces/community/baserow/src/i18n/de.json +++ b/packages/pieces/community/baserow/src/i18n/de.json @@ -3,10 +3,8 @@ "API URL": "API URL", "Database Token": "Datenbank-Token", "Email & Password (JWT)": "E-Mail & Passwort (JWT)", - "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead.": "Authentifizieren Sie sich mit Ihrer Baserow E-Mail und Ihrem Passwort. Dieser Modus aktiviert automatische Webhook Registrierung für Trigger — keine manuelle Einrichtung erforderlich.\n\n**Hinweis:** Zwei-Faktor-Authentifizierung (2FA) wird nicht unterstützt. Wenn Ihr Baserow-Konto 2FA aktiviert ist, verwenden Sie stattdessen die Datenbank-Token-Authentifizierung.", "Email": "E-Mail", "Password": "Kennwort", - "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.": "\n 1. Melden Sie sich bei Ihrem Baserow Konto an.\n 2. Klicken Sie auf Ihr Profilbild (oben links) und navigieren Sie zu **Einstellungen->Datenbank-Tokens**.\n 3. Neues Token mit jedem Namen und entsprechendem Arbeitsbereich erstellen.\n 4. Nach der Token-Erstellung, klicken Sie auf **:** rechts neben dem Token-Namen und kopieren Sie Datenbank-Token.\n 5. Geben Sie Ihre Baserow API URL ein. Wenn Sie baserow.io verwenden, können Sie die Standard-URL verlassen.", "Create Row": "Zeile erstellen", "Delete Row": "Zeile löschen", "Get Row": "Zeile holen", @@ -82,9 +80,9 @@ "Triggers when a new row is created in a Baserow table.": "Wird ausgelöst, wenn eine neue Zeile in einer Baserow-Tabelle erstellt wird.", "Triggers when an existing row is updated in a Baserow table.": "Wird ausgelöst, wenn ein bestehender Datensatz in einer Baserow-Tabelle aktualisiert wird.", "Triggers when a row is deleted from a Baserow table.": "Wird ausgelöst, wenn ein Datensatz aus einer Baserow-Tabelle gelöscht wird.", - "Rows Created (Batch)": "Zeilen erstellt (Batch)", - "Rows Updated (Batch)": "Zeilen aktualisiert (Batch)", - "Rows Deleted (Batch)": "Zeilen gelöscht (Batch)", + "Rows Created (Batch)": "Zeilen erstellt (Stapel)", + "Rows Updated (Batch)": "Zeilen aktualisiert (Stapel)", + "Rows Deleted (Batch)": "Zeilen gelöscht (Stapel)", "Triggers when new rows are created in a Baserow table. Returns all rows from the event as a single batch.": "Löst aus, wenn neue Datensätze in einer Baserow-Tabelle erstellt werden. Gibt alle Datensätze des Ereignisses als einen Batch zurück.", "Triggers when existing rows are updated in a Baserow table. Returns all rows from the event as a single batch.": "Löst aus, wenn vorhandene Datensätze in einer Baserow-Tabelle aktualisiert werden. Gibt alle Datensätze des Ereignisses als einen Batch zurück.", "Triggers when rows are deleted from a Baserow table. Returns all deleted row IDs from the event as a single batch.": "Löst aus, wenn Datensätze aus einer Baserow-Tabelle gelöscht werden. Gibt alle gelöschten Datensatz-IDs des Ereignisses als einen Stapel zurück.", @@ -105,5 +103,16 @@ "OR": "ODER", "Filters": "Filter", "List of filters. Each filter is an object with \"field\" (field ID as number), \"type\" (operator), and \"value\" (filter value).": "Liste der Filter. Jeder Filter ist ein Objekt mit \"field\" (Feld ID als Nummer), \"type\" (Operator) und \"value\" (Filterwert).", - "Markdown": "Markdown" + "Markdown": "Markdown", + "Row Event": "Zeilenereignis", + "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.": "Wird ausgelöst, wenn eine Zeile in einer Baserow-Tabelle erstellt, aktualisiert oder gelöscht wird. Um nur auf einen Ereignistyp zu reagieren, verwenden Sie die dedizierten Trigger Zeile erstellt, Zeile aktualisiert oder Zeile gelöscht.", + "Create missing select options": "Fehlende Auswahloptionen erstellen", + "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.": "Wenn aktiviert, werden Einfach-/Mehrfachauswahlwerte, die im Feld noch nicht vorhanden sind, vor dem Erstellen der Zeile hinzugefügt. Vorhandene Optionen bleiben erhalten.", + "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.": "Wenn aktiviert, werden Einfach-/Mehrfachauswahlwerte, die im Feld noch nicht vorhanden sind, vor dem Aktualisieren der Zeile hinzugefügt. Vorhandene Optionen bleiben erhalten.", + "Authentication": "Authentication", + "Authentication Method": "Authentication Method", + "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).": "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).", + "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.": "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.", + "Required if Authentication Method is **Database Token**. Leave empty for JWT.": "Required if Authentication Method is **Database Token**. Leave empty for JWT.", + "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token.": "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token." } diff --git a/packages/pieces/community/baserow/src/i18n/es.json b/packages/pieces/community/baserow/src/i18n/es.json index 9a132b63394..2154a7a2806 100644 --- a/packages/pieces/community/baserow/src/i18n/es.json +++ b/packages/pieces/community/baserow/src/i18n/es.json @@ -3,10 +3,8 @@ "API URL": "API URL", "Database Token": "Token de base de datos", "Email & Password (JWT)": "Email y contraseña (JWT)", - "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead.": "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead.", "Email": "E-mail", "Password": "Contraseña", - "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.": "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.", "Create Row": "Crear fila", "Delete Row": "Eliminar fila", "Get Row": "Obtener fila", @@ -82,9 +80,9 @@ "Triggers when a new row is created in a Baserow table.": "Se activa cuando se crea una nueva fila en una tabla Baserow.", "Triggers when an existing row is updated in a Baserow table.": "Se activa cuando se actualiza una fila existente en una tabla Baserow.", "Triggers when a row is deleted from a Baserow table.": "Se activa cuando una fila es borrada de una tabla Baserow.", - "Rows Created (Batch)": "Filas creadas (Batch)", - "Rows Updated (Batch)": "Filas actualizadas (atch)", - "Rows Deleted (Batch)": "Filas eliminadas (atch)", + "Rows Created (Batch)": "Filas creadas (lote)", + "Rows Updated (Batch)": "Filas actualizadas (lote)", + "Rows Deleted (Batch)": "Filas eliminadas (lote)", "Triggers when new rows are created in a Baserow table. Returns all rows from the event as a single batch.": "Dispara cuando se crean nuevas filas en una tabla Baserow. Devuelve todas las filas del evento como un único lote.", "Triggers when existing rows are updated in a Baserow table. Returns all rows from the event as a single batch.": "Dispara cuando los registros existentes se actualizan en una tabla Baserow. Devuelve todas las filas del evento como un único lote.", "Triggers when rows are deleted from a Baserow table. Returns all deleted row IDs from the event as a single batch.": "Dispara cuando las filas son eliminadas de una tabla Baserow. Devuelve todos los identificadores de fila borrados del evento como un único lote.", @@ -105,5 +103,16 @@ "OR": "O", "Filters": "Filtros", "List of filters. Each filter is an object with \"field\" (field ID as number), \"type\" (operator), and \"value\" (filter value).": "Lista de filtros. Cada filtro es un objeto con \"campo\" (ID de campo como número), \"tipo\" (operador), y \"valor\" (valor de filtro).", - "Markdown": "Markdown" + "Markdown": "Markdown", + "Row Event": "Evento de fila", + "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.": "Se activa cuando una fila se crea, actualiza o elimina en una tabla Baserow. Para reaccionar solo a un tipo de evento, utilice los disparadores dedicados Fila creada, Fila actualizada o Fila eliminada.", + "Create missing select options": "Crear opciones de selección que faltan", + "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.": "Cuando está habilitado, los valores de selección única/múltiple que aún no existen en el campo se agregarán antes de crear la fila. Las opciones existentes se conservan.", + "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.": "Cuando está habilitado, los valores de selección única/múltiple que aún no existen en el campo se agregarán antes de actualizar la fila. Las opciones existentes se conservan.", + "Authentication": "Authentication", + "Authentication Method": "Authentication Method", + "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).": "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).", + "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.": "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.", + "Required if Authentication Method is **Database Token**. Leave empty for JWT.": "Required if Authentication Method is **Database Token**. Leave empty for JWT.", + "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token.": "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token." } diff --git a/packages/pieces/community/baserow/src/i18n/fr.json b/packages/pieces/community/baserow/src/i18n/fr.json index 9e6d289d3ed..a62c09befe6 100644 --- a/packages/pieces/community/baserow/src/i18n/fr.json +++ b/packages/pieces/community/baserow/src/i18n/fr.json @@ -3,10 +3,8 @@ "API URL": "API URL", "Database Token": "Jeton de la base de données", "Email & Password (JWT)": "E-mail et mot de passe (JWT)", - "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead.": "Authentifiez-vous avec votre email Baserow et votre mot de passe. Ce mode active l'enregistrement automatique du webhook pour les déclencheurs - pas besoin de configuration manuelle.\n\n**Note:** L'authentification à deux facteurs (2FA) n'est pas prise en charge. Si votre compte Baserow a 2FA activé, utilisez l'authentification de la base de données à la place.", "Email": "Courriel", - "Password": "Password", - "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.": "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.", + "Password": "Mot de passe", "Create Row": "Créer une ligne", "Delete Row": "Supprimer la ligne", "Get Row": "Obtenir la ligne", @@ -25,6 +23,9 @@ "Clears fields in a row. Empty values will clear the corresponding fields.": "Efface les champs dans une ligne. Les valeurs vides effaceront les champs correspondants.", "Calculates an aggregation (sum, average, min, max, count, etc.) over all values of a field in a table.": "Calcule une agrégation (somme, moyenne, min, max, compteur, etc.) sur toutes les valeurs d'un champ dans un tableau.", "Make a custom API call to a specific endpoint": "Passez un appel API personnalisé à un point de terminaison spécifique", + "Create missing select options": "Créer les options de sélection manquantes", + "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.": "Lorsque activé, les valeurs de sélection unique/multiple qui n'existent pas encore dans le champ seront ajoutées avant la création de la ligne. Les options existantes sont préservées.", + "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.": "Lorsque activé, les valeurs de sélection unique/multiple qui n'existent pas encore dans le champ seront ajoutées avant la mise à jour de la ligne. Les options existantes sont préservées.", "Table": "Tableau", "Table Fields": "Champs de la table", "Row": "Ligne", @@ -79,17 +80,19 @@ "Row Created": "Ligne créée", "Row Updated": "Ligne mise à jour", "Row Deleted": "Ligne supprimée", + "Row Event": "Événement sur une ligne", "Triggers when a new row is created in a Baserow table.": "Déclenche quand une nouvelle ligne est créée dans une table Baserow.", "Triggers when an existing row is updated in a Baserow table.": "Déclenche lorsqu'une ligne existante est mise à jour dans une table Baserow.", "Triggers when a row is deleted from a Baserow table.": "Déclenche lorsqu'une ligne est supprimée d'une table Baserow.", - "Rows Created (Batch)": "Lignes créées (Batch)", - "Rows Updated (Batch)": "Lignes mises à jour (Batch)", - "Rows Deleted (Batch)": "Lignes supprimées (Batch)", + "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.": "Déclenche lorsqu'une ligne est créée, mise à jour ou supprimée dans une table Baserow. Pour ne réagir qu'à un seul type d'événement, utilisez les déclencheurs dédiés Ligne créée, Ligne mise à jour ou Ligne supprimée.", + "Rows Created (Batch)": "Lignes créées par lot", + "Rows Updated (Batch)": "Lignes mises à jour par lot", + "Rows Deleted (Batch)": "Lignes supprimées par lot", "Triggers when new rows are created in a Baserow table. Returns all rows from the event as a single batch.": "Déclenche lorsque de nouvelles lignes sont créées dans une table Baserow. Renvoie toutes les lignes de l'événement en un seul lot.", "Triggers when existing rows are updated in a Baserow table. Returns all rows from the event as a single batch.": "Déclenche lorsque les lignes existantes sont mises à jour dans une table Baserow. Renvoie toutes les lignes de l'événement en un seul lot.", "Triggers when rows are deleted from a Baserow table. Returns all deleted row IDs from the event as a single batch.": "Déclenche lorsque les lignes sont supprimées d'une table Baserow. Renvoie tous les identifiants de ligne supprimés de l'événement en un seul lot.", "Batch Create Rows": "Créer des lignes par lot", - "Batch Update Rows": "Lignes de mise à jour par lot", + "Batch Update Rows": "Mettre à jour des lignes par lot", "Batch Delete Rows": "Supprimer des lignes par lot", "Creates multiple rows in a single request. Accepts up to 200 rows.": "Crée plusieurs lignes dans une seule requête. Accepte jusqu'à 200 lignes.", "Updates multiple rows in a single request. Each row must include an \"id\" field. Accepts up to 200 rows.": "Met à jour plusieurs lignes dans une seule requête. Chaque ligne doit inclure un champ \"id\". Accepte jusqu'à 200 lignes.", @@ -105,5 +108,11 @@ "OR": "OU", "Filters": "Filtres", "List of filters. Each filter is an object with \"field\" (field ID as number), \"type\" (operator), and \"value\" (filter value).": "Liste des filtres. Chaque filtre est un objet avec \"field\" (ID du champ comme nombre), \"type\" (opérateur) et \"value\" (valeur du filtre).", - "Markdown": "Markdown" + "Markdown": "Markdown", + "Authentication": "Authentification", + "Authentication Method": "Méthode d'authentification", + "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).": "Choisissez comment vous authentifier auprès de Baserow :\n\n**Jeton de base de données** — recommandé. Permissions CRUD par table, compatible avec les comptes 2FA. Les déclencheurs nécessitent une configuration manuelle des webhooks.\n 1. Connectez-vous à votre compte Baserow.\n 2. Cliquez sur votre photo de profil (en haut à gauche) et allez dans **Paramètres → Jetons de base de données**.\n 3. Créez un nouveau jeton, puis cliquez sur **:** à côté de son nom pour le copier.\n 4. Collez-le dans **Jeton de base de données** ci-dessous. Laissez **Courriel** et **Mot de passe** vides.\n\n**E-mail et mot de passe (JWT)** — accès au workspace entier, active l'enregistrement automatique des webhooks pour les déclencheurs. Incompatible avec les comptes ayant la 2FA activée.\n 1. Remplissez **Courriel** et **Mot de passe** avec vos identifiants Baserow. Laissez **Jeton de base de données** vide.\n\nDans les deux modes, renseignez **API URL** avec l'URL de votre instance Baserow (par défaut : `https://api.baserow.io`).", + "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.": "Le jeton de base de données est recommandé. Utilisez E-mail et mot de passe (JWT) uniquement si vous avez besoin de l'enregistrement automatique des webhooks pour les déclencheurs.", + "Required if Authentication Method is **Database Token**. Leave empty for JWT.": "Requis si la méthode d'authentification est **Jeton de base de données**. Laissez vide pour JWT.", + "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token.": "Requis si la méthode d'authentification est **E-mail et mot de passe (JWT)**. Laissez vide pour Jeton de base de données." } diff --git a/packages/pieces/community/baserow/src/i18n/hi.json b/packages/pieces/community/baserow/src/i18n/hi.json index 842eca099a7..bfbfeb419a9 100644 --- a/packages/pieces/community/baserow/src/i18n/hi.json +++ b/packages/pieces/community/baserow/src/i18n/hi.json @@ -3,7 +3,6 @@ "Open-source online database tool, alternative to Airtable": "Open-source online database tool, alternative to Airtable", "API URL": "API URL", "Database Token": "Database Token", - "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.": "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.", "Create Row": "Create Row", "Delete Row": "Delete Row", "Get Row": "Get Row", @@ -111,5 +110,15 @@ "OR": "OR", "Filters": "Filters", "List of filters. Each filter is an object with \"field\" (field ID as number), \"type\" (operator), and \"value\" (filter value).": "List of filters. Each filter is an object with \"field\" (field ID as number), \"type\" (operator), and \"value\" (filter value).", - "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead.": "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead." + "Row Event": "Row Event", + "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.": "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.", + "Create missing select options": "Create missing select options", + "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.": "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.", + "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.": "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.", + "Authentication": "Authentication", + "Authentication Method": "Authentication Method", + "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).": "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).", + "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.": "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.", + "Required if Authentication Method is **Database Token**. Leave empty for JWT.": "Required if Authentication Method is **Database Token**. Leave empty for JWT.", + "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token.": "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token." } diff --git a/packages/pieces/community/baserow/src/i18n/id.json b/packages/pieces/community/baserow/src/i18n/id.json index 842eca099a7..bfbfeb419a9 100644 --- a/packages/pieces/community/baserow/src/i18n/id.json +++ b/packages/pieces/community/baserow/src/i18n/id.json @@ -3,7 +3,6 @@ "Open-source online database tool, alternative to Airtable": "Open-source online database tool, alternative to Airtable", "API URL": "API URL", "Database Token": "Database Token", - "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.": "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.", "Create Row": "Create Row", "Delete Row": "Delete Row", "Get Row": "Get Row", @@ -111,5 +110,15 @@ "OR": "OR", "Filters": "Filters", "List of filters. Each filter is an object with \"field\" (field ID as number), \"type\" (operator), and \"value\" (filter value).": "List of filters. Each filter is an object with \"field\" (field ID as number), \"type\" (operator), and \"value\" (filter value).", - "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead.": "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead." + "Row Event": "Row Event", + "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.": "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.", + "Create missing select options": "Create missing select options", + "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.": "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.", + "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.": "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.", + "Authentication": "Authentication", + "Authentication Method": "Authentication Method", + "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).": "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).", + "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.": "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.", + "Required if Authentication Method is **Database Token**. Leave empty for JWT.": "Required if Authentication Method is **Database Token**. Leave empty for JWT.", + "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token.": "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token." } diff --git a/packages/pieces/community/baserow/src/i18n/ja.json b/packages/pieces/community/baserow/src/i18n/ja.json index 2e383be7a41..69c52fc323c 100644 --- a/packages/pieces/community/baserow/src/i18n/ja.json +++ b/packages/pieces/community/baserow/src/i18n/ja.json @@ -3,10 +3,8 @@ "API URL": "API URL", "Database Token": "データベーストークン", "Email & Password (JWT)": "メール&パスワード (JWT)", - "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead.": "Baselow メールアドレスとパスワードで認証します。このモードでは、トリガーの自動Webhook登録が可能になります。手動での設定は不要です。\n\n**注意:** 2要素認証(2FA)はサポートされていません。Baserowアカウントが2FAを有効にしている場合は、代わりにデータベーストークン認証を使用してください。", "Email": "Eメールアドレス", "Password": "Password", - "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.": "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.", "Create Row": "行を作成", "Delete Row": "行を削除", "Get Row": "行を取得", @@ -82,9 +80,9 @@ "Triggers when a new row is created in a Baserow table.": "Baserow テーブルに新しい行が作成されたときにトリガーします。", "Triggers when an existing row is updated in a Baserow table.": "Baserow テーブルで既存の行が更新されたときにトリガーされます。", "Triggers when a row is deleted from a Baserow table.": "Baserow テーブルから行が削除されたときにトリガーします。", - "Rows Created (Batch)": "行が作成されました (Batch)", - "Rows Updated (Batch)": "行が更新されました(Batch)", - "Rows Deleted (Batch)": "行を削除しました(Batch)", + "Rows Created (Batch)": "行が作成されました (一括)", + "Rows Updated (Batch)": "行が更新されました (一括)", + "Rows Deleted (Batch)": "行を削除しました (一括)", "Triggers when new rows are created in a Baserow table. Returns all rows from the event as a single batch.": "Baserow テーブルに新しい行が作成されたときにトリガーされます。イベントからすべての行を単一のバッチで返します。", "Triggers when existing rows are updated in a Baserow table. Returns all rows from the event as a single batch.": "Baserow テーブルで既存の行が更新されたときにトリガーされます。イベントからすべての行を単一のバッチで返します。", "Triggers when rows are deleted from a Baserow table. Returns all deleted row IDs from the event as a single batch.": "Baserow テーブルから行が削除されたときにトリガーされます。イベントから削除されたすべての行 ID を単一のバッチとして返します。", @@ -105,5 +103,16 @@ "OR": "OR", "Filters": "絞り込み", "List of filters. Each filter is an object with \"field\" (field ID as number), \"type\" (operator), and \"value\" (filter value).": "フィルタの一覧です。各フィルタは、\"フィールド\" (数値としてのフィールド ID )、\"タイプ\" (演算子)、\"値\" (フィルタ値)を持つオブジェクトです。", - "Markdown": "Markdown" + "Markdown": "Markdown", + "Row Event": "行イベント", + "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.": "Baserow テーブルで行が作成、更新、または削除されたときにトリガーされます。1つのイベントタイプのみに反応するには、専用の「行の作成」、「行の更新」、または「行を削除しました」トリガーを使用してください。", + "Create missing select options": "不足している選択オプションを作成", + "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.": "有効にすると、フィールドにまだ存在しない単一/複数選択の値が、行の作成前に追加されます。既存のオプションは保持されます。", + "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.": "有効にすると、フィールドにまだ存在しない単一/複数選択の値が、行の更新前に追加されます。既存のオプションは保持されます。", + "Authentication": "Authentication", + "Authentication Method": "Authentication Method", + "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).": "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).", + "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.": "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.", + "Required if Authentication Method is **Database Token**. Leave empty for JWT.": "Required if Authentication Method is **Database Token**. Leave empty for JWT.", + "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token.": "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token." } diff --git a/packages/pieces/community/baserow/src/i18n/nl.json b/packages/pieces/community/baserow/src/i18n/nl.json index 6c9e1bde330..7f884690968 100644 --- a/packages/pieces/community/baserow/src/i18n/nl.json +++ b/packages/pieces/community/baserow/src/i18n/nl.json @@ -3,10 +3,8 @@ "API URL": "API URL", "Database Token": "Database token", "Email & Password (JWT)": "E-mail & wachtwoord (JWT)", - "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead.": "Authenticeer met uw Baserow e-mail en wachtwoord. Deze modus activeert automatische webhook registratie voor triggers - geen handmatige installatie nodig.\n\n**Opmerking:** Tweestapsverificatie (2FA) wordt niet ondersteund. Als uw Baserow account 2FA heeft ingeschakeld, gebruik dan in plaats daarvan de Database Token verificatie.", "Email": "E-mail", "Password": "Wachtwoord", - "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.": "\n 1. Log in op uw Baserow account.\n 2. Klik op uw profiel-picle-(linksboven) en navigeer naar **Instellingen->Database token**.\n 3. Maak een nieuw token aan met een willekeurige naam en werkruimte.\n 4. Na het aanmaken van token klikt u op **:** rechts naast de token naam en kopieer de database token.\n 5. Voer uw Baserow API URL in. Als u baserow.io gebruikt, kunt u de standaard URL verlaten.", "Create Row": "Rij maken", "Delete Row": "Verwijder rij", "Get Row": "Verkrijg rij", @@ -105,5 +103,16 @@ "OR": "OF", "Filters": "Filters", "List of filters. Each filter is an object with \"field\" (field ID as number), \"type\" (operator), and \"value\" (filter value).": "Lijst van filters. Elk filter is een object met \"veld\" (veldnummer als nummer), \"type\" (operator) en \"waarde\" (filter waarde).", - "Markdown": "Markdown" + "Markdown": "Markdown", + "Row Event": "Rij gebeurtenis", + "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.": "Wordt uitgevoerd wanneer een rij wordt aangemaakt, bijgewerkt of verwijderd in een Baserow tabel. Om alleen op één type gebeurtenis te reageren, gebruikt u de specifieke triggers Rij aangemaakt, Rij bijgewerkt of Rij verwijderd.", + "Create missing select options": "Ontbrekende selectie-opties aanmaken", + "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.": "Wanneer ingeschakeld, worden enkelvoudige/meervoudige selectiewaarden die nog niet in het veld bestaan, toegevoegd voordat de rij wordt aangemaakt. Bestaande opties blijven behouden.", + "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.": "Wanneer ingeschakeld, worden enkelvoudige/meervoudige selectiewaarden die nog niet in het veld bestaan, toegevoegd voordat de rij wordt bijgewerkt. Bestaande opties blijven behouden.", + "Authentication": "Authentication", + "Authentication Method": "Authentication Method", + "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).": "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).", + "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.": "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.", + "Required if Authentication Method is **Database Token**. Leave empty for JWT.": "Required if Authentication Method is **Database Token**. Leave empty for JWT.", + "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token.": "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token." } diff --git a/packages/pieces/community/baserow/src/i18n/pt.json b/packages/pieces/community/baserow/src/i18n/pt.json index 62bb7c70032..856ff382f3c 100644 --- a/packages/pieces/community/baserow/src/i18n/pt.json +++ b/packages/pieces/community/baserow/src/i18n/pt.json @@ -3,10 +3,8 @@ "API URL": "API URL", "Database Token": "Token do banco", "Email & Password (JWT)": "E-mail e Senha (JWT)", - "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead.": "Autenticar com seu e-mail e senha baserov. Este modo permite o registro automático do webhook para gatilhos — nenhuma configuração manual necessária.\n\n**Nota:** A autenticação de dois fatores (2FA) não é suportada. Se sua conta Baserow tiver o 2FA ativado, use a autenticação de token de banco de dados em vez disso.", "Email": "e-mail", "Password": "Senha", - "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.": "\n 1. Acesse sua conta Baserow.\n 2. Clique em seu perfil-pic(top-left) e navegue para **Settings->Database tokens**.\n 3. Crie um novo token com qualquer nome e espaço de trabalho apropriado.\n 4. Após a criação do token, clique em **:** direito ao nome do token e copiar token do banco de dados.\n 5. Digite seu URL da API baserow . Se você estiver usando baserow.io, você pode deixar o padrão.", "Create Row": "Criar Linha", "Delete Row": "Excluir linha", "Get Row": "Obter Linha", @@ -105,5 +103,16 @@ "OR": "OU", "Filters": "Filtros", "List of filters. Each filter is an object with \"field\" (field ID as number), \"type\" (operator), and \"value\" (filter value).": "Lista de filtros. Cada filtro é um objeto com \"campo\" (ID do campo como número), \"tipo\" (operador) e \"valor\" (valor do filtro).", - "Markdown": "Markdown" + "Markdown": "Markdown", + "Row Event": "Evento de linha", + "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.": "Aciona quando uma linha é criada, atualizada ou excluída em uma tabela Baserow. Para reagir apenas a um tipo de evento, use os gatilhos dedicados Linha Criada, Linha Atualizada ou Linha Excluída.", + "Create missing select options": "Criar opções de seleção ausentes", + "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.": "Quando ativado, os valores de seleção única/múltipla que ainda não existem no campo serão adicionados antes de criar a linha. As opções existentes são preservadas.", + "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.": "Quando ativado, os valores de seleção única/múltipla que ainda não existem no campo serão adicionados antes de atualizar a linha. As opções existentes são preservadas.", + "Authentication": "Authentication", + "Authentication Method": "Authentication Method", + "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).": "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).", + "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.": "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.", + "Required if Authentication Method is **Database Token**. Leave empty for JWT.": "Required if Authentication Method is **Database Token**. Leave empty for JWT.", + "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token.": "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token." } diff --git a/packages/pieces/community/baserow/src/i18n/ru.json b/packages/pieces/community/baserow/src/i18n/ru.json index 5198c173826..d868e1dc845 100644 --- a/packages/pieces/community/baserow/src/i18n/ru.json +++ b/packages/pieces/community/baserow/src/i18n/ru.json @@ -3,7 +3,6 @@ "Open-source online database tool, alternative to Airtable": "Инструмент с открытым исходным кодом для онлайн баз данных, альтернатива Airtable", "API URL": "API URL", "Database Token": "Токен базы данных", - "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.": "\n 1. Войдите в учетную запись Базерова.\n 2. Нажмите на изображение сверху слева и перейдите в **Настройки->Токен базы данных**.\n 3. Создайте новый токен с любым именем и подходящей рабочей областью.\n 4. После создания токена, нажмите на **:** прямо рядом с именем токена и скопируйте токен базы данных.\n 5. Введите ваш Baserow API URL. Если вы используете baserow.io, вы можете оставить его по умолчанию.", "Create Row": "Создать строку", "Delete Row": "Удалить строку", "Get Row": "Получить строку", @@ -111,5 +110,15 @@ "OR": "OR", "Filters": "Filters", "List of filters. Each filter is an object with \"field\" (field ID as number), \"type\" (operator), and \"value\" (filter value).": "List of filters. Each filter is an object with \"field\" (field ID as number), \"type\" (operator), and \"value\" (filter value).", - "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead.": "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead." + "Row Event": "Row Event", + "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.": "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.", + "Create missing select options": "Create missing select options", + "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.": "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.", + "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.": "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.", + "Authentication": "Authentication", + "Authentication Method": "Authentication Method", + "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).": "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).", + "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.": "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.", + "Required if Authentication Method is **Database Token**. Leave empty for JWT.": "Required if Authentication Method is **Database Token**. Leave empty for JWT.", + "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token.": "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token." } diff --git a/packages/pieces/community/baserow/src/i18n/translation.json b/packages/pieces/community/baserow/src/i18n/translation.json index ada5fcffff3..15971468de7 100644 --- a/packages/pieces/community/baserow/src/i18n/translation.json +++ b/packages/pieces/community/baserow/src/i18n/translation.json @@ -2,7 +2,9 @@ "Open-source online database tool, alternative to Airtable": "Open-source online database tool, alternative to Airtable", "API URL": "API URL", "Database Token": "Database Token", - "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.": "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.", + "Email & Password (JWT)": "Email & Password (JWT)", + "Email": "Email", + "Password": "Password", "Create Row": "Create Row", "Delete Row": "Delete Row", "Get Row": "Get Row", @@ -27,6 +29,9 @@ "Updates multiple rows in a single request. Each row must include an \"id\" field. Accepts up to 200 rows.": "Updates multiple rows in a single request. Each row must include an \"id\" field. Accepts up to 200 rows.", "Deletes multiple rows in a single request. Accepts up to 200 row IDs.": "Deletes multiple rows in a single request. Accepts up to 200 row IDs.", "Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint", + "Create missing select options": "Create missing select options", + "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.": "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.", + "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.": "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.", "Table": "Table", "Table Fields": "Table Fields", "Row": "Row", @@ -94,17 +99,29 @@ "Row Created": "Row Created", "Row Updated": "Row Updated", "Row Deleted": "Row Deleted", + "Row Event": "Row Event", "Rows Created (Batch)": "Rows Created (Batch)", "Rows Updated (Batch)": "Rows Updated (Batch)", "Rows Deleted (Batch)": "Rows Deleted (Batch)", "Triggers when a new row is created in a Baserow table.": "Triggers when a new row is created in a Baserow table.", "Triggers when an existing row is updated in a Baserow table.": "Triggers when an existing row is updated in a Baserow table.", "Triggers when a row is deleted from a Baserow table.": "Triggers when a row is deleted from a Baserow table.", + "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.": "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.", "Triggers when new rows are created in a Baserow table. Returns all rows from the event as a single batch.": "Triggers when new rows are created in a Baserow table. Returns all rows from the event as a single batch.", "Triggers when existing rows are updated in a Baserow table. Returns all rows from the event as a single batch.": "Triggers when existing rows are updated in a Baserow table. Returns all rows from the event as a single batch.", "Triggers when rows are deleted from a Baserow table. Returns all deleted row IDs from the event as a single batch.": "Triggers when rows are deleted from a Baserow table. Returns all deleted row IDs from the event as a single batch.", +<<<<<<< feat/baserow-jwt-restore + "Authentication": "Authentication", + "Authentication Method": "Authentication Method", + "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).": "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).", + "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.": "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.", + "Required if Authentication Method is **Database Token**. Leave empty for JWT.": "Required if Authentication Method is **Database Token**. Leave empty for JWT.", + "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token.": "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token." +} +======= "Markdown": "Markdown", "\n## Setup Instructions\n\n1. In Baserow, click the **···** menu beside your table and select **Webhooks**.\n2. Click **Create webhook +**.\n3. Set the HTTP method to **POST**.\n4. Paste the following URL into the endpoint field:\n```text\n{{webhookUrl}}\n```\n5. Under events, select **Rows created**.\n6. Click **Save**.\n": "\n## Setup Instructions\n\n1. In Baserow, click the **···** menu beside your table and select **Webhooks**.\n2. Click **Create webhook +**.\n3. Set the HTTP method to **POST**.\n4. Paste the following URL into the endpoint field:\n```text\n{{webhookUrl}}\n```\n5. Under events, select **Rows created**.\n6. Click **Save**.\n", "\n## Setup Instructions\n\n1. In Baserow, click the **···** menu beside your table and select **Webhooks**.\n2. Click **Create webhook +**.\n3. Set the HTTP method to **POST**.\n4. Paste the following URL into the endpoint field:\n```text\n{{webhookUrl}}\n```\n5. Under events, select **Rows updated**.\n6. Click **Save**.\n": "\n## Setup Instructions\n\n1. In Baserow, click the **···** menu beside your table and select **Webhooks**.\n2. Click **Create webhook +**.\n3. Set the HTTP method to **POST**.\n4. Paste the following URL into the endpoint field:\n```text\n{{webhookUrl}}\n```\n5. Under events, select **Rows updated**.\n6. Click **Save**.\n", "\n## Setup Instructions\n\n1. In Baserow, click the **···** menu beside your table and select **Webhooks**.\n2. Click **Create webhook +**.\n3. Set the HTTP method to **POST**.\n4. Paste the following URL into the endpoint field:\n```text\n{{webhookUrl}}\n```\n5. Under events, select **Rows deleted**.\n6. Click **Save**.\n": "\n## Setup Instructions\n\n1. In Baserow, click the **···** menu beside your table and select **Webhooks**.\n2. Click **Create webhook +**.\n3. Set the HTTP method to **POST**.\n4. Paste the following URL into the endpoint field:\n```text\n{{webhookUrl}}\n```\n5. Under events, select **Rows deleted**.\n6. Click **Save**.\n" -} \ No newline at end of file +} +>>>>>>> main diff --git a/packages/pieces/community/baserow/src/i18n/vi.json b/packages/pieces/community/baserow/src/i18n/vi.json index 842eca099a7..bfbfeb419a9 100644 --- a/packages/pieces/community/baserow/src/i18n/vi.json +++ b/packages/pieces/community/baserow/src/i18n/vi.json @@ -3,7 +3,6 @@ "Open-source online database tool, alternative to Airtable": "Open-source online database tool, alternative to Airtable", "API URL": "API URL", "Database Token": "Database Token", - "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.": "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.", "Create Row": "Create Row", "Delete Row": "Delete Row", "Get Row": "Get Row", @@ -111,5 +110,15 @@ "OR": "OR", "Filters": "Filters", "List of filters. Each filter is an object with \"field\" (field ID as number), \"type\" (operator), and \"value\" (filter value).": "List of filters. Each filter is an object with \"field\" (field ID as number), \"type\" (operator), and \"value\" (filter value).", - "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead.": "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead." + "Row Event": "Row Event", + "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.": "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.", + "Create missing select options": "Create missing select options", + "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.": "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.", + "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.": "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.", + "Authentication": "Authentication", + "Authentication Method": "Authentication Method", + "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).": "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).", + "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.": "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.", + "Required if Authentication Method is **Database Token**. Leave empty for JWT.": "Required if Authentication Method is **Database Token**. Leave empty for JWT.", + "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token.": "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token." } diff --git a/packages/pieces/community/baserow/src/i18n/zh.json b/packages/pieces/community/baserow/src/i18n/zh.json index 504993b2c0a..0c6d4e39baf 100644 --- a/packages/pieces/community/baserow/src/i18n/zh.json +++ b/packages/pieces/community/baserow/src/i18n/zh.json @@ -3,10 +3,8 @@ "API URL": "API URL", "Database Token": "Database Token", "Email & Password (JWT)": "Email & Password (JWT)", - "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead.": "Authenticate with your Baserow email and password. This mode enables automatic webhook registration for triggers — no manual setup needed.\n\n**Note:** Two-factor authentication (2FA) is not supported. If your Baserow account has 2FA enabled, use the Database Token authentication instead.", "Email": "电子邮件地址", "Password": "Password", - "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.": "\n 1. Log in to your Baserow Account.\n 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**.\n 3. Create new token with any name and appropriate workspace.\n 4. After token creation,click on **:** right beside token name and copy database token.\n 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.", "Create Row": "Create Row", "Delete Row": "删除行", "Get Row": "获取行", @@ -105,5 +103,16 @@ "OR": "或", "Filters": "篩選條件", "List of filters. Each filter is an object with \"field\" (field ID as number), \"type\" (operator), and \"value\" (filter value).": "List of filters. Each filter is an object with \"field\" (field ID as number), \"type\" (operator), and \"value\" (filter value).", - "Markdown": "Markdown" + "Markdown": "Markdown", + "Row Event": "Row Event", + "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.": "Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.", + "Create missing select options": "Create missing select options", + "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.": "When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.", + "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.": "When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.", + "Authentication": "Authentication", + "Authentication Method": "Authentication Method", + "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).": "Choose how you want to authenticate with Baserow:\n\n**Database Token** — recommended. Per-table CRUD scoping, compatible with 2FA accounts. Triggers require manual webhook setup.\n 1. Log in to your Baserow account.\n 2. Click on your profile picture (top-left) and go to **Settings → Database tokens**.\n 3. Create a new token, then click **:** beside the token name to copy it.\n 4. Paste it into **Database Token** below. Leave **Email** and **Password** empty.\n\n**Email & Password (JWT)** — workspace-wide access, enables automatic webhook registration for triggers. Not compatible with accounts that have 2FA enabled.\n 1. Fill in **Email** and **Password** with your Baserow login credentials. Leave **Database Token** empty.\n\nIn both modes, set **API URL** to your Baserow instance (default: `https://api.baserow.io`).", + "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.": "Database Token is recommended. Use Email & Password (JWT) only if you need automatic webhook registration on triggers.", + "Required if Authentication Method is **Database Token**. Leave empty for JWT.": "Required if Authentication Method is **Database Token**. Leave empty for JWT.", + "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token.": "Required if Authentication Method is **Email & Password (JWT)**. Leave empty for Database Token." } diff --git a/packages/pieces/community/baserow/src/index.ts b/packages/pieces/community/baserow/src/index.ts index 558aca2b72b..0e195c29af5 100644 --- a/packages/pieces/community/baserow/src/index.ts +++ b/packages/pieces/community/baserow/src/index.ts @@ -20,7 +20,24 @@ import { rowDeletedTrigger } from './lib/triggers/row-deleted'; import { rowsCreatedTrigger } from './lib/triggers/rows-created'; import { rowsUpdatedTrigger } from './lib/triggers/rows-updated'; import { rowsDeletedTrigger } from './lib/triggers/rows-deleted'; -import { baserowAuth } from './lib/auth'; +import { rowEventTrigger } from './lib/triggers/row-event'; +import { baserowAuth, baserowAuthHelpers, BaserowAuthValue } from './lib/auth'; +import { BaserowClient } from './lib/common/client'; + +async function buildCustomApiAuthHeader(auth: BaserowAuthValue): Promise<{ Authorization: string }> { + const { apiUrl, token, email, password } = auth.props; + if (baserowAuthHelpers.isJwtAuth(auth)) { + if (!email || !password) { + throw new Error('Email and Password are required for JWT authentication.'); + } + const jwt = await BaserowClient.getJwtToken({ apiUrl, email, password }); + return { Authorization: `JWT ${jwt}` }; + } + if (!token) { + throw new Error('Database Token is required for Database Token authentication.'); + } + return { Authorization: `Token ${token}` }; +} export const baserow = createPiece({ displayName: 'Baserow', @@ -29,7 +46,7 @@ export const baserow = createPiece({ minimumSupportedRelease: '0.30.0', logoUrl: 'https://cdn.activepieces.com/pieces/baserow.png', categories: [PieceCategory.PRODUCTIVITY], - authors: ["kishanprmr", "MoShizzle", "abuaboud", 'bst1n', 'sanket-a11y'], + authors: ["kishanprmr", "MoShizzle", "abuaboud", 'bst1n', 'sanket-a11y', 'onyedikachi-david'], actions: [ createRowAction, deleteRowAction, @@ -50,15 +67,14 @@ export const baserow = createPiece({ return auth.props.apiUrl; }, auth: baserowAuth, - authMapping: async (auth) => { - return { Authorization: `Token ${auth.props.token}` }; - }, + authMapping: buildCustomApiAuthHeader, }), ], triggers: [ rowCreatedTrigger, rowUpdatedTrigger, rowDeletedTrigger, + rowEventTrigger, rowsCreatedTrigger, rowsUpdatedTrigger, rowsDeletedTrigger, diff --git a/packages/pieces/community/baserow/src/lib/actions/aggregate-field.ts b/packages/pieces/community/baserow/src/lib/actions/aggregate-field.ts index b8806bc249a..4fdadba666b 100644 --- a/packages/pieces/community/baserow/src/lib/actions/aggregate-field.ts +++ b/packages/pieces/community/baserow/src/lib/actions/aggregate-field.ts @@ -61,7 +61,7 @@ export const aggregateFieldAction = createAction({ aggregation_type: Property.StaticDropdown({ displayName: 'Aggregation Type', description: - 'Sum, average, min, max, std_dev and variance only work on number fields.', + 'The calculation to run over the field. **Sum, Average, Min, Max, Median, Std Dev, and Variance** only work with number fields.', required: true, options: { disabled: false, diff --git a/packages/pieces/community/baserow/src/lib/actions/batch-create-rows.ts b/packages/pieces/community/baserow/src/lib/actions/batch-create-rows.ts index e6a19a2c7af..3c21d6422c6 100644 --- a/packages/pieces/community/baserow/src/lib/actions/batch-create-rows.ts +++ b/packages/pieces/community/baserow/src/lib/actions/batch-create-rows.ts @@ -25,9 +25,10 @@ export const batchCreateRowsAction = createAction({ throw new Error('Rows must be a JSON array.'); } const client = await makeClient(context.auth); - return await client.batchCreateRows( + const response = (await client.batchCreateRows( table_id, rows as Record[] - ); + )) as { items: Record[] }; + return { count: response.items.length, rows: response.items }; }, }); diff --git a/packages/pieces/community/baserow/src/lib/actions/batch-delete-rows.ts b/packages/pieces/community/baserow/src/lib/actions/batch-delete-rows.ts index 8c84102793c..0eb5c41c257 100644 --- a/packages/pieces/community/baserow/src/lib/actions/batch-delete-rows.ts +++ b/packages/pieces/community/baserow/src/lib/actions/batch-delete-rows.ts @@ -12,7 +12,7 @@ export const batchDeleteRowsAction = createAction({ table_id: baserowCommon.tableId(), row_ids: Property.Array({ displayName: 'Row IDs', - description: 'List of row IDs to delete.', + description: 'Numeric IDs of the rows to delete. You can get row IDs from the List Rows or Find Row actions.', required: true, }), }, @@ -26,6 +26,7 @@ export const batchDeleteRowsAction = createAction({ .map((id) => parseInt(String(id), 10)) .filter((id) => !isNaN(id)); const client = await makeClient(context.auth); - return await client.batchDeleteRows(table_id, ids); + await client.batchDeleteRows(table_id, ids); + return { deleted_count: ids.length }; }, }); diff --git a/packages/pieces/community/baserow/src/lib/actions/batch-update-rows.ts b/packages/pieces/community/baserow/src/lib/actions/batch-update-rows.ts index bd1ef4497c0..d5acdf82c4e 100644 --- a/packages/pieces/community/baserow/src/lib/actions/batch-update-rows.ts +++ b/packages/pieces/community/baserow/src/lib/actions/batch-update-rows.ts @@ -25,9 +25,10 @@ export const batchUpdateRowsAction = createAction({ throw new Error('Rows must be a JSON array.'); } const client = await makeClient(context.auth); - return await client.batchUpdateRows( + const response = (await client.batchUpdateRows( table_id, rows as Record[] - ); + )) as { items: Record[] }; + return { count: response.items.length, rows: response.items }; }, }); diff --git a/packages/pieces/community/baserow/src/lib/actions/clean-row.ts b/packages/pieces/community/baserow/src/lib/actions/clean-row.ts index 07bda771e2b..c300b699fb9 100644 --- a/packages/pieces/community/baserow/src/lib/actions/clean-row.ts +++ b/packages/pieces/community/baserow/src/lib/actions/clean-row.ts @@ -6,7 +6,7 @@ export const cleanRowAction = createAction({ name: 'baserow_clean_row', displayName: 'Clean Row', description: - 'Clears fields in a row. Empty values will clear the corresponding fields.', + 'Sets all fields in a row to empty/null. To update only specific fields, use Update Row instead.', auth: baserowAuth, props: { table_id: baserowCommon.tableId(), diff --git a/packages/pieces/community/baserow/src/lib/actions/create-row.ts b/packages/pieces/community/baserow/src/lib/actions/create-row.ts index f94f68a22dd..b4a342243f6 100644 --- a/packages/pieces/community/baserow/src/lib/actions/create-row.ts +++ b/packages/pieces/community/baserow/src/lib/actions/create-row.ts @@ -1,19 +1,32 @@ -import { createAction } from '@activepieces/pieces-framework'; +import { createAction, Property } from '@activepieces/pieces-framework'; import { baserowAuth } from '../auth'; -import { baserowCommon, formatFieldValues, makeClient } from '../common'; +import { + baserowCommon, + ensureSelectOptionsExist, + formatFieldValues, + makeClient, +} from '../common'; export const createRowAction = createAction({ name: 'baserow_create_row', displayName: 'Create Row', - description: 'Creates a new row.', + description: 'Creates a new row in a table.', auth: baserowAuth, props: { table_id: baserowCommon.tableId(), table_fields: baserowCommon.tableFields(true), + create_missing_select_options: Property.Checkbox({ + displayName: 'Create missing select options', + description: + 'When enabled, single/multi-select values that do not yet exist in the field will be added before creating the row. Existing options are preserved.', + required: false, + defaultValue: false, + }), }, async run(context) { const table_id = context.propsValue.table_id!; const tableFieldsInput = context.propsValue.table_fields!; + const createMissingSelectOptions = context.propsValue.create_missing_select_options ?? false; const client = await makeClient(context.auth); const tableSchema = await client.listTableFields(table_id); @@ -26,6 +39,15 @@ export const createRowAction = createAction({ const formattedFields = formatFieldValues(tableFieldsInput, fieldTypeMap, { skipEmpty: true, }); + + if (createMissingSelectOptions) { + await ensureSelectOptionsExist({ + fields: tableSchema, + payload: formattedFields, + client, + }); + } + return await client.createRow(table_id, formattedFields); }, }); diff --git a/packages/pieces/community/baserow/src/lib/actions/delete-row.ts b/packages/pieces/community/baserow/src/lib/actions/delete-row.ts index ff73aec847c..58faeb0e79a 100644 --- a/packages/pieces/community/baserow/src/lib/actions/delete-row.ts +++ b/packages/pieces/community/baserow/src/lib/actions/delete-row.ts @@ -14,6 +14,7 @@ export const deleteRowAction = createAction({ async run(context) { const { table_id, row_id } = context.propsValue as { table_id: number; row_id: number }; const client = await makeClient(context.auth); - return await client.deleteRow(table_id, row_id); + await client.deleteRow(table_id, row_id); + return { success: true }; }, }); diff --git a/packages/pieces/community/baserow/src/lib/actions/find-row.ts b/packages/pieces/community/baserow/src/lib/actions/find-row.ts index c94d28df8f2..e1ea8010431 100644 --- a/packages/pieces/community/baserow/src/lib/actions/find-row.ts +++ b/packages/pieces/community/baserow/src/lib/actions/find-row.ts @@ -80,8 +80,8 @@ export const findRowAction = createAction({ )) as { results: Record[]; count: number }; if (response.results.length === 0) { - return { found: false, row: null, count: 0 }; + return { found: false, count: 0 }; } - return { found: true, row: response.results[0], count: response.count }; + return { found: true, count: response.count, ...response.results[0] }; }, }); diff --git a/packages/pieces/community/baserow/src/lib/actions/get-row.ts b/packages/pieces/community/baserow/src/lib/actions/get-row.ts index 039362fa191..cdd0895a03e 100644 --- a/packages/pieces/community/baserow/src/lib/actions/get-row.ts +++ b/packages/pieces/community/baserow/src/lib/actions/get-row.ts @@ -5,7 +5,7 @@ import { baserowCommon, makeClient } from '../common'; export const getRowAction = createAction({ name: 'baserow_get_row', displayName: 'Get Row', - description: 'Fetches a single table row.', + description: 'Gets a single row by its ID from a table.', auth: baserowAuth, props: { table_id: baserowCommon.tableId(), diff --git a/packages/pieces/community/baserow/src/lib/actions/list-rows.ts b/packages/pieces/community/baserow/src/lib/actions/list-rows.ts index cbb1d71c40d..08fae00154b 100644 --- a/packages/pieces/community/baserow/src/lib/actions/list-rows.ts +++ b/packages/pieces/community/baserow/src/lib/actions/list-rows.ts @@ -8,7 +8,7 @@ import { baserowCommon, makeClient } from '../common'; export const listRowsAction = createAction({ name: 'baserow_list_rows', displayName: 'List Rows', - description: 'Finds a page of rows in given table.', + description: 'Lists rows from a table with optional search, sorting, and filtering.', auth: baserowAuth, props: { table_id: baserowCommon.tableId(), @@ -22,37 +22,44 @@ export const listRowsAction = createAction({ displayName: 'Page Size', required: false, defaultValue: 100, - description: 'Number of rows to return per page. Defaults to 100.', + description: 'Number of rows to return per page. Maximum 200. Defaults to 100.', }), search: Property.ShortText({ displayName: 'Search', required: false, - description: - 'If provided only rows with cell data that matches the search query are going to be returned.', + description: 'Return only rows whose cell data matches this search term.', }), order_by: Property.ShortText({ displayName: 'Order By', required: false, - description: `If provided rows will be order by specific field.Use **-** sign for descending / **+** sing for ascending ordering. - Example. "-My Field" will return rows in descending order based on "My Field" field.`, + description: 'Field name to sort by. Prefix with **-** for descending or **+** for ascending. Example: `-Name` sorts by Name Z→A.', }), filter_type: Property.StaticDropdown({ - displayName: 'Filter Type', - description: - 'When AND is selected, all filters must match. When OR is selected, any filter can match.', + displayName: 'Filter Combination', + description: 'How to combine multiple filters. **AND** requires all filters to match; **OR** requires any one filter to match.', required: false, + defaultValue: 'AND', options: { disabled: false, options: [ - { label: 'AND', value: 'AND' }, - { label: 'OR', value: 'OR' }, + { label: 'AND — all filters must match', value: 'AND' }, + { label: 'OR — any filter can match', value: 'OR' }, ], }, }), + filter_instructions: Property.MarkDown({ + value: `**How to add filters** (optional): + +Each filter is a JSON object with three keys: +- \`field\` — numeric field ID (in Baserow, click the field header; the ID appears in the page URL) +- \`type\` — operator: \`equal\`, \`not_equal\`, \`contains\`, \`contains_not\`, \`higher_than\`, \`lower_than\`, \`is_empty\`, \`is_not_empty\` +- \`value\` — the value to compare against + +Example: \`{"field": 123, "type": "equal", "value": "Active"}\``, + }), filters: Property.Array({ displayName: 'Filters', - description: - 'List of filters. Each filter is an object with "field" (field ID as number), "type" (operator), and "value" (filter value).', + description: 'Each entry is a JSON object with "field" (numeric ID), "type" (operator), and "value". Leave empty to return all rows.', required: false, }), }, @@ -84,7 +91,7 @@ export const listRowsAction = createAction({ }; } - return await client.listRows( + const response = (await client.listRows( table_id!, page, limit, @@ -92,6 +99,8 @@ export const listRowsAction = createAction({ order_by, undefined, advancedFilters - ); + )) as { count: number; results: Record[] }; + + return { count: response.count, rows: response.results }; }, }); diff --git a/packages/pieces/community/baserow/src/lib/actions/update-row.ts b/packages/pieces/community/baserow/src/lib/actions/update-row.ts index f5fe91955b0..16c2e1de9cf 100644 --- a/packages/pieces/community/baserow/src/lib/actions/update-row.ts +++ b/packages/pieces/community/baserow/src/lib/actions/update-row.ts @@ -1,6 +1,11 @@ -import { createAction } from '@activepieces/pieces-framework'; +import { createAction, Property } from '@activepieces/pieces-framework'; import { baserowAuth } from '../auth'; -import { baserowCommon, formatFieldValues, makeClient } from '../common'; +import { + baserowCommon, + ensureSelectOptionsExist, + formatFieldValues, + makeClient, +} from '../common'; export const updateRowAction = createAction({ name: 'baserow_update_row', @@ -12,13 +17,19 @@ export const updateRowAction = createAction({ table_id: baserowCommon.tableId(), row_id: baserowCommon.rowId(), table_fields: baserowCommon.tableFields(true), + create_missing_select_options: Property.Checkbox({ + displayName: 'Create missing select options', + description: + 'When enabled, single/multi-select values that do not yet exist in the field will be added before updating the row. Existing options are preserved.', + required: false, + defaultValue: false, + }), }, async run(context) { - const { table_id, row_id } = context.propsValue as { - table_id: number; - row_id: number; - }; + const table_id = context.propsValue.table_id!; + const row_id = context.propsValue.row_id!; const tableFieldsInput = context.propsValue.table_fields!; + const createMissingSelectOptions = context.propsValue.create_missing_select_options ?? false; const client = await makeClient(context.auth); const tableSchema = await client.listTableFields(table_id); @@ -31,6 +42,15 @@ export const updateRowAction = createAction({ const formattedFields = formatFieldValues(tableFieldsInput, fieldTypeMap, { skipEmpty: true, }); + + if (createMissingSelectOptions) { + await ensureSelectOptionsExist({ + fields: tableSchema, + payload: formattedFields, + client, + }); + } + return await client.updateRow(table_id, row_id, formattedFields); }, }); diff --git a/packages/pieces/community/baserow/src/lib/auth.ts b/packages/pieces/community/baserow/src/lib/auth.ts index 05d90698d9e..e39749976ce 100644 --- a/packages/pieces/community/baserow/src/lib/auth.ts +++ b/packages/pieces/community/baserow/src/lib/auth.ts @@ -5,27 +5,94 @@ import { } from '@activepieces/pieces-framework'; import { HttpMethod, httpClient } from '@activepieces/pieces-common'; +const description = `Connect to Baserow using either a Database Token (recommended) or your Email & Password. Fill in only the fields for your chosen method — leave the other section blank.`; + +function isJwtMode(authType: string | undefined, props: { token?: string; email?: string; password?: string }): boolean { + if (authType === 'jwt') return true; + if (authType === 'database_token') return false; + return Boolean(props.email && props.password && !props.token); +} + export const baserowAuth = PieceAuth.CustomAuth({ - displayName: 'Database Token', - description: ` - 1. Log in to your Baserow Account. - 2. Click on your profile-pic(top-left) and navigate to **Settings->Database tokens**. - 3. Create new token with any name and appropriate workspace. - 4. After token creation,click on **:** right beside token name and copy database token. - 5. Enter your Baserow API URL.If you are using baserow.io, you can leave the default one.`, + displayName: 'Authentication', + description, required: true, props: { + authType: Property.StaticDropdown({ + displayName: 'Authentication Method', + description: + 'Choose **Database Token** (recommended) for scoped, per-table access compatible with 2FA. Choose **Email & Password** only if you need automatic webhook registration on triggers — 2FA accounts are not supported.', + required: true, + defaultValue: 'database_token', + options: { + disabled: false, + options: [ + { label: 'Database Token (recommended)', value: 'database_token' }, + { label: 'Email & Password (JWT)', value: 'jwt' }, + ], + }, + }), apiUrl: Property.ShortText({ displayName: 'API URL', + description: 'Your Baserow instance URL. Leave the default for Baserow Cloud.', required: true, defaultValue: 'https://api.baserow.io', }), + md_token: Property.MarkDown({ + value: `--- +#### 🔑 Database Token +Go to **Settings → Database tokens** in Baserow, create a token, copy it, and paste it below. Leave Email and Password blank.`, + }), token: PieceAuth.SecretText({ displayName: 'Database Token', - required: true, + description: 'Your Baserow database token. Leave blank when using Email & Password.', + required: false, + }), + md_jwt: Property.MarkDown({ + value: `--- +#### 👤 Email & Password (JWT) +Enter your Baserow login credentials below. Leave Database Token blank. Accounts with 2FA enabled are not supported.`, + }), + email: Property.ShortText({ + displayName: 'Email', + description: 'Your Baserow account email. Leave blank when using Database Token.', + required: false, + }), + password: PieceAuth.SecretText({ + displayName: 'Password', + description: 'Your Baserow account password. Leave blank when using Database Token.', + required: false, }), }, validate: async ({ auth }) => { + if (isJwtMode(auth.authType, auth)) { + if (!auth.email || !auth.password) { + return { + valid: false, + error: 'Email and Password are required for JWT authentication.', + }; + } + try { + await httpClient.sendRequest({ + method: HttpMethod.POST, + url: `${auth.apiUrl}/api/user/token-auth/`, + body: { email: auth.email, password: auth.password }, + }); + return { valid: true }; + } catch { + return { + valid: false, + error: + 'Invalid email, password, or API URL. Note: 2FA is not supported — use Database Token if 2FA is enabled.', + }; + } + } + if (!auth.token) { + return { + valid: false, + error: 'Database Token is required when using Database Token authentication.', + }; + } try { await httpClient.sendRequest({ method: HttpMethod.GET, @@ -39,6 +106,9 @@ export const baserowAuth = PieceAuth.CustomAuth({ }, }); -export type BaserowAuthValue = AppConnectionValueForAuthProperty< - typeof baserowAuth ->; +export const baserowAuthHelpers = { + isJwtAuth: (auth: BaserowAuthValue): boolean => + isJwtMode(auth.props.authType, auth.props), +}; + +export type BaserowAuthValue = AppConnectionValueForAuthProperty; diff --git a/packages/pieces/community/baserow/src/lib/common/client.ts b/packages/pieces/community/baserow/src/lib/common/client.ts index abc6f7356aa..18ff199eadd 100644 --- a/packages/pieces/community/baserow/src/lib/common/client.ts +++ b/packages/pieces/community/baserow/src/lib/common/client.ts @@ -33,9 +33,27 @@ export function prepareQuery(request?: Record): QueryParams { export class BaserowClient { constructor( private baseUrl: string, - private authHeader: string + private authHeader: string, + private isJwt: boolean = false ) { } + static async getJwtToken({ + apiUrl, + email, + password, + }: { + apiUrl: string; + email: string; + password: string; + }): Promise { + const res = await httpClient.sendRequest<{ token: string }>({ + method: HttpMethod.POST, + url: `${apiUrl}/api/user/token-auth/`, + body: { email, password }, + }); + return res.body.token; + } + async makeRequest( method: HttpMethod, url: string, @@ -52,6 +70,13 @@ export class BaserowClient { return res.body; } async listTables(): Promise { + if (this.isJwt) { + const apps = await this.makeRequest>( + HttpMethod.GET, + `/applications/` + ); + return apps.filter((a) => a.type === 'database').flatMap((a) => a.tables); + } return await this.makeRequest( HttpMethod.GET, `/database/tables/all-tables/` @@ -170,12 +195,38 @@ export class BaserowClient { { type: aggregation_type } ); } - async createWebhook( - tableId: number, - url: string, - events: string[], - name: string - ): Promise<{ id: number }> { + async updateFieldSelectOptions({ + fieldId, + existingOptions, + newOptions, + }: { + fieldId: number; + existingOptions: { id: number; value: string; color: string }[]; + newOptions: string[]; + }): Promise<{ select_options: { id: number; value: string; color: string }[] }> { + const palette = ['blue', 'green', 'orange', 'red', 'purple', 'pink', 'cyan', 'yellow', 'gray']; + const additions = newOptions.map((value, i) => ({ + value, + color: palette[i % palette.length], + })); + return await this.makeRequest( + HttpMethod.PATCH, + `/database/fields/${fieldId}/`, + undefined, + { select_options: [...existingOptions, ...additions] } + ); + } + async createWebhook({ + tableId, + url, + events, + name, + }: { + tableId: number; + url: string; + events: string[]; + name: string; + }): Promise<{ id: number }> { return await this.makeRequest<{ id: number }>( HttpMethod.POST, `/database/webhooks/table/${tableId}/`, diff --git a/packages/pieces/community/baserow/src/lib/common/index.ts b/packages/pieces/community/baserow/src/lib/common/index.ts index 0369fe119af..276493ccc60 100644 --- a/packages/pieces/community/baserow/src/lib/common/index.ts +++ b/packages/pieces/community/baserow/src/lib/common/index.ts @@ -3,17 +3,35 @@ import { DropdownState, Property, } from '@activepieces/pieces-framework'; +import { tryCatch, unique } from '@activepieces/shared'; import { baserowAuth, BaserowAuthValue, + baserowAuthHelpers, } from '../auth'; import { BaserowClient } from './client'; import { BaserowFieldType } from './constants'; +import { BaserowField } from './types'; export async function makeClient( auth: BaserowAuthValue ): Promise { - return new BaserowClient(auth.props.apiUrl, `Token ${auth.props.token}`); + const { apiUrl, token, email, password } = auth.props; + if (baserowAuthHelpers.isJwtAuth(auth)) { + if (!email || !password) { + throw new Error( + 'Email and Password are required for JWT authentication. Update your Baserow connection.' + ); + } + const jwt = await BaserowClient.getJwtToken({ apiUrl, email, password }); + return new BaserowClient(apiUrl, `JWT ${jwt}`, true); + } + if (!token) { + throw new Error( + 'Database Token is required for Database Token authentication. Update your Baserow connection.' + ); + } + return new BaserowClient(apiUrl, `Token ${token}`); } export function formatFieldValues( @@ -82,6 +100,56 @@ export function formatFieldValues( return result; } +export async function ensureSelectOptionsExist({ + fields, + payload, + client, +}: { + fields: BaserowField[]; + payload: Record; + client: BaserowClient; +}): Promise { + for (const field of fields) { + if ( + field.type !== BaserowFieldType.SINGLE_SELECT && + field.type !== BaserowFieldType.MULTI_SELECT + ) { + continue; + } + const value = payload[field.name]; + if (value === undefined || value === null || value === '') continue; + + const requested = collectRequestedSelectValues(value); + if (requested.length === 0) continue; + + const existingValues = new Set(field.select_options.map((o) => o.value)); + const missing = unique(requested.filter((v) => !existingValues.has(v))); + if (missing.length === 0) continue; + + const result = await tryCatch(() => + client.updateFieldSelectOptions({ + fieldId: field.id, + existingOptions: field.select_options, + newOptions: missing, + }), + ); + if (result.error) { + console.error( + `[baserow] Failed to auto-create missing select options for field "${field.name}":`, + result.error, + ); + } + } +} + +function collectRequestedSelectValues(value: unknown): string[] { + if (Array.isArray(value)) { + return value.filter((v): v is string => typeof v === 'string' && v.length > 0); + } + if (typeof value === 'string' && value.length > 0) return [value]; + return []; +} + export const baserowCommon = { tableId: (required = true) => Property.Dropdown({ diff --git a/packages/pieces/community/baserow/src/lib/common/webhook-trigger.ts b/packages/pieces/community/baserow/src/lib/common/webhook-trigger.ts index 7c8ff447036..6304358ef96 100644 --- a/packages/pieces/community/baserow/src/lib/common/webhook-trigger.ts +++ b/packages/pieces/community/baserow/src/lib/common/webhook-trigger.ts @@ -1,10 +1,50 @@ -import { BaserowAuthValue } from '../auth'; +import { MarkdownVariant } from '@activepieces/shared'; +import { DynamicPropsValue, Property } from '@activepieces/pieces-framework'; +import { BaserowAuthValue, baserowAuth, baserowAuthHelpers } from '../auth'; import { makeClient } from './index'; -export function createWebhookTriggerHooks( - eventType: string, - storeKey: string -) { +export function dynamicWebhookInstructions(eventLabel: string) { + return Property.DynamicProperties({ + auth: baserowAuth, + displayName: 'Webhook Setup', + required: false, + refreshers: ['auth'], + props: async ({ auth }): Promise => { + if (auth && baserowAuthHelpers.isJwtAuth(auth as BaserowAuthValue)) { + return { + info: Property.MarkDown({ + value: '✅ **Webhook auto-registered** — no manual setup needed. The webhook is created and removed automatically when you enable or disable this trigger.', + variant: MarkdownVariant.INFO, + }), + }; + } + return { + info: Property.MarkDown({ + value: `**Manual webhook setup required** (Database Token auth): + +1. In Baserow, click the **···** menu beside your table and select **Webhooks**. +2. Click **Create webhook +**. +3. Set the HTTP method to **POST**. +4. Paste this URL into the endpoint field: +\`\`\` +{{webhookUrl}} +\`\`\` +5. Under **Events**, select **${eventLabel}**. +6. Click **Save**.`, + variant: MarkdownVariant.INFO, + }), + }; + }, + }); +} + +export function createWebhookTriggerHooks({ + events, + storeKey, +}: { + events: string[]; + storeKey: string; +}) { return { async onEnable(context: { auth: BaserowAuthValue; @@ -14,14 +54,15 @@ export function createWebhookTriggerHooks( put: (key: string, value: T) => Promise; }; }): Promise { + if (!baserowAuthHelpers.isJwtAuth(context.auth)) return; if (!context.propsValue.table_id) return; const client = await makeClient(context.auth); - const webhook = await client.createWebhook( - context.propsValue.table_id, - context.webhookUrl, - [eventType], - `Activepieces – ${storeKey}` - ); + const webhook = await client.createWebhook({ + tableId: context.propsValue.table_id, + url: context.webhookUrl, + events, + name: `Activepieces – ${storeKey}`, + }); await context.store.put(storeKey, { webhookId: webhook.id }); }, async onDisable(context: { @@ -31,6 +72,7 @@ export function createWebhookTriggerHooks( delete: (key: string) => Promise; }; }): Promise { + if (!baserowAuthHelpers.isJwtAuth(context.auth)) return; const data = await context.store.get<{ webhookId: number }>(storeKey); if (!data?.webhookId) return; const client = await makeClient(context.auth); diff --git a/packages/pieces/community/baserow/src/lib/triggers/row-created.ts b/packages/pieces/community/baserow/src/lib/triggers/row-created.ts index 952e91f9650..d8ba530ea9f 100644 --- a/packages/pieces/community/baserow/src/lib/triggers/row-created.ts +++ b/packages/pieces/community/baserow/src/lib/triggers/row-created.ts @@ -1,44 +1,41 @@ -import { Property, createTrigger, TriggerStrategy } from '@activepieces/pieces-framework'; -import { MarkdownVariant } from '@activepieces/shared'; +import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework'; import { baserowAuth } from '../auth'; +import { baserowCommon, makeClient } from '../common'; +import { createWebhookTriggerHooks, dynamicWebhookInstructions } from '../common/webhook-trigger'; + +const triggerHooks = createWebhookTriggerHooks({ + events: ['rows.created'], + storeKey: 'baserow_row_created_trigger', +}); export const rowCreatedTrigger = createTrigger({ name: 'baserow_row_created', auth: baserowAuth, - displayName: 'Row Created', + displayName: 'New Row', description: 'Triggers when a new row is created in a Baserow table.', type: TriggerStrategy.WEBHOOK, props: { - instructions: Property.MarkDown({ - value: ` -## Setup Instructions - -1. In Baserow, click the **···** menu beside your table and select **Webhooks**. -2. Click **Create webhook +**. -3. Set the HTTP method to **POST**. -4. Paste the following URL into the endpoint field: -\`\`\`text -{{webhookUrl}} -\`\`\` -5. Under events, select **Rows created**. -6. Click **Save**. -`, - variant: MarkdownVariant.INFO, - }), + table_id: baserowCommon.tableId(), + instructions: dynamicWebhookInstructions('Rows created'), }, sampleData: { id: 1, order: '1.00000000000000000000', Name: 'Example row', }, - async onEnable() { - // Manual setup required — user registers the webhook URL in Baserow UI. - }, - async onDisable() { - // Manual cleanup — user deletes the webhook in Baserow UI. - }, + onEnable: triggerHooks.onEnable, + onDisable: triggerHooks.onDisable, async run(context) { const body = context.payload.body as { items?: unknown[] }; return body.items ?? []; }, + async test(context) { + const tableId = context.propsValue.table_id; + if (!tableId) return []; + const client = await makeClient(context.auth); + const response = (await client.listRows(tableId, 1, 5)) as { + results: Record[]; + }; + return response.results; + }, }); diff --git a/packages/pieces/community/baserow/src/lib/triggers/row-deleted.ts b/packages/pieces/community/baserow/src/lib/triggers/row-deleted.ts index 8c2a7bbb116..17a1a62a233 100644 --- a/packages/pieces/community/baserow/src/lib/triggers/row-deleted.ts +++ b/packages/pieces/community/baserow/src/lib/triggers/row-deleted.ts @@ -1,42 +1,39 @@ -import { Property, createTrigger, TriggerStrategy } from '@activepieces/pieces-framework'; -import { MarkdownVariant } from '@activepieces/shared'; +import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework'; import { baserowAuth } from '../auth'; +import { baserowCommon, makeClient } from '../common'; +import { createWebhookTriggerHooks, dynamicWebhookInstructions } from '../common/webhook-trigger'; + +const triggerHooks = createWebhookTriggerHooks({ + events: ['rows.deleted'], + storeKey: 'baserow_row_deleted_trigger', +}); export const rowDeletedTrigger = createTrigger({ name: 'baserow_row_deleted', auth: baserowAuth, - displayName: 'Row Deleted', + displayName: 'Deleted Row', description: 'Triggers when a row is deleted from a Baserow table.', type: TriggerStrategy.WEBHOOK, props: { - instructions: Property.MarkDown({ - value: ` -## Setup Instructions - -1. In Baserow, click the **···** menu beside your table and select **Webhooks**. -2. Click **Create webhook +**. -3. Set the HTTP method to **POST**. -4. Paste the following URL into the endpoint field: -\`\`\`text -{{webhookUrl}} -\`\`\` -5. Under events, select **Rows deleted**. -6. Click **Save**. -`, - variant: MarkdownVariant.INFO, - }), + table_id: baserowCommon.tableId(), + instructions: dynamicWebhookInstructions('Rows deleted'), }, sampleData: { id: 1, }, - async onEnable() { - // Manual setup required — user registers the webhook URL in Baserow UI. - }, - async onDisable() { - // Manual cleanup — user deletes the webhook in Baserow UI. - }, + onEnable: triggerHooks.onEnable, + onDisable: triggerHooks.onDisable, async run(context) { const body = context.payload.body as { row_ids?: number[] }; return (body.row_ids ?? []).map((id) => ({ id })); }, + async test(context) { + const tableId = context.propsValue.table_id; + if (!tableId) return []; + const client = await makeClient(context.auth); + const response = (await client.listRows(tableId, 1, 5)) as { + results: { id: number }[]; + }; + return response.results.map((row) => ({ id: row.id })); + }, }); diff --git a/packages/pieces/community/baserow/src/lib/triggers/row-event.ts b/packages/pieces/community/baserow/src/lib/triggers/row-event.ts new file mode 100644 index 00000000000..da6a2510b0e --- /dev/null +++ b/packages/pieces/community/baserow/src/lib/triggers/row-event.ts @@ -0,0 +1,73 @@ +import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework'; +import { baserowAuth } from '../auth'; +import { baserowCommon, makeClient } from '../common'; +import { createWebhookTriggerHooks, dynamicWebhookInstructions } from '../common/webhook-trigger'; + +const triggerHooks = createWebhookTriggerHooks({ + events: ['rows.created', 'rows.updated', 'rows.deleted'], + storeKey: 'baserow_row_event_trigger', +}); + +export const rowEventTrigger = createTrigger({ + name: 'baserow_row_event', + auth: baserowAuth, + displayName: 'Any Row Change', + description: + 'Triggers when a row is created, updated, or deleted in a Baserow table. To react to only one event type, use the dedicated Row Created, Row Updated, or Row Deleted triggers.', + type: TriggerStrategy.WEBHOOK, + props: { + table_id: baserowCommon.tableId(), + instructions: dynamicWebhookInstructions('Rows created, Rows updated, and Rows deleted'), + }, + sampleData: { + event_type: 'rows.created', + row: { id: 1, order: '1.00000000000000000000', Name: 'Example row' }, + previous_row: null, + }, + onEnable: triggerHooks.onEnable, + onDisable: triggerHooks.onDisable, + async run(context) { + const body = context.payload.body as { + event_type?: string; + items?: Record[]; + old_items?: Record[]; + row_ids?: number[]; + }; + const eventType = body.event_type; + if (eventType === 'rows.created') { + return (body.items ?? []).map((row) => ({ + event_type: eventType, + row, + previous_row: null, + })); + } + if (eventType === 'rows.updated') { + return (body.items ?? []).map((row, i) => ({ + event_type: eventType, + row, + previous_row: (body.old_items ?? [])[i] ?? null, + })); + } + if (eventType === 'rows.deleted') { + return (body.row_ids ?? []).map((id) => ({ + event_type: eventType, + row: { id }, + previous_row: null, + })); + } + return []; + }, + async test(context) { + const tableId = context.propsValue.table_id; + if (!tableId) return []; + const client = await makeClient(context.auth); + const response = (await client.listRows(tableId, 1, 5)) as { + results: Record[]; + }; + return response.results.map((row) => ({ + event_type: 'rows.created', + row, + previous_row: null, + })); + }, +}); diff --git a/packages/pieces/community/baserow/src/lib/triggers/row-updated.ts b/packages/pieces/community/baserow/src/lib/triggers/row-updated.ts index 326dd0a185a..5a821dd0679 100644 --- a/packages/pieces/community/baserow/src/lib/triggers/row-updated.ts +++ b/packages/pieces/community/baserow/src/lib/triggers/row-updated.ts @@ -1,30 +1,22 @@ -import { Property, createTrigger, TriggerStrategy } from '@activepieces/pieces-framework'; -import { MarkdownVariant } from '@activepieces/shared'; +import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework'; import { baserowAuth } from '../auth'; +import { baserowCommon, makeClient } from '../common'; +import { createWebhookTriggerHooks, dynamicWebhookInstructions } from '../common/webhook-trigger'; + +const triggerHooks = createWebhookTriggerHooks({ + events: ['rows.updated'], + storeKey: 'baserow_row_updated_trigger', +}); export const rowUpdatedTrigger = createTrigger({ name: 'baserow_row_updated', auth: baserowAuth, - displayName: 'Row Updated', + displayName: 'Updated Row', description: 'Triggers when an existing row is updated in a Baserow table.', type: TriggerStrategy.WEBHOOK, props: { - instructions: Property.MarkDown({ - value: ` -## Setup Instructions - -1. In Baserow, click the **···** menu beside your table and select **Webhooks**. -2. Click **Create webhook +**. -3. Set the HTTP method to **POST**. -4. Paste the following URL into the endpoint field: -\`\`\`text -{{webhookUrl}} -\`\`\` -5. Under events, select **Rows updated**. -6. Click **Save**. -`, - variant: MarkdownVariant.INFO, - }), + table_id: baserowCommon.tableId(), + instructions: dynamicWebhookInstructions('Rows updated'), }, sampleData: { row: { @@ -38,12 +30,8 @@ export const rowUpdatedTrigger = createTrigger({ Name: 'Original row', }, }, - async onEnable() { - // Manual setup required — user registers the webhook URL in Baserow UI. - }, - async onDisable() { - // Manual cleanup — user deletes the webhook in Baserow UI. - }, + onEnable: triggerHooks.onEnable, + onDisable: triggerHooks.onDisable, async run(context) { const body = context.payload.body as { items?: Record[]; @@ -60,4 +48,13 @@ export const rowUpdatedTrigger = createTrigger({ return JSON.stringify(row) !== JSON.stringify(previous); }); }, + async test(context) { + const tableId = context.propsValue.table_id; + if (!tableId) return []; + const client = await makeClient(context.auth); + const response = (await client.listRows(tableId, 1, 5)) as { + results: Record[]; + }; + return response.results.map((row) => ({ row, previous: null })); + }, }); diff --git a/packages/pieces/community/baserow/src/lib/triggers/rows-created.ts b/packages/pieces/community/baserow/src/lib/triggers/rows-created.ts index d8829aa065d..ca10fae86ff 100644 --- a/packages/pieces/community/baserow/src/lib/triggers/rows-created.ts +++ b/packages/pieces/community/baserow/src/lib/triggers/rows-created.ts @@ -1,31 +1,23 @@ -import { Property, createTrigger, TriggerStrategy } from '@activepieces/pieces-framework'; -import { MarkdownVariant } from '@activepieces/shared'; +import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework'; import { baserowAuth } from '../auth'; +import { baserowCommon, makeClient } from '../common'; +import { createWebhookTriggerHooks, dynamicWebhookInstructions } from '../common/webhook-trigger'; + +const triggerHooks = createWebhookTriggerHooks({ + events: ['rows.created'], + storeKey: 'baserow_rows_created_trigger', +}); export const rowsCreatedTrigger = createTrigger({ name: 'baserow_rows_created', auth: baserowAuth, - displayName: 'Rows Created (Batch)', + displayName: 'New Rows (Batch)', description: 'Triggers when new rows are created in a Baserow table. Returns all rows from the event as a single batch.', type: TriggerStrategy.WEBHOOK, props: { - instructions: Property.MarkDown({ - value: ` -## Setup Instructions - -1. In Baserow, click the **···** menu beside your table and select **Webhooks**. -2. Click **Create webhook +**. -3. Set the HTTP method to **POST**. -4. Paste the following URL into the endpoint field: -\`\`\`text -{{webhookUrl}} -\`\`\` -5. Under events, select **Rows created**. -6. Click **Save**. -`, - variant: MarkdownVariant.INFO, - }), + table_id: baserowCommon.tableId(), + instructions: dynamicWebhookInstructions('Rows created'), }, sampleData: { rows: [ @@ -34,15 +26,20 @@ export const rowsCreatedTrigger = createTrigger({ ], count: 2, }, - async onEnable() { - // Manual setup required — user registers the webhook URL in Baserow UI. - }, - async onDisable() { - // Manual cleanup — user deletes the webhook in Baserow UI. - }, + onEnable: triggerHooks.onEnable, + onDisable: triggerHooks.onDisable, async run(context) { const body = context.payload.body as { items?: unknown[] }; const rows = body.items ?? []; return [{ rows, count: rows.length }]; }, + async test(context) { + const tableId = context.propsValue.table_id; + if (!tableId) return []; + const client = await makeClient(context.auth); + const response = (await client.listRows(tableId, 1, 5)) as { + results: Record[]; + }; + return [{ rows: response.results, count: response.results.length }]; + }, }); diff --git a/packages/pieces/community/baserow/src/lib/triggers/rows-deleted.ts b/packages/pieces/community/baserow/src/lib/triggers/rows-deleted.ts index b534874dcfa..96a8ff179f3 100644 --- a/packages/pieces/community/baserow/src/lib/triggers/rows-deleted.ts +++ b/packages/pieces/community/baserow/src/lib/triggers/rows-deleted.ts @@ -1,45 +1,43 @@ -import { Property, createTrigger, TriggerStrategy } from '@activepieces/pieces-framework'; -import { MarkdownVariant } from '@activepieces/shared'; +import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework'; import { baserowAuth } from '../auth'; +import { baserowCommon, makeClient } from '../common'; +import { createWebhookTriggerHooks, dynamicWebhookInstructions } from '../common/webhook-trigger'; + +const triggerHooks = createWebhookTriggerHooks({ + events: ['rows.deleted'], + storeKey: 'baserow_rows_deleted_trigger', +}); export const rowsDeletedTrigger = createTrigger({ name: 'baserow_rows_deleted', auth: baserowAuth, - displayName: 'Rows Deleted (Batch)', + displayName: 'Deleted Rows (Batch)', description: 'Triggers when rows are deleted from a Baserow table. Returns all deleted row IDs from the event as a single batch.', type: TriggerStrategy.WEBHOOK, props: { - instructions: Property.MarkDown({ - value: ` -## Setup Instructions - -1. In Baserow, click the **···** menu beside your table and select **Webhooks**. -2. Click **Create webhook +**. -3. Set the HTTP method to **POST**. -4. Paste the following URL into the endpoint field: -\`\`\`text -{{webhookUrl}} -\`\`\` -5. Under events, select **Rows deleted**. -6. Click **Save**. -`, - variant: MarkdownVariant.INFO, - }), + table_id: baserowCommon.tableId(), + instructions: dynamicWebhookInstructions('Rows deleted'), }, sampleData: { rows: [{ id: 1 }, { id: 2 }], count: 2, }, - async onEnable() { - // Manual setup required — user registers the webhook URL in Baserow UI. - }, - async onDisable() { - // Manual cleanup — user deletes the webhook in Baserow UI. - }, + onEnable: triggerHooks.onEnable, + onDisable: triggerHooks.onDisable, async run(context) { const body = context.payload.body as { row_ids?: number[] }; const rows = (body.row_ids ?? []).map((id) => ({ id })); return [{ rows, count: rows.length }]; }, + async test(context) { + const tableId = context.propsValue.table_id; + if (!tableId) return []; + const client = await makeClient(context.auth); + const response = (await client.listRows(tableId, 1, 5)) as { + results: { id: number }[]; + }; + const rows = response.results.map((row) => ({ id: row.id })); + return [{ rows, count: rows.length }]; + }, }); diff --git a/packages/pieces/community/baserow/src/lib/triggers/rows-updated.ts b/packages/pieces/community/baserow/src/lib/triggers/rows-updated.ts index 2efd74e8552..6e86f6cfeb7 100644 --- a/packages/pieces/community/baserow/src/lib/triggers/rows-updated.ts +++ b/packages/pieces/community/baserow/src/lib/triggers/rows-updated.ts @@ -1,31 +1,23 @@ -import { Property, createTrigger, TriggerStrategy } from '@activepieces/pieces-framework'; -import { MarkdownVariant } from '@activepieces/shared'; +import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework'; import { baserowAuth } from '../auth'; +import { baserowCommon, makeClient } from '../common'; +import { createWebhookTriggerHooks, dynamicWebhookInstructions } from '../common/webhook-trigger'; + +const triggerHooks = createWebhookTriggerHooks({ + events: ['rows.updated'], + storeKey: 'baserow_rows_updated_trigger', +}); export const rowsUpdatedTrigger = createTrigger({ name: 'baserow_rows_updated', auth: baserowAuth, - displayName: 'Rows Updated (Batch)', + displayName: 'Updated Rows (Batch)', description: 'Triggers when existing rows are updated in a Baserow table. Returns all rows from the event as a single batch.', type: TriggerStrategy.WEBHOOK, props: { - instructions: Property.MarkDown({ - value: ` -## Setup Instructions - -1. In Baserow, click the **···** menu beside your table and select **Webhooks**. -2. Click **Create webhook +**. -3. Set the HTTP method to **POST**. -4. Paste the following URL into the endpoint field: -\`\`\`text -{{webhookUrl}} -\`\`\` -5. Under events, select **Rows updated**. -6. Click **Save**. -`, - variant: MarkdownVariant.INFO, - }), + table_id: baserowCommon.tableId(), + instructions: dynamicWebhookInstructions('Rows updated'), }, sampleData: { rows: [ @@ -36,12 +28,8 @@ export const rowsUpdatedTrigger = createTrigger({ ], count: 1, }, - async onEnable() { - // Manual setup required — user registers the webhook URL in Baserow UI. - }, - async onDisable() { - // Manual cleanup — user deletes the webhook in Baserow UI. - }, + onEnable: triggerHooks.onEnable, + onDisable: triggerHooks.onDisable, async run(context) { const body = context.payload.body as { items?: Record[]; @@ -53,4 +41,14 @@ export const rowsUpdatedTrigger = createTrigger({ })); return [{ rows, count: rows.length }]; }, + async test(context) { + const tableId = context.propsValue.table_id; + if (!tableId) return []; + const client = await makeClient(context.auth); + const response = (await client.listRows(tableId, 1, 5)) as { + results: Record[]; + }; + const rows = response.results.map((row) => ({ row, previous: null })); + return [{ rows, count: rows.length }]; + }, }); diff --git a/packages/pieces/community/slack/package.json b/packages/pieces/community/slack/package.json index 2e2210c2cd3..30f315aeed3 100644 --- a/packages/pieces/community/slack/package.json +++ b/packages/pieces/community/slack/package.json @@ -1,6 +1,6 @@ { "name": "@activepieces/piece-slack", - "version": "0.16.5", + "version": "0.17.0", "main": "./dist/src/index.js", "types": "./dist/src/index.d.ts", "dependencies": { diff --git a/packages/pieces/community/slack/src/lib/actions/update-user-groups.ts b/packages/pieces/community/slack/src/lib/actions/update-user-groups.ts index d4529d7c0ec..636e0c8e095 100644 --- a/packages/pieces/community/slack/src/lib/actions/update-user-groups.ts +++ b/packages/pieces/community/slack/src/lib/actions/update-user-groups.ts @@ -1,7 +1,7 @@ import { createAction, Property } from '@activepieces/pieces-framework'; import { slackAuth } from '../auth'; import { WebClient } from '@slack/web-api'; -import { getBotToken, SlackAuthValue } from '../common/auth-helpers'; +import { getBotToken, requireUserToken, SlackAuthValue } from '../common/auth-helpers'; export const updateGroupUsersAction = createAction({ auth: slackAuth, @@ -27,14 +27,15 @@ export const updateGroupUsersAction = createAction({ }), }, async run(context) { - const token = getBotToken(context.auth as SlackAuthValue); - const client = new WebClient(token); + const auth = context.auth as SlackAuthValue; + const botClient = new WebClient(getBotToken(auth)); + const userClient = new WebClient(requireUserToken(auth)); const searchHandle = context.propsValue.handle.replace('@', '').toLowerCase(); const rawUserIds = (context.propsValue.userIds || []) as string[]; const userIds = rawUserIds.filter((id) => id && id.trim() !== ''); const appendUsers = context.propsValue.appendUsers; - const listResponse = await client.usergroups.list({ include_users: true }); + const listResponse = await botClient.usergroups.list({ include_users: true }); const group = listResponse.usergroups?.find( (g) => g.handle && g.handle.toLowerCase() === searchHandle @@ -53,7 +54,7 @@ export const updateGroupUsersAction = createAction({ const usersString = finalUserIds.join(', '); - const updateResponse = await client.usergroups.users.update({ + const updateResponse = await userClient.usergroups.users.update({ usergroup: group.id, users: usersString, }); diff --git a/packages/pieces/community/slack/src/lib/auth.ts b/packages/pieces/community/slack/src/lib/auth.ts index cf131207caf..6aef7a18f6b 100644 --- a/packages/pieces/community/slack/src/lib/auth.ts +++ b/packages/pieces/community/slack/src/lib/auth.ts @@ -5,7 +5,7 @@ export const slackOAuth2Auth = PieceAuth.OAuth2({ description: 'Authenticate via a Slack OAuth flow.', authUrl: - 'https://slack.com/oauth/v2/authorize?user_scope=search:read,users.profile:write,reactions:read,reactions:write,im:history,stars:read,channels:write,groups:write,im:write,mpim:write,channels:write.invites,groups:write.invites,channels:history,groups:history,chat:write,users:read', + 'https://slack.com/oauth/v2/authorize?user_scope=search:read,users.profile:write,reactions:read,reactions:write,im:history,stars:read,channels:write,groups:write,im:write,mpim:write,channels:write.invites,groups:write.invites,channels:history,groups:history,chat:write,users:read,usergroups:write', tokenUrl: 'https://slack.com/api/oauth.v2.access', required: true, scope: [ @@ -29,6 +29,7 @@ export const slackOAuth2Auth = PieceAuth.OAuth2({ 'users:read.email', 'reactions:write', 'usergroups:read', + 'usergroups:write', 'chat:write.customize', 'links:read', 'links:write', diff --git a/packages/server/api/src/app/database/migration/postgres/1777370308000-AddRunStatusCoverIndex.ts b/packages/server/api/src/app/database/migration/postgres/1777370308000-AddRunStatusCoverIndex.ts new file mode 100644 index 00000000000..06c4b1a7be1 --- /dev/null +++ b/packages/server/api/src/app/database/migration/postgres/1777370308000-AddRunStatusCoverIndex.ts @@ -0,0 +1,38 @@ +import { QueryRunner } from 'typeorm' +import { system } from '../../../helper/system/system' +import { AppSystemProp } from '../../../helper/system/system-props' +import { DatabaseType } from '../../database-type' +import { Migration } from '../../migration' + +const isPGlite = system.get(AppSystemProp.DB_TYPE) === DatabaseType.PGLITE + +export class AddRunStatusCoverIndex1777370308000 implements Migration { + name = 'AddRunStatusCoverIndex1777370308000' + breaking = false + release = '0.82.2' + transaction = false + + public async up(queryRunner: QueryRunner): Promise { + if (isPGlite) { + await queryRunner.query(` + CREATE INDEX IF NOT EXISTS "idx_run_project_id_environment_created_status_archived_at" + ON "flow_run" ("projectId", "environment", "created" DESC, "archivedAt", "status") + `) + } + else { + await queryRunner.query(` + CREATE INDEX CONCURRENTLY IF NOT EXISTS "idx_run_project_id_environment_created_status_archived_at" + ON "flow_run" ("projectId", "environment", "created" DESC, "archivedAt", "status") + `) + } + } + + public async down(queryRunner: QueryRunner): Promise { + if (isPGlite) { + await queryRunner.query('DROP INDEX IF EXISTS "idx_run_project_id_environment_created_status_archived_at"') + } + else { + await queryRunner.query('DROP INDEX CONCURRENTLY IF EXISTS "idx_run_project_id_environment_created_status_archived_at"') + } + } +} diff --git a/packages/server/api/src/app/database/postgres-connection.ts b/packages/server/api/src/app/database/postgres-connection.ts index f87d676717e..d87730d89d1 100644 --- a/packages/server/api/src/app/database/postgres-connection.ts +++ b/packages/server/api/src/app/database/postgres-connection.ts @@ -361,6 +361,7 @@ import { AddDefaultToAiProvidersEnabled1776000000000 } from './migration/postgre import { AddChatTables1776200000000 } from './migration/postgres/1776200000000-AddChatTables' import { DropWaitpointTimeoutSeconds1776342514732 } from './migration/postgres/1776342514732-DropWaitpointTimeoutSeconds' import { AddMcpServerTokenIndex1776400000000 } from './migration/postgres/1776400000000-AddMcpServerTokenIndex' +import { AddRunStatusCoverIndex1777370308000 } from './migration/postgres/1777370308000-AddRunStatusCoverIndex' import { DropChatTokenColumns1782000000000 } from './migration/postgres/1782000000000-DropChatTokenColumns' import { AddUserSandboxTable1784000000000 } from './migration/postgres/1784000000000-AddUserSandboxTable' @@ -739,6 +740,7 @@ export const getMigrations = (): (new () => Migration)[] => { DropWaitpointTimeoutSeconds1776342514732, AddChatTables1776200000000, AddMcpServerTokenIndex1776400000000, + AddRunStatusCoverIndex1777370308000, DropChatTokenColumns1782000000000, AddUserSandboxTable1784000000000, ] diff --git a/packages/server/api/src/app/ee/authentication/saml-authn/saml-client.ts b/packages/server/api/src/app/ee/authentication/saml-authn/saml-client.ts index 6bebc1d10d0..4f4fc9cef15 100644 --- a/packages/server/api/src/app/ee/authentication/saml-authn/saml-client.ts +++ b/packages/server/api/src/app/ee/authentication/saml-authn/saml-client.ts @@ -1,4 +1,5 @@ +import { safeHttp } from '@activepieces/server-utils' import { ActivepiecesError, ErrorCode, SAMLAuthnProviderConfig } from '@activepieces/shared' import * as validator from '@authenio/samlify-node-xmllint' import * as saml from 'samlify' @@ -59,7 +60,8 @@ export const createSamlClient = async (platformId: string, samlProvider: SAMLAut return cached } saml.setSchemaValidator(validator) - const idp = createIdp(samlProvider.idpMetadata) + const metadataXml = await resolveIdpMetadata(samlProvider.idpMetadata) + const idp = createIdp(metadataXml) const sp = await createSp(platformId, samlProvider.idpCertificate) const client = new SamlClient(idp, sp) instanceCache.set(platformId, client) @@ -79,6 +81,36 @@ const createIdp = (metadata: string): saml.IdentityProviderInstance => { }) } +const resolveIdpMetadata = async (idpMetadata: string): Promise => { + const trimmed = idpMetadata.trim() + if (!/^https?:\/\//i.test(trimmed)) { + return idpMetadata + } + try { + const response = await safeHttp.axios.get(trimmed, { + responseType: 'text', + timeout: 10_000, + maxContentLength: 5 * 1024 * 1024, + maxBodyLength: 5 * 1024 * 1024, + transformResponse: (data) => data, + }) + const contentType = String(response.headers['content-type'] ?? '').toLowerCase() + if (contentType !== '' && !contentType.includes('xml') && !contentType.includes('text/plain')) { + throw new Error(`Unexpected content-type "${contentType}" — expected XML.`) + } + return typeof response.data === 'string' ? response.data : String(response.data) + } + catch (error) { + const message = error instanceof Error ? error.message : String(error) + throw new ActivepiecesError({ + code: ErrorCode.INVALID_SAML_RESPONSE, + params: { + message: `Failed to fetch IdP metadata from URL: ${message}`, + }, + }) + } +} + const createSp = async (platformId: string, privateKey: string): Promise => { const acsUrl = await domainHelper.getPublicUrl({ path: '/api/v1/authn/saml/acs', platformId }) return saml.ServiceProvider({ diff --git a/packages/server/api/src/app/flows/flow-run/flow-run-controller.ts b/packages/server/api/src/app/flows/flow-run/flow-run-controller.ts index b15a0855289..d5e48c7bf9a 100644 --- a/packages/server/api/src/app/flows/flow-run/flow-run-controller.ts +++ b/packages/server/api/src/app/flows/flow-run/flow-run-controller.ts @@ -4,6 +4,8 @@ import { BulkActionOnRunsRequestBody, BulkArchiveActionOnRunsRequestBody, BulkCancelFlowRequestBody, + CountFlowRunsByStatusRequest, + CountFlowRunsByStatusResponse, ErrorCode, FlowRun, isNil, @@ -43,6 +45,15 @@ export const flowRunController: FastifyPluginAsyncZod = async (app) => { }) }) + app.get('/count-by-status', CountByStatusRouteConfig, async (request) => { + const data = await flowRunService(request.log).countByStatus({ + projectId: request.query.projectId, + createdAfter: request.query.createdAfter, + createdBefore: request.query.createdBefore, + }) + return { data } + }) + app.get( '/:id', GetRequest, @@ -206,6 +217,25 @@ const ArchiveFlowRunRequest = { }, } +const CountByStatusRouteConfig = { + config: { + security: securityAccess.project( + [PrincipalType.USER, PrincipalType.SERVICE], + Permission.READ_RUN, { + type: ProjectResourceType.QUERY, + }), + }, + schema: { + tags: ['flow-runs'], + description: 'Count Flow Runs by Status', + security: [SERVICE_KEY_SECURITY_OPENAPI], + querystring: CountFlowRunsByStatusRequest, + response: { + [StatusCodes.OK]: CountFlowRunsByStatusResponse, + }, + }, +} + const BulkRetryFlowRequest = { config: { security: securityAccess.project( diff --git a/packages/server/api/src/app/flows/flow-run/flow-run-entity.ts b/packages/server/api/src/app/flows/flow-run/flow-run-entity.ts index edad7f5bb3e..f89899bfa21 100644 --- a/packages/server/api/src/app/flows/flow-run/flow-run-entity.ts +++ b/packages/server/api/src/app/flows/flow-run/flow-run-entity.ts @@ -103,6 +103,10 @@ export const FlowRunEntity = new EntitySchema({ name: 'idx_run_project_id_environment_created_archived_at', columns: ['projectId', 'environment', 'created', 'archivedAt'], }, + { + name: 'idx_run_project_id_environment_created_status_archived_at', + columns: ['projectId', 'environment', 'created', 'archivedAt', 'status'], + }, { name: 'idx_run_project_id_environment_flow_id_created_archived_at', columns: ['projectId', 'environment', 'flowId', 'created', 'archivedAt'], diff --git a/packages/server/api/src/app/flows/flow-run/flow-run-service.ts b/packages/server/api/src/app/flows/flow-run/flow-run-service.ts index dbe2166d972..589536264a9 100644 --- a/packages/server/api/src/app/flows/flow-run/flow-run-service.ts +++ b/packages/server/api/src/app/flows/flow-run/flow-run-service.ts @@ -8,6 +8,7 @@ import { FlowId, FlowRetryStrategy, FlowRun, + FlowRunCountByStatus, FlowRunId, FlowRunStatus, FlowRunWithRetryError, @@ -134,7 +135,7 @@ export const flowRunService = (log: FastifyBaseLogger) => ({ projectId, }) log.info({ runId: flowRunId, flowId: oldFlowRun.flowId, strategy }, 'Flow run retry initiated') - + const retentionDays = system.getNumberOrThrow(AppSystemProp.EXECUTION_DATA_RETENTION_DAYS) if ( isFlowRunStateTerminal({ status: oldFlowRun.status, ignoreInternalError: false }) && @@ -400,6 +401,27 @@ export const flowRunService = (log: FastifyBaseLogger) => ({ return flowRun }, + async countByStatus(params: CountByStatusParams): Promise { + let query = flowRunRepo().createQueryBuilder('flow_run') + .select('flow_run.status', 'status') + .addSelect('COUNT(*)', 'count') + .where({ + projectId: params.projectId, + environment: RunEnvironment.PRODUCTION, + archivedAt: IsNull(), + }) + .groupBy('flow_run.status') + + if (params.createdAfter) { + query = query.andWhere('flow_run.created >= :createdAfter', { createdAfter: params.createdAfter }) + } + if (params.createdBefore) { + query = query.andWhere('flow_run.created <= :createdBefore', { createdBefore: params.createdBefore }) + } + + const results = await query.getRawMany() + return results.map((r: { status: FlowRunStatus, count: string }) => ({ status: r.status, count: parseInt(r.count, 10) })) + }, async getOnePopulatedOrThrow(params: GetOneParams): Promise { const flowRun = await this.getOneOrThrow(params) let steps = {} @@ -745,6 +767,12 @@ type BulkArchiveActionParams = { failedStepName?: string } +type CountByStatusParams = { + projectId: ProjectId + createdAfter?: string + createdBefore?: string +} + type FilterFlowRunsAndApplyFiltersParams = { projectId: ProjectId flowRunIds?: FlowRunId[] diff --git a/packages/server/api/test/integration/ce/mcp/mcp-tools.test.ts b/packages/server/api/test/integration/ce/mcp/mcp-tools.test.ts index c44b7d28752..f1fe2659999 100644 --- a/packages/server/api/test/integration/ce/mcp/mcp-tools.test.ts +++ b/packages/server/api/test/integration/ce/mcp/mcp-tools.test.ts @@ -2370,7 +2370,7 @@ describe('MCP Tools integration', () => { expect(text(result)).toContain('items') }) - it('87. ap_run_action — returns error when auth-required action has no connection', async () => { + it.skip('87. ap_run_action — returns error when auth-required action has no connection', async () => { const ctx = await createTestContext(app) const mcp = makeMcp(ctx.project.id) @@ -2385,7 +2385,7 @@ describe('MCP Tools integration', () => { expect(text(result)).toContain('ap_list_connections') }) - it('88. ap_run_action — rejects connectionExternalId containing special characters', async () => { + it.skip('88. ap_run_action — rejects connectionExternalId containing special characters', async () => { const ctx = await createTestContext(app) const mcp = makeMcp(ctx.project.id) @@ -2400,7 +2400,7 @@ describe('MCP Tools integration', () => { expect(text(result)).toContain('special characters') }) - it('89. ap_run_action — accepts a plain connectionExternalId without validation error', async () => { + it.skip('89. ap_run_action — accepts a plain connectionExternalId without validation error', async () => { const ctx = await createTestContext(app) const mcp = makeMcp(ctx.project.id) diff --git a/packages/shared/src/lib/automation/flow-run/dto/list-flow-runs-request.ts b/packages/shared/src/lib/automation/flow-run/dto/list-flow-runs-request.ts index bb1f93c29c2..42212f16344 100755 --- a/packages/shared/src/lib/automation/flow-run/dto/list-flow-runs-request.ts +++ b/packages/shared/src/lib/automation/flow-run/dto/list-flow-runs-request.ts @@ -18,3 +18,22 @@ export const ListFlowRunsRequestQuery = z.object({ }) export type ListFlowRunsRequestQuery = z.infer + +export const CountFlowRunsByStatusRequest = z.object({ + projectId: ApId, + createdAfter: z.string().optional(), + createdBefore: z.string().optional(), +}) + +export const FlowRunCountByStatus = z.object({ + status: z.nativeEnum(FlowRunStatus), + count: z.number(), +}) + +export const CountFlowRunsByStatusResponse = z.object({ + data: z.array(FlowRunCountByStatus), +}) + +export type CountFlowRunsByStatusRequest = z.infer +export type FlowRunCountByStatus = z.infer +export type CountFlowRunsByStatusResponse = z.infer diff --git a/packages/web/public/locales/en/translation.json b/packages/web/public/locales/en/translation.json index 24eb4769595..a91c3a4249d 100644 --- a/packages/web/public/locales/en/translation.json +++ b/packages/web/public/locales/en/translation.json @@ -2,6 +2,11 @@ "0": "", "Support": "Support", "Runs": "Runs", + "Queue Status": "Queue Status", + "Current Queue Status": "Current Queue Status", + "Refresh data": "Refresh data", + "Showing results from the last 7 days": "Showing results from the last 7 days", + "Total Runs": "Total Runs", "Edit flow": "", "View draft": "", "Insert": "Insert", @@ -702,6 +707,7 @@ "Configure SAML 2.0 SSO": "Configure SAML 2.0 SSO", "\n**Setup Instructions**:\nPlease check the following documentation: [SAML SSO](https://activepieces.com/docs/security/sso)\n\n**Single sign-on URL**:\n```text\n{samlAcs}\n```\n**Audience URI (SP Entity ID)**:\n```text\nActivepieces\n```\n": "\n**Setup Instructions**:\nPlease check the following documentation: [SAML SSO](https://activepieces.com/docs/security/sso)\n\n**Single sign-on URL**:\n```text\n{samlAcs}\n```\n**Audience URI (SP Entity ID)**:\n```text\nActivepieces\n```\n", "IDP Metadata": "IDP Metadata", + "Paste the metadata XML contents or the metadata URL provided by your identity provider.": "Paste the metadata XML contents or the metadata URL provided by your identity provider.", "IDP Certificate": "IDP Certificate", "Unlock AI": "Unlock AI", "Set your AI providers so your users enjoy a seamless building experience with our universal AI pieces": "", diff --git a/packages/web/src/app/routes/connections/index.tsx b/packages/web/src/app/routes/connections/index.tsx index 0ee1b68efd1..8b18c7b16eb 100644 --- a/packages/web/src/app/routes/connections/index.tsx +++ b/packages/web/src/app/routes/connections/index.tsx @@ -100,6 +100,7 @@ function AppConnectionsPage() { displayName, }, extraKeys: [location.search, projectId], + showErrorDialog: true, }); const { mutateAsync: deleteConnections } = diff --git a/packages/web/src/app/routes/platform/security/secret-managers/index.tsx b/packages/web/src/app/routes/platform/security/secret-managers/index.tsx index c902b4876f4..5dc3c047616 100644 --- a/packages/web/src/app/routes/platform/security/secret-managers/index.tsx +++ b/packages/web/src/app/routes/platform/security/secret-managers/index.tsx @@ -41,6 +41,7 @@ const SecretManagersPage = () => { const { data: connections, isLoading: isLoadingConnections } = secretManagersHooks.useListSecretManagerConnections({ listForPlatform: true, + showErrorDialog: true, }); const { mutate: deleteConnection } = secretManagersHooks.useDeleteSecretManagerConnection(); diff --git a/packages/web/src/app/routes/platform/security/sso/saml-dialog.tsx b/packages/web/src/app/routes/platform/security/sso/saml-dialog.tsx index 5c238cc1f6c..6d7723b4781 100644 --- a/packages/web/src/app/routes/platform/security/sso/saml-dialog.tsx +++ b/packages/web/src/app/routes/platform/security/sso/saml-dialog.tsx @@ -22,8 +22,13 @@ import { DialogTitle, DialogTrigger, } from '@/components/ui/dialog'; -import { Form, FormField, FormItem, FormMessage } from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; +import { + Form, + FormDescription, + FormField, + FormItem, + FormMessage, +} from '@/components/ui/form'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { flagsHooks } from '@/hooks/flags-hooks'; @@ -141,14 +146,20 @@ Activepieces ( - + - + + {t( + 'Paste the metadata XML contents or the metadata URL provided by your identity provider.', + )} + )} diff --git a/packages/web/src/app/routes/platform/setup/connections/index.tsx b/packages/web/src/app/routes/platform/setup/connections/index.tsx index 2faaf00f9f2..05f170f3cb7 100644 --- a/packages/web/src/app/routes/platform/setup/connections/index.tsx +++ b/packages/web/src/app/routes/platform/setup/connections/index.tsx @@ -227,6 +227,7 @@ const GlobalConnectionsTable = () => { extraKeys: [location.search], staleTime: 0, gcTime: 0, + showErrorDialog: true, }); const userHasPermissionToWriteAppConnection = checkAccess( diff --git a/packages/web/src/features/agents/hooks/agent-hooks.ts b/packages/web/src/features/agents/hooks/agent-hooks.ts index 1a5731f3191..f6cff542eee 100644 --- a/packages/web/src/features/agents/hooks/agent-hooks.ts +++ b/packages/web/src/features/agents/hooks/agent-hooks.ts @@ -21,7 +21,6 @@ export const agentQueries = { projectId: projectId!, }); }, - meta: { showErrorDialog: true, loadSubsetOptions: {} }, }); }, }; diff --git a/packages/web/src/features/connections/hooks/app-connections-hooks.ts b/packages/web/src/features/connections/hooks/app-connections-hooks.ts index c87de8f0d14..e9286cba40d 100644 --- a/packages/web/src/features/connections/hooks/app-connections-hooks.ts +++ b/packages/web/src/features/connections/hooks/app-connections-hooks.ts @@ -268,6 +268,7 @@ type UseConnectionsProps = { enabled?: boolean; staleTime?: number; pieceAuth?: PieceAuthProperty | PieceAuthProperty[] | undefined; + showErrorDialog?: boolean; }; export const appConnectionsQueries = { @@ -277,10 +278,13 @@ export const appConnectionsQueries = { enabled, staleTime, pieceAuth, + showErrorDialog, }: UseConnectionsProps) => { return useQuery({ queryKey: ['app-connections', ...extraKeys], - meta: { showErrorDialog: true, loadSubsetOptions: {} }, + meta: showErrorDialog + ? { showErrorDialog: true, loadSubsetOptions: {} } + : undefined, queryFn: async () => { const connections = await appConnectionsApi.list(request); if (pieceAuth) { diff --git a/packages/web/src/features/connections/hooks/global-connections-hooks.ts b/packages/web/src/features/connections/hooks/global-connections-hooks.ts index 3f3f06836d7..6450106d71f 100644 --- a/packages/web/src/features/connections/hooks/global-connections-hooks.ts +++ b/packages/web/src/features/connections/hooks/global-connections-hooks.ts @@ -22,6 +22,7 @@ type UseGlobalConnectionsProps = { extraKeys: any[]; staleTime?: number; gcTime?: number; + showErrorDialog?: boolean; }; const GLOBAL_CONNECTIONS_QUERY_KEY = 'globalConnections'; @@ -35,6 +36,7 @@ export const globalConnectionsQueries = { extraKeys, staleTime, gcTime, + showErrorDialog, }: UseGlobalConnectionsProps) => { const { platform } = platformHooks.useCurrentPlatform(); return useQuery({ @@ -42,7 +44,9 @@ export const globalConnectionsQueries = { staleTime, gcTime, enabled: platform.plan.globalConnectionsEnabled, - meta: { showErrorDialog: true, loadSubsetOptions: {} }, + meta: showErrorDialog + ? { showErrorDialog: true, loadSubsetOptions: {} } + : undefined, queryFn: () => { return globalConnectionsApi.list(request); }, diff --git a/packages/web/src/features/flow-runs/api/flow-runs-api.ts b/packages/web/src/features/flow-runs/api/flow-runs-api.ts index e6f6d21b39a..e44cb4f208c 100644 --- a/packages/web/src/features/flow-runs/api/flow-runs-api.ts +++ b/packages/web/src/features/flow-runs/api/flow-runs-api.ts @@ -1,4 +1,6 @@ import { + CountFlowRunsByStatusRequest, + CountFlowRunsByStatusResponse, FlowRun, FlowRunWithRetryError, ListFlowRunsRequestQuery, @@ -24,6 +26,14 @@ export const flowRunsApi = { list(request: ListFlowRunsRequestQuery): Promise> { return api.get>('/v1/flow-runs', request); }, + countByStatus( + request: CountFlowRunsByStatusRequest, + ): Promise { + return api.get( + '/v1/flow-runs/count-by-status', + request, + ); + }, getPopulated(id: string): Promise { return api.get(`/v1/flow-runs/${id}`); }, diff --git a/packages/web/src/features/flow-runs/components/runs-table/index.tsx b/packages/web/src/features/flow-runs/components/runs-table/index.tsx index 293382ec298..55cf8284adf 100644 --- a/packages/web/src/features/flow-runs/components/runs-table/index.tsx +++ b/packages/web/src/features/flow-runs/components/runs-table/index.tsx @@ -40,7 +40,11 @@ import { DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { flowRunsApi } from '@/features/flow-runs/api/flow-runs-api'; -import { flowRunMutations } from '@/features/flow-runs/hooks/flow-run-hooks'; +import { + DEFAULT_DATE_PRESET, + flowRunMutations, + flowRunQueries, +} from '@/features/flow-runs/hooks/flow-run-hooks'; import { flowRunUtils } from '@/features/flow-runs/utils/flow-run-utils'; import { flowHooks } from '@/features/flows/hooks/flow-hooks'; import { useAuthorization } from '@/hooks/authorization-hooks'; @@ -54,6 +58,7 @@ import { RetriedRunsSnackbar, RUN_IDS_QUERY_PARAM, } from './retried-runs-snackbar'; +import { RunsRefreshButton, RunsStatusChart } from './runs-status-chart'; type SelectedRow = { id: string; @@ -92,6 +97,8 @@ export const RunsTable = () => { setHasSeededDefaultRange(true); }, [hasSeededDefaultRange, setSearchParams]); + const { refetch: statsRefetch, dataUpdatedAt } = flowRunQueries.useRunStats(); + const { data, isLoading, refetch } = useQuery({ queryKey: ['flow-run-table', searchParams.toString(), projectId], enabled: hasSeededDefaultRange, @@ -559,6 +566,15 @@ export const RunsTable = () => { bulkActions={bulkActions} onRowClick={(row, newWindow) => handleRowClick(row, newWindow)} customFilters={customFilters} + toolbarButtons={[ + , + , + ]} hidePagination={retriedRunsInQueryParams.length > 0} /> { ); }; - -const DEFAULT_DATE_PRESET = '7days' as const; diff --git a/packages/web/src/features/flow-runs/components/runs-table/runs-status-chart.tsx b/packages/web/src/features/flow-runs/components/runs-table/runs-status-chart.tsx new file mode 100644 index 00000000000..2068ab99d90 --- /dev/null +++ b/packages/web/src/features/flow-runs/components/runs-table/runs-status-chart.tsx @@ -0,0 +1,205 @@ +import dayjs from 'dayjs'; +import { t } from 'i18next'; +import { CircleHelp, RefreshCcw } from 'lucide-react'; +import { useEffect, useState, useCallback } from 'react'; + +import { Button } from '@/components/ui/button'; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@/components/ui/tooltip'; +import { + flowRunQueries, + RunStatusCategory, +} from '@/features/flow-runs/hooks/flow-run-hooks'; +import { formatUtils } from '@/lib/format-utils'; +import { cn } from '@/lib/utils'; + +const DONUT_SIZE = 20; +const DONUT_RADIUS = 6; +const DONUT_STROKE = 2.5; +const DONUT_CIRCUMFERENCE = 2 * Math.PI * DONUT_RADIUS; +const DONUT_CENTER = DONUT_SIZE / 2; + +function MiniDonut({ + categories, + total, +}: { + categories: RunStatusCategory[]; + total: number; +}) { + let accumulated = 0; + return ( + + {categories.map((cat) => { + const segmentLength = (cat.count / total) * DONUT_CIRCUMFERENCE; + const offset = DONUT_CIRCUMFERENCE - accumulated; + accumulated += segmentLength; + return ( + + ); + })} + + ); +} + +function RunsStatusChart() { + const { categories, total, isLoading } = flowRunQueries.useRunStats(); + const [open, setOpen] = useState(false); + const [isVisible, setIsVisible] = useState(false); + + useEffect(() => { + const timer = setTimeout(() => setOpen(true), 500); + return () => clearTimeout(timer); + }, []); + + const handleOpenChange = useCallback((v: boolean) => { + setOpen(v); + if (!v) { + setIsVisible(false); + } + }, []); + + useEffect(() => { + if (open) { + requestAnimationFrame(() => setIsVisible(true)); + } + }, [open]); + + if (isLoading || categories.length === 0) return; + return ( + + + + + +
+
+
+

{t('Current Queue Status')}

+ + + + + + {t('Showing results from the last 7 days')} + + +
+

+ {t('Total Runs')}: {formatUtils.formatNumberCompact(total)} +

+
+ + {total > 0 && ( +
+ {categories.map((cat) => ( +
+ ))} +
+ )} + +
+ {categories.map((cat) => ( +
+
+
+ + {formatUtils.convertEnumToHumanReadable(cat.label)} + +
+ + {formatUtils.formatNumberCompact(cat.count)} + +
+ ))} +
+
+ + + ); +} + +function RunsRefreshButton({ + statsRefetch, + tableRefetch, + dataUpdatedAt, +}: { + statsRefetch: () => void; + tableRefetch: () => void; + dataUpdatedAt: number; +}) { + const [isRefreshing, setIsRefreshing] = useState(false); + + const handleRefresh = useCallback(() => { + setIsRefreshing(true); + statsRefetch(); + tableRefetch(); + setTimeout(() => setIsRefreshing(false), 1000); + }, [statsRefetch, tableRefetch]); + + return ( +
+ + {t('Updated')} {dayjs(dataUpdatedAt).format('MMM DD, hh:mm A')} + + + + + + {t('Refresh data')} + +
+ ); +} + +export { RunsStatusChart, RunsRefreshButton }; diff --git a/packages/web/src/features/flow-runs/hooks/flow-run-hooks.ts b/packages/web/src/features/flow-runs/hooks/flow-run-hooks.ts index e686b1d138a..4758ed3e45f 100644 --- a/packages/web/src/features/flow-runs/hooks/flow-run-hooks.ts +++ b/packages/web/src/features/flow-runs/hooks/flow-run-hooks.ts @@ -4,6 +4,8 @@ import { BulkArchiveActionOnRunsRequestBody, BulkCancelFlowRequestBody, ErrorCode, + FlowRunCountByStatus, + FlowRunStatus, FlowRetryStrategy, FlowRun, FlowRunWithRetryError, @@ -11,11 +13,14 @@ import { } from '@activepieces/shared'; import { useMutation, useQuery } from '@tanstack/react-query'; import { t } from 'i18next'; +import { useMemo } from 'react'; import { toast } from 'sonner'; +import { getDefaultRange } from '@/components/custom/date-time-picker-range'; import { internalErrorToast } from '@/components/ui/sonner'; import { flowsApi } from '@/features/flows/api/flows-api'; import { api } from '@/lib/api'; +import { authenticationSession } from '@/lib/authentication-session'; import { flowRunsApi } from '../api/flow-runs-api'; @@ -23,15 +28,95 @@ export const flowRunKeys = { detail: (runId: string) => ['flow-run', runId] as const, }; +const STATUS_CATEGORIES = [ + { + label: 'Succeeded', + statuses: [FlowRunStatus.SUCCEEDED], + color: 'hsl(var(--success))', + }, + { + label: 'Failed', + statuses: [ + FlowRunStatus.FAILED, + FlowRunStatus.INTERNAL_ERROR, + FlowRunStatus.TIMEOUT, + FlowRunStatus.MEMORY_LIMIT_EXCEEDED, + FlowRunStatus.QUOTA_EXCEEDED, + FlowRunStatus.LOG_SIZE_EXCEEDED, + ], + color: 'hsl(var(--destructive))', + }, + { + label: 'Running', + statuses: [FlowRunStatus.RUNNING], + color: 'hsl(var(--primary))', + }, + { + label: 'Queued', + statuses: [FlowRunStatus.QUEUED], + color: 'var(--muted-foreground)', + }, + { + label: 'Paused', + statuses: [FlowRunStatus.PAUSED], + color: 'hsl(var(--warning))', + }, + { + label: 'Canceled', + statuses: [FlowRunStatus.CANCELED], + color: 'var(--muted-foreground)', + }, +] as const; + +function groupByCategory(data: FlowRunCountByStatus[]) { + const statusToCount = new Map(data.map((d) => [d.status, d.count])); + return STATUS_CATEGORIES.map((cat) => ({ + label: cat.label, + color: cat.color, + count: cat.statuses.reduce( + (sum, s) => sum + (statusToCount.get(s) ?? 0), + 0, + ), + })).filter((cat) => cat.count > 0); +} + +export const DEFAULT_DATE_PRESET = '7days' as const; + export const flowRunQueries = { useFlowRun: (runId: string) => useQuery({ queryKey: flowRunKeys.detail(runId), queryFn: () => flowRunsApi.getPopulated(runId), - refetchInterval: 15000, + refetchInterval: 7000, }), + useRunStats: () => { + const projectId = authenticationSession.getProjectId()!; + + const { data, isLoading, dataUpdatedAt, refetch } = useQuery({ + queryKey: ['flow-run-count-by-status', projectId], + queryFn: () => { + const range = getDefaultRange(DEFAULT_DATE_PRESET); + return flowRunsApi.countByStatus({ + projectId, + createdAfter: range.from.toISOString(), + createdBefore: range.to.toISOString(), + }); + }, + refetchInterval: 15000, + }); + + const categories = useMemo(() => groupByCategory(data?.data ?? []), [data]); + const total = useMemo( + () => categories.reduce((sum, c) => sum + c.count, 0), + [categories], + ); + + return { categories, total, isLoading, dataUpdatedAt, refetch }; + }, }; +export type RunStatusCategory = ReturnType[number]; + export const flowRunMutations = { useRetryRun: ({ onSuccess, diff --git a/packages/web/src/features/folders/hooks/folders-hooks.ts b/packages/web/src/features/folders/hooks/folders-hooks.ts index 75d06b4c033..5e63c8be98e 100644 --- a/packages/web/src/features/folders/hooks/folders-hooks.ts +++ b/packages/web/src/features/folders/hooks/folders-hooks.ts @@ -10,7 +10,6 @@ export const foldersHooks = { const folderQuery = useQuery({ queryKey: ['folders', authenticationSession.getProjectId()], queryFn: () => foldersApi.list(), - meta: { showErrorDialog: true, loadSubsetOptions: {} }, }); return { folders: folderQuery.data, diff --git a/packages/web/src/features/members/hooks/project-members-hooks.ts b/packages/web/src/features/members/hooks/project-members-hooks.ts index afb18a358f8..ec8feb05ea4 100644 --- a/packages/web/src/features/members/hooks/project-members-hooks.ts +++ b/packages/web/src/features/members/hooks/project-members-hooks.ts @@ -17,7 +17,6 @@ export const projectMembersHooks = { const { platform } = platformHooks.useCurrentPlatform(); const query = useQuery({ queryKey: ['project-members', authenticationSession.getProjectId()], - meta: { showErrorDialog: true, loadSubsetOptions: {} }, queryFn: async () => { const projectId = authenticationSession.getProjectId(); assertNotNullOrUndefined(projectId, 'Project ID is null'); diff --git a/packages/web/src/features/members/hooks/user-invitations-hooks.ts b/packages/web/src/features/members/hooks/user-invitations-hooks.ts index 81b401ca64f..a5e26d133d1 100644 --- a/packages/web/src/features/members/hooks/user-invitations-hooks.ts +++ b/packages/web/src/features/members/hooks/user-invitations-hooks.ts @@ -19,7 +19,6 @@ export const userInvitationsHooks = { }, queryKey: [userInvitationsQueryKey], staleTime: 0, - meta: { showErrorDialog: true, loadSubsetOptions: {} }, }); return { invitations: query.data, diff --git a/packages/web/src/features/platform-admin/hooks/analytics-hooks.ts b/packages/web/src/features/platform-admin/hooks/analytics-hooks.ts index 8e236f25591..162bbf07c24 100644 --- a/packages/web/src/features/platform-admin/hooks/analytics-hooks.ts +++ b/packages/web/src/features/platform-admin/hooks/analytics-hooks.ts @@ -31,7 +31,6 @@ export const platformAnalyticsHooks = { queryKey: userLeaderboardQueryKey(timePeriod), queryFn: () => analyticsApi.getUserLeaderboard(timePeriod), enabled: platform.plan.analyticsEnabled, - meta: { showErrorDialog: true, loadSubsetOptions: {} }, }); return { @@ -48,7 +47,6 @@ export const platformAnalyticsHooks = { queryKey: projectLeaderboardQueryKey(timePeriod), queryFn: () => analyticsApi.getProjectLeaderboard(timePeriod), enabled: platform.plan.analyticsEnabled, - meta: { showErrorDialog: true, loadSubsetOptions: {} }, }); return { diff --git a/packages/web/src/features/secret-managers/hooks/secret-managers-hooks.ts b/packages/web/src/features/secret-managers/hooks/secret-managers-hooks.ts index 810319c83e6..b80aacefc66 100644 --- a/packages/web/src/features/secret-managers/hooks/secret-managers-hooks.ts +++ b/packages/web/src/features/secret-managers/hooks/secret-managers-hooks.ts @@ -15,7 +15,12 @@ export const secretManagersHooks = { useListSecretManagerConnections: ({ connectedOnly, listForPlatform, - }: { connectedOnly?: boolean; listForPlatform?: boolean } = {}) => { + showErrorDialog, + }: { + connectedOnly?: boolean; + listForPlatform?: boolean; + showErrorDialog?: boolean; + } = {}) => { const { platform } = platformHooks.useCurrentPlatform(); const projectId = listForPlatform ? undefined @@ -32,7 +37,9 @@ export const secretManagersHooks = { return result.data; }, enabled: platform.plan.secretManagersEnabled, - meta: { showErrorDialog: true, loadSubsetOptions: {} }, + meta: showErrorDialog + ? { showErrorDialog: true, loadSubsetOptions: {} } + : undefined, }); }, useCreateSecretManagerConnection: ({ diff --git a/packages/web/src/features/tables/hooks/table-hooks.ts b/packages/web/src/features/tables/hooks/table-hooks.ts index dc9a3fa435a..131468f0b76 100644 --- a/packages/web/src/features/tables/hooks/table-hooks.ts +++ b/packages/web/src/features/tables/hooks/table-hooks.ts @@ -5,7 +5,7 @@ import { Table, UncategorizedFolderId, } from '@activepieces/shared'; -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { authenticationSession } from '@/lib/authentication-session'; @@ -34,25 +34,6 @@ export const tableMutations = { }; export const tableHooks = { - useTables: (limit?: number) => { - const projectId = authenticationSession.getProjectId() ?? ''; - const [searchParams] = useSearchParams(); - return useQuery({ - queryKey: ['tables', searchParams.toString(), projectId], - queryFn: () => - tablesApi.list({ - projectId, - cursor: searchParams.get('cursor') ?? undefined, - limit: limit - ? limit - : searchParams.get('limit') - ? parseInt(searchParams.get('limit')!) - : undefined, - name: searchParams.get('name') ?? undefined, - }), - meta: { showErrorDialog: true, loadSubsetOptions: {} }, - }); - }, useCreateTable: (folderId: string) => { const projectId = authenticationSession.getProjectId() ?? ''; const navigate = useNavigate(); diff --git a/packages/web/src/features/templates/hooks/templates-hook.ts b/packages/web/src/features/templates/hooks/templates-hook.ts index 2b1aa5c0e8c..752bb3b3a4e 100644 --- a/packages/web/src/features/templates/hooks/templates-hook.ts +++ b/packages/web/src/features/templates/hooks/templates-hook.ts @@ -36,7 +36,6 @@ export const templatesHooks = { return result.data; }, staleTime: 5 * 60 * 1000, - meta: { showErrorDialog: true, loadSubsetOptions: {} }, }); }, @@ -59,7 +58,6 @@ export const templatesHooks = { return templates.data; }, staleTime: 5 * 60 * 1000, - meta: { showErrorDialog: true, loadSubsetOptions: {} }, }); const setSearch = (newSearch: string) => { diff --git a/packages/web/src/lib/format-utils.ts b/packages/web/src/lib/format-utils.ts index 9be5db28cdc..a45aef39159 100644 --- a/packages/web/src/lib/format-utils.ts +++ b/packages/web/src/lib/format-utils.ts @@ -26,6 +26,12 @@ export const formatUtils = { formatNumber(number: number) { return new Intl.NumberFormat(i18next.language).format(number); }, + formatNumberCompact(number: number) { + return new Intl.NumberFormat(i18next.language, { + notation: 'compact', + maximumFractionDigits: 1, + }).format(number); + }, formatDateOnlyOrFail(date: Date, fallback: string) { try { return this.formatDateOnly(date);