Skip to content

feat: redesign integration upload with template selection#804

Merged
Israeltheminer merged 3 commits into
mainfrom
feat/integration-upload-templates
Mar 17, 2026
Merged

feat: redesign integration upload with template selection#804
Israeltheminer merged 3 commits into
mainfrom
feat/integration-upload-templates

Conversation

@Israeltheminer

@Israeltheminer Israeltheminer commented Mar 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add template-based integration creation flow with pre-built templates grid (GitHub, Slack, Gmail, Outlook, Teams, Shopify, etc.)
  • Templates auto-fetch config, connector, and icon from GitHub examples/integrations/
  • Tabbed dialog with "Template" and "Upload" options for adding integrations
  • Inline delete section in manage dialog using shared DeleteDialog component
  • Improved credentials form layout
  • Updated integration configs with expanded operation definitions and capabilities

Test plan

  • Open "Add integration" → verify both Template and Upload tabs work
  • Select a template (e.g., GitHub) → verify config, connector, and icon load correctly
  • Upload a custom .zip package → verify manual upload still works
  • Open integration manage dialog → verify delete button and credentials form
  • Run tests: bun run --filter @tale/platform test

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

New Features

  • Added integration templates for quick setup of popular services (GitHub, Slack, Discord, Gmail, Outlook, Teams, Shopify, Twilio, and more).
  • Enabled sync capability configuration with frequency settings for integrations.

Improvements

  • Revamped integration upload dialog with tabbed navigation for custom uploads or template selection.
  • Enhanced delete confirmation flow with dedicated dialog component.
  • Improved OAuth2 credential management UI with relocated edit controls.

…improvements

Add template-based integration creation flow alongside the existing manual upload:
- New template step with pre-built integrations grid (GitHub, Slack, Gmail, etc.)
- Templates fetch config.json, connector, and icon from GitHub examples
- Tabbed dialog with "Template" and "Upload" options
- Inline delete section in manage dialog using shared DeleteDialog component
- Improved credentials form layout
- Updated integration configs with expanded operation definitions

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai

coderabbitai Bot commented Mar 17, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This change introduces integration template support and extends synchronization capabilities across the platform. It adds a new capabilities object (with canSync and syncFrequency fields) to 9 integration configuration files, refactors the integration upload dialog to include a template selection tab alongside the existing upload step, replaces the inline delete confirmation UI with a dedicated DeleteDialog component, relocates OAuth2 credential editing from a summary button to a header IconButton, and introduces new utilities for fetching and parsing integration templates from a remote repository. Localization strings are expanded to support OAuth status flows and template-driven UI interactions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

The repetitive capabilities additions across nine homogeneous JSON config files are straightforward to review, but the new template system (template fetching with caching, template selection UI, validation schema updates) and UI refactorings (delete dialog, OAuth2 button relocation, tab-based upload flow) require careful examination of logic correctness, state management, and integration points across multiple components.

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: redesign integration upload with template selection' directly and clearly describes the main change: adding template-based selection to the integration upload flow.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/integration-upload-templates
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can use oxc to improve the quality of JavaScript and TypeScript code reviews.

Add a configuration file to your project to customize how CodeRabbit runs oxc.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@services/platform/app/features/settings/integrations/components/integration-upload/constants/integration-templates.ts`:
- Around line 1-2: The GITHUB_RAW_BASE constant currently points at the mutable
"main" branch which can drift; update the constant GITHUB_RAW_BASE (or make it
configurable) to reference an immutable ref (a specific commit SHA or release
tag) or use a server-provided versioned ref so template URLs are pinned and
cannot change without a deploy; replace the "main" path segment with the chosen
tag/SHA or wire the value to a config/env variable that the server supplies.

In
`@services/platform/app/features/settings/integrations/components/integration-upload/utils/fetch-template-files.ts`:
- Around line 24-28: Replace the Promise.all(fetch...) call that produces
configResp, connectorResp, and iconResp with Promise.allSettled so network/CORS
rejections are captured; then inspect each settled result (configResp,
connectorResp, iconResp) and convert rejected entries into appropriate
ParseResult error objects or null responses before proceeding (preserve the
function's ParseResult contract). In the function fetchTemplateFiles (and any
helper methods it calls), handle each settled entry: if status === "fulfilled"
use the Response, if "rejected" create a structured error outcome (including the
error message) and ensure the function returns that ParseResult instead of
throwing. Apply the identical allSettled + explicit handling pattern to the
later connector and icon fetch code paths referenced around the original
connector rejection handling (lines ~51-56) so no fetch rejection can escape as
an unhandled promise rejection.

In
`@services/platform/app/features/settings/integrations/components/integration-upload/utils/validate-config.ts`:
- Around line 86-93: The capabilities schema currently allows any string for
syncFrequency and doesn't enforce that canSync implies a schedule; update the
zod schema(s) where capabilities is defined (the capabilities: z.object({...})
block and the similar block at lines 104-116) to (1) validate syncFrequency
against a strict schedule format (e.g., a cron/recurrence regex or ISO-8601
interval pattern) instead of z.string().optional(), and (2) add a refinement or
.superRefine on the capabilities object to require that when canSync is true
then syncFrequency is present and valid (and optionally forbid syncFrequency
when canSync is false if desired). Use the existing schema symbol names
(capabilities, syncFrequency, canSync) so the change is easy to locate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e082c216-3491-406b-a151-258c12b236fc

📥 Commits

Reviewing files that changed from the base of the PR and between 64c2712 and 0008e34.

📒 Files selected for processing (23)
  • examples/integrations/circuly/config.json
  • examples/integrations/discord/config.json
  • examples/integrations/github/config.json
  • examples/integrations/gmail/config.json
  • examples/integrations/outlook/config.json
  • examples/integrations/protel/config.json
  • examples/integrations/shopify/config.json
  • examples/integrations/slack/config.json
  • examples/integrations/teams/config.json
  • examples/integrations/twilio/config.json
  • services/platform/app/features/settings/integrations/components/integration-manage-dialog.tsx
  • services/platform/app/features/settings/integrations/components/integration-manage/integration-credentials-form.tsx
  • services/platform/app/features/settings/integrations/components/integration-manage/integration-delete-section.tsx
  • services/platform/app/features/settings/integrations/components/integration-upload/constants/integration-templates.ts
  • services/platform/app/features/settings/integrations/components/integration-upload/hooks/use-upload-integration.ts
  • services/platform/app/features/settings/integrations/components/integration-upload/integration-upload-dialog.tsx
  • services/platform/app/features/settings/integrations/components/integration-upload/steps/template-step.tsx
  • services/platform/app/features/settings/integrations/components/integration-upload/steps/upload-step.tsx
  • services/platform/app/features/settings/integrations/components/integration-upload/utils/__tests__/fetch-template-files.test.ts
  • services/platform/app/features/settings/integrations/components/integration-upload/utils/fetch-template-files.ts
  • services/platform/app/features/settings/integrations/components/integration-upload/utils/validate-config.ts
  • services/platform/app/features/settings/integrations/components/integrations.tsx
  • services/platform/messages/en.json
💤 Files with no reviewable changes (1)
  • services/platform/app/features/settings/integrations/components/integration-manage/integration-delete-section.tsx

Comment on lines +24 to +28
const [configResp, connectorResp, iconResp] = await Promise.all([
fetch(configUrl),
template.type !== 'sql' ? fetch(connectorUrl) : Promise.resolve(null),
fetch(iconUrl),
]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "fetch-template-files.ts" -type f

Repository: tale-project/tale

Length of output: 175


🏁 Script executed:

cat -n services/platform/app/features/settings/integrations/components/integration-upload/utils/fetch-template-files.ts

Repository: tale-project/tale

Length of output: 2545


Handle network/CORS rejections in Promise.all() to maintain structured error responses.

Line 24 uses Promise.all() over fetch(), which throws an unhandled rejection on network or CORS errors. The function contract requires always returning ParseResult, but unhandled rejections bypass this. Even the optional icon fetch can crash the template flow if the network request fails.

Switch to Promise.allSettled() to capture both resolved responses and rejections, then handle each explicitly:

Suggested approach
-  const [configResp, connectorResp, iconResp] = await Promise.all([
+  const [configResult, connectorResult, iconResult] = await Promise.allSettled([
     fetch(configUrl),
     template.type !== 'sql' ? fetch(connectorUrl) : Promise.resolve(null),
     fetch(iconUrl),
   ]);
+
+  if (configResult.status === 'rejected') {
+    return {
+      success: false,
+      error: 'Failed to fetch template configuration from GitHub',
+    };
+  }
+
+  const configResp = configResult.value;
+  const connectorResp =
+    connectorResult.status === 'fulfilled' ? connectorResult.value : null;
+  const iconResp = iconResult.status === 'fulfilled' ? iconResult.value : null;

Apply the same pattern to lines 51-56 (connector rejection handling) and icon handling.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const [configResp, connectorResp, iconResp] = await Promise.all([
fetch(configUrl),
template.type !== 'sql' ? fetch(connectorUrl) : Promise.resolve(null),
fetch(iconUrl),
]);
const [configResult, connectorResult, iconResult] = await Promise.allSettled([
fetch(configUrl),
template.type !== 'sql' ? fetch(connectorUrl) : Promise.resolve(null),
fetch(iconUrl),
]);
if (configResult.status === 'rejected') {
return {
success: false,
error: 'Failed to fetch template configuration from GitHub',
};
}
const configResp = configResult.value;
const connectorResp =
connectorResult.status === 'fulfilled' ? connectorResult.value : null;
const iconResp = iconResult.status === 'fulfilled' ? iconResult.value : null;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@services/platform/app/features/settings/integrations/components/integration-upload/utils/fetch-template-files.ts`
around lines 24 - 28, Replace the Promise.all(fetch...) call that produces
configResp, connectorResp, and iconResp with Promise.allSettled so network/CORS
rejections are captured; then inspect each settled result (configResp,
connectorResp, iconResp) and convert rejected entries into appropriate
ParseResult error objects or null responses before proceeding (preserve the
function's ParseResult contract). In the function fetchTemplateFiles (and any
helper methods it calls), handle each settled entry: if status === "fulfilled"
use the Response, if "rejected" create a structured error outcome (including the
error message) and ensure the function returns that ParseResult instead of
throwing. Apply the identical allSettled + explicit handling pattern to the
later connector and icon fetch code paths referenced around the original
connector rejection handling (lines ~51-56) so no fetch rejection can escape as
an unhandled promise rejection.

Comment on lines +86 to +93
capabilities: z
.object({
canSync: z.boolean().optional(),
canPush: z.boolean().optional(),
canWebhook: z.boolean().optional(),
syncFrequency: z.string().optional(),
})
.optional(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate capabilities.syncFrequency and enforce canSync dependency.

Right now, any string is accepted for syncFrequency (Line 91), and canSync: true does not require a schedule. This can allow invalid runtime sync configs through validation.

🔧 Proposed fix
+const cron5FieldPattern = /^\S+\s+\S+\s+\S+\s+\S+\s+\S+$/;
+
 export const integrationConfigSchema = z
   .object({
@@
     capabilities: z
       .object({
         canSync: z.boolean().optional(),
         canPush: z.boolean().optional(),
         canWebhook: z.boolean().optional(),
-        syncFrequency: z.string().optional(),
+        syncFrequency: z
+          .string()
+          .trim()
+          .regex(
+            cron5FieldPattern,
+            'syncFrequency must be a 5-field cron expression',
+          )
+          .optional(),
       })
       .optional(),
   })
@@
   .superRefine((data, ctx) => {
+    if (data.capabilities?.canSync && !data.capabilities.syncFrequency) {
+      ctx.addIssue({
+        code: z.ZodIssueCode.custom,
+        path: ['capabilities', 'syncFrequency'],
+        message: 'syncFrequency is required when capabilities.canSync is true',
+      });
+    }
+
     if (data.type === 'sql') {
       data.operations.forEach((op, index) => {

Also applies to: 104-116

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@services/platform/app/features/settings/integrations/components/integration-upload/utils/validate-config.ts`
around lines 86 - 93, The capabilities schema currently allows any string for
syncFrequency and doesn't enforce that canSync implies a schedule; update the
zod schema(s) where capabilities is defined (the capabilities: z.object({...})
block and the similar block at lines 104-116) to (1) validate syncFrequency
against a strict schedule format (e.g., a cron/recurrence regex or ISO-8601
interval pattern) instead of z.string().optional(), and (2) add a refinement or
.superRefine on the capabilities object to require that when canSync is true
then syncFrequency is present and valid (and optionally forbid syncFrequency
when canSync is false if desired). Use the existing schema symbol names
(capabilities, syncFrequency, canSync) so the change is easy to locate.

- Use Promise.allSettled() instead of Promise.all() in fetchTemplateFiles
  to handle network/CORS rejections gracefully instead of throwing
- Validate syncFrequency as a 5-field cron expression and require
  canSync: true when syncFrequency is set
- Extract TEMPLATES_REF constant for template source pinning
@Israeltheminer Israeltheminer merged commit dc28e21 into main Mar 17, 2026
17 checks passed
@Israeltheminer Israeltheminer deleted the feat/integration-upload-templates branch March 17, 2026 10:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant