Skip to content

feat(ingestion): add Figma connector (ui-design, bronze-only) - #1306

Merged
mitasovr merged 3 commits into
constructorfabric:mainfrom
mitasovr:feat/figma-connector
Jun 15, 2026
Merged

feat(ingestion): add Figma connector (ui-design, bronze-only)#1306
mitasovr merged 3 commits into
constructorfabric:mainfrom
mitasovr:feat/figma-connector

Conversation

@mitasovr

@mitasovr mitasovr commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Summary

New nocode declarative connector for the Figma REST API v1 — first connector in the ui-design category. Bronze-only by design: five raw streams land in bronze_figma, the dbt step is RMT promotion only (figma__bronze_promoted). Silver (class_design_activity) is a deliberate follow-up.

Stream Endpoint Sync mode
design_projects GET /v1/teams/{id}/projects (per configured team) full refresh
design_files GET /v1/projects/{id}/files incremental, client-side cursor on last_modified
design_file_meta GET /v1/files/{key}/meta (creator, last_touched_by, editor_type) full refresh (substream)
design_file_versions GET /v1/files/{key}/versions (paginated, bounded by figma_start_date) full refresh (substream)
design_file_comments GET /v1/files/{key}/comments (replies via parent_comment_id) full refresh (substream)

API constraints encoded in the manifest

Verified against the official OpenAPI spec (figma/rest-api-spec, June 2026):

  • No team / member enumeration in the public REST API — figma_team_ids is required config (taken from team URLs); User objects carry only id/handle, never email. Identity resolution is deferred to dbt (handle matching via Identity Manager, or Enterprise SCIM later).
  • Tiered rate limits bound to the token owner's seat type. All five endpoints used are Tier 2/3; the Tier 1 full-document endpoint (GET /v1/files/{key}, 6 req/month on viewer seats) is deliberately not used — design content never leaves Figma.
  • 429 honours Retry-After (600 s cap, confluence pattern); file-level 403/404 are IGNOREd (invite-only projects, deleted files are routine), team-level ones FAIL the run (bad token/team id).
  • Manifest is fully inlined (no $refs except schemas — inline substream parents, inline auth) and passes both validate-strict and validate, including the stricter post-drift :latest CDK image.

Docs

docs/components/connectors/ui-design/README.md and figma/figma.md corrected to v1.1: the March draft assumed a nonexistent GET /v1/teams/{id}/members endpoint and user emails in version/comment payloads. The two large PRDs under ui-design/*/specs/ still carry the stale assumption — flagged for a separate pass.

Tooling fix (first commit)

generate-catalog.sh and generate-schema.sh resolved INGESTION_DIR as SCRIPT_DIR/.. while living in tools/declarative-connector/ — every invocation failed with tools/tools/... path. Fixed to ../...

Test plan

  • source.sh validate-strict ui-design/figma — Builder-UI compatible
  • source.sh validate ui-design/figma — CDK runtime valid
  • check against a live workspace — credentials/team id OK
  • discover — 5 streams, design_files incremental with last_modified cursor
  • per-stream read — 0 errors, every record carries tenant_id/source_id/unique_key; extra_fields denormalization (project_name, team_id) confirmed working
  • resume read — cursor advances (2020-01-01 → file's last_modified); boundary record re-emission deduped by RMT
  • empty versions/comments cross-checked against the raw API (genuine — 30-day version retention on Starter plan, untouched file)
  • e2e on a workspace with richer data (versions/comments) before production rollout

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a Figma UI design connector for raw extraction of projects, files, metadata, versions, and comments; bronze tables and dbt source/staging/promotion artifacts included.
    • Connector uses personal access token (PAT) auth and emits standardized tenant/source identifiers.
  • Documentation

    • Detailed setup guide, config fields, Kubernetes Secret template, API endpoint/limitation and identity-resolution guidance (SCIM for Enterprise; handle matching otherwise), and rate-limit behavior.
  • Chores

    • Fixed connector tooling path resolution for manifest/schema generation.

Roman Mitasov and others added 2 commits June 11, 2026 23:34
…te-schema.sh

Both scripts live in tools/declarative-connector/ but resolved
INGESTION_DIR as SCRIPT_DIR/.., which points at tools/ and breaks every
invocation (./tools/tools/declarative-connector/source.sh not found).
Resolve two levels up instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Roman Mitasov <Roman.Mitasov@constructor.tech>
Nocode declarative connector for the Figma REST API v1. Five raw
streams into bronze_figma: design_projects, design_files (client-side
incremental on last_modified), design_file_meta, design_file_versions
(start-date bounded), design_file_comments. dbt step is RMT promotion
only — Silver (class_design_activity) is a follow-up.

API facts verified against figma/rest-api-spec and encoded in the
manifest:
- no team or member enumeration endpoints (team IDs are required
  config; User objects carry id/handle only, never email — identity
  resolution deferred to dbt via handle matching or Enterprise SCIM)
- per-endpoint rate-limit tiers bound to the token owner's seat type;
  the Tier 1 full-document endpoint is deliberately not used
- 429 honours Retry-After (600s cap); file-level 403/404 are IGNOREd
  (invite-only projects, deleted files), team-level ones FAIL the run

Manifest is fully inlined (no $refs except schemas) and passes both
validate-strict and validate. Live-tested against a real workspace:
check/discover/per-stream read green, mandatory fields present,
incremental cursor advances on resume; empty versions/comments
confirmed as genuine API responses (30-day version retention on
Starter plan).

Docs: ui-design domain spec and figma.md corrected to v1.1 — the
March draft assumed a nonexistent GET /v1/teams/{id}/members endpoint
and user emails in version/comment payloads.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Roman Mitasov <Roman.Mitasov@constructor.tech>
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f892f9f5-bbbf-4349-8fa3-b393e0ab5778

📥 Commits

Reviewing files that changed from the base of the PR and between caf548a and e8b3f0c.

📒 Files selected for processing (3)
  • docs/components/connectors/ui-design/README.md
  • docs/components/connectors/ui-design/figma/figma.md
  • src/ingestion/connectors/ui-design/figma/connector.yaml
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/components/connectors/ui-design/README.md
  • docs/components/connectors/ui-design/figma/figma.md
  • src/ingestion/connectors/ui-design/figma/connector.yaml

📝 Walkthrough

Walkthrough

This PR adds a complete Figma UI design connector to the Insight platform. It defines five bronze-only streams (projects, files, metadata, versions, comments) extracted via Figma REST API v1, with identity resolution deferred to Silver and dbt-driven activity aggregation. Includes verified API specifications, declarative stream manifests with pagination and incremental sync, dbt promotion models, deployment templates, and build script fixes.

Changes

Figma Connector Implementation

Layer / File(s) Summary
API Specification & Design Verification
docs/components/connectors/ui-design/figma/figma.md
Figma REST API v1 specification (v1.1, June 2026) with verified endpoints, PAT auth via X-Figma-Token, rate-limit tiers, and corrected identity/activity behavior: bronze-only streams; no email in REST authors; team enumeration manual via config; activity derived in dbt.
Connector Overview & Architecture
docs/components/connectors/ui-design/README.md
High-level connector spec updated with v1.1 corrections: clarifies design_file_activity as dbt-derived (not connector-emitted); documents implemented bronze substreams with sync modes; revises identity resolution (SCIM first, then handle-based fallback).
Stream Definitions & Output Schemas
src/ingestion/connectors/ui-design/figma/connector.yaml
Five declarative streams with JSON output schemas: design_projects (team-partitioned), design_files (incremental by last_modified), design_file_meta, design_file_versions (cursor-paginated, date-bounded), design_file_comments (with parent/reply structure). Includes auth, error handling (per-file 403/404 ignored, team-level failures propagate), and unique key standardization.
Connector Configuration & Descriptor
src/ingestion/connectors/ui-design/figma/descriptor.yaml
Connection specification and descriptor: required figma_token, figma_team_ids, optional figma_start_date (default 2020-01-01) and figma_page_size (1–50, default 50); descriptor defines nocode connector v1.0.0 with cron schedule and dbt figma+ tag selection; concurrency and validation constraints set.
dbt Bronze Transformation & Schema
src/ingestion/connectors/ui-design/figma/dbt/figma__bronze_promoted.sql, src/ingestion/connectors/ui-design/figma/dbt/schema.yml
dbt source schema (bronze_figma) enumerates raw tables; promotion view calls promote_bronze_to_rmt macro for idempotent RMT layer bootstrap; notes identity constraints and dbt-driven activity construction.
Deployment Documentation & Secret Template
src/ingestion/connectors/ui-design/figma/README.md, src/ingestion/secrets/connectors/figma.yaml.example
Connector README: scope (REST API v1 projects/files/meta/versions/comments), bronze-only status, prerequisites (PAT scopes, seat-rate constraints), config field reference, API behavior documentation (429/5xx retry, per-file 403/404 ignore), Silver target status. Kubernetes Secret template with figma_token, figma_team_ids, figma_start_date placeholders.
Build Script Path Corrections
src/ingestion/tools/declarative-connector/generate-catalog.sh, src/ingestion/tools/declarative-connector/generate-schema.sh
INGESTION_DIR path resolution corrected to two directory levels above script (${SCRIPT_DIR}/../..), fixing downstream CONNECTORS_DIR and TOOLS_DIR derivations.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • mozhaev-dev

Poem

🐰 Whiskers twitch with delight,
Figma's designs now shine bright,
Bronze flows through the streams,
dbt transforms our dreams,
API verified, all feels right!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a new Figma connector for UI design with bronze-only ingestion streams.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/ingestion/connectors/ui-design/figma/README.md (2)

23-56: 💤 Low value

Clarify that "automatically injected" fields are not user-provided.

The "Automatically injected" table (lines 51-56) documents fields that the platform provides, but their placement after the Secret template (lines 23-40) might suggest users should include them. Consider adding a brief note that these fields are NOT part of the user-provided Secret — they're injected by Insight at runtime. Alternatively, reorganize to present user-provided fields first, then platform-provided fields afterward.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ingestion/connectors/ui-design/figma/README.md` around lines 23 - 56, The
"Automatically injected" table in README.md may be mistaken for user-provided
Secret fields; update the README.md (the K8s Secret example and the tables) so
it's explicit these fields are platform-injected and not part of the Secret a
user must create: either move the "Automatically injected" table away from the
Secret YAML and add a short note above it stating that insight_tenant_id and
insight_source_id are injected by Insight at runtime (not to be included in the
Secret), or add a one-line parenthetical under the table header clarifying "NOT
user-provided — injected by Insight (tenant_id and
insight.cyberfabric.com/source-id)". Ensure the symbols insight_tenant_id and
insight_source_id are mentioned exactly to help locate the content.

44-49: Align README docs with connector schema for figma_page_size

  • connector.yaml includes figma_page_size (integer, default 50; used via config.get('figma_page_size', 50)), so the README fields-table entry is accurate.
  • The K8s Secret example omits figma_page_size because it’s optional (default applies when not set); optional improvement: add it as a commented line for discoverability.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ingestion/connectors/ui-design/figma/README.md` around lines 44 - 49, The
README's fields-table is correct about figma_page_size but the K8s Secret
example omits it; update README.md to explicitly note that figma_page_size is
optional (default 50) and add a commented example line showing how to set
figma_page_size in the Secret for discoverability, referencing the connector
schema entry and the code usage (config.get('figma_page_size', 50)) so readers
see the source of the default.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/components/connectors/ui-design/figma/figma.md`:
- Around line 159-163: Add a language hint to the fenced code block that starts
with the lines "design_file_versions.author_id / author_handle → Identity
Manager name-matching (handle ≈ display name in HR/git sources) → person_id":
change the opening triple-backtick to include a language (e.g., ```text) so the
block is recognized by the linter and MD040 is resolved; update the opening
fence only (do not alter the block contents) where that snippet appears in
figma.md.

In `@docs/components/connectors/ui-design/README.md`:
- Line 60: The README currently conflicts on ownership of design_file_activity:
update the design_file_activity section and any "Populated at collection
time"/"At collection time" references to reflect that design_file_activity is a
dbt-derived (Silver layer) target model (same contract as design_users note), so
replace those collection-time statements with wording that it is produced by the
dbt pipeline/Silver derivation and mention any upstream collection holds raw
events used by dbt; ensure you update the headings and sentence(s) referring to
design_file_activity to consistently state "dbt-derived (Silver layer)" and keep
the separate note that design_users cannot be populated from the Figma REST API.

In `@src/ingestion/connectors/ui-design/figma/connector.yaml`:
- Around line 1035-1041: The figma_team_ids string allows empty partitions
(e.g., "123," or "123,,456") which leads to malformed team requests; update the
schema for the figma_team_ids field to validate input (either change its type to
an array of strings with items pattern /^\d+$/ and minItems: 1, or add a string
pattern like /^\d+(,\d+)*$/) so trailing commas and empty segments are rejected;
also ensure any consumer that currently does a split on figma_team_ids (the code
that parses this field) trims and filters empty values to avoid generating
"/v1/teams//projects" requests.

In `@src/ingestion/connectors/ui-design/figma/descriptor.yaml`:
- Around line 17-20: The descriptor.yaml incorrectly lists figma_start_date
under required_fields causing a mismatch with connector.yaml where
figma_start_date is optional with a default; remove figma_start_date from the
required_fields array (so only figma_token and figma_team_ids remain) to align
the descriptor with connector.yaml and allow deployments to use the defaulted
optional start date.

---

Nitpick comments:
In `@src/ingestion/connectors/ui-design/figma/README.md`:
- Around line 23-56: The "Automatically injected" table in README.md may be
mistaken for user-provided Secret fields; update the README.md (the K8s Secret
example and the tables) so it's explicit these fields are platform-injected and
not part of the Secret a user must create: either move the "Automatically
injected" table away from the Secret YAML and add a short note above it stating
that insight_tenant_id and insight_source_id are injected by Insight at runtime
(not to be included in the Secret), or add a one-line parenthetical under the
table header clarifying "NOT user-provided — injected by Insight (tenant_id and
insight.cyberfabric.com/source-id)". Ensure the symbols insight_tenant_id and
insight_source_id are mentioned exactly to help locate the content.
- Around line 44-49: The README's fields-table is correct about figma_page_size
but the K8s Secret example omits it; update README.md to explicitly note that
figma_page_size is optional (default 50) and add a commented example line
showing how to set figma_page_size in the Secret for discoverability,
referencing the connector schema entry and the code usage
(config.get('figma_page_size', 50)) so readers see the source of the default.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: efb62f66-7360-4119-b8fc-cd685cdc1721

📥 Commits

Reviewing files that changed from the base of the PR and between a51eaa4 and caf548a.

📒 Files selected for processing (10)
  • docs/components/connectors/ui-design/README.md
  • docs/components/connectors/ui-design/figma/figma.md
  • src/ingestion/connectors/ui-design/figma/README.md
  • src/ingestion/connectors/ui-design/figma/connector.yaml
  • src/ingestion/connectors/ui-design/figma/dbt/figma__bronze_promoted.sql
  • src/ingestion/connectors/ui-design/figma/dbt/schema.yml
  • src/ingestion/connectors/ui-design/figma/descriptor.yaml
  • src/ingestion/secrets/connectors/figma.yaml.example
  • src/ingestion/tools/declarative-connector/generate-catalog.sh
  • src/ingestion/tools/declarative-connector/generate-schema.sh

Comment thread docs/components/connectors/ui-design/figma/figma.md Outdated
Comment thread docs/components/connectors/ui-design/README.md
Comment thread src/ingestion/connectors/ui-design/figma/connector.yaml
Comment on lines +17 to +20
required_fields:
- figma_token
- figma_team_ids
- figma_start_date

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

figma_start_date is incorrectly required in descriptor secret fields.

Line 20 conflicts with connector.yaml where figma_start_date is optional and defaulted. This mismatch can block deployments/configs that rely on defaults.

Suggested fix
 secret:
   required_fields:
     - figma_token
     - figma_team_ids
-    - figma_start_date
📝 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
required_fields:
- figma_token
- figma_team_ids
- figma_start_date
required_fields:
- figma_token
- figma_team_ids
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ingestion/connectors/ui-design/figma/descriptor.yaml` around lines 17 -
20, The descriptor.yaml incorrectly lists figma_start_date under required_fields
causing a mismatch with connector.yaml where figma_start_date is optional with a
default; remove figma_start_date from the required_fields array (so only
figma_token and figma_team_ids remain) to align the descriptor with
connector.yaml and allow deployments to use the defaulted optional start date.

- validate figma_team_ids format in the spec (minLength + pattern) so
  trailing/double commas fail at check time instead of producing
  /v1/teams//projects requests
- align design_file_activity derivation wording in the ui-design domain
  spec with the dbt/Silver contract (was: "at collection time")
- add language hint to a fenced block in figma.md (MD040)

figma_start_date stays in descriptor secret.required_fields by design —
confluence and zoom require their start_date the same way (explicit
deployment config over silent defaults).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Roman Mitasov <Roman.Mitasov@constructor.tech>
@mitasovr
mitasovr merged commit ae60ce3 into constructorfabric:main Jun 15, 2026
12 checks passed
mitasovr pushed a commit to mitasovr/insight that referenced this pull request Jun 15, 2026
The publish-chart job both patch-bumps the umbrella `version` and
rewrites `ingestion.toolboxImage` in charts/insight/values.yaml from the
tree checked out at this run's trigger SHA. When two version-bumping PRs
merge back-to-back — especially two that touch src/ingestion/** and so
both rebuild the toolbox — the second run's trigger SHA does not yet
contain the first run's `chore(release)` commit, so both runs:

  * compute the SAME next umbrella version, and
  * rewrite the SAME `ingestion.toolboxImage` line to their own build tag.

The first run pushes its release commit; the second run's commit-back is
then rejected (non-fast-forward). The retry's `git pull --rebase` hits a
conflict in charts/insight/values.yaml — specifically on the
`ingestion.toolboxImage` line (the version line auto-merges because both
runs wrote the same value) — and aborts -> exit 1. A plain job re-run
repeats this forever because `checkout` re-pins the same stale SHA.

Secondary symptom: both runs `helm push` the same chart version with
different contents, so the chart published to GHCR diverges from what
main records as that version.

Fix: re-anchor the working tree to the live branch tip (`git fetch` +
`git reset --hard origin/$GITHUB_REF_NAME`) before any value is computed,
so version-compute, the toolboxImage ref and the commit-back are all
consistent with the branch tip — and a re-run recomputes against the
refreshed tip instead of replaying the stale SHA. Combined with the
existing per-ref `concurrency` serialisation the final push fast-forwards,
so the fragile rebase-retry is replaced with a fail-loud guard.

Reproduced by run 27535189783 (constructorfabric#1306 Figma then constructorfabric#1305 Workday merged
back-to-back, both touching src/ingestion/**).

Note: bump-descriptors shares the same rebase-retry pattern; left
untouched here to keep this fix scoped to the reproduced failure.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mitasovr added a commit that referenced this pull request Jun 15, 2026
The publish-chart job both patch-bumps the umbrella `version` and
rewrites `ingestion.toolboxImage` in charts/insight/values.yaml from the
tree checked out at this run's trigger SHA. When two version-bumping PRs
merge back-to-back — especially two that touch src/ingestion/** and so
both rebuild the toolbox — the second run's trigger SHA does not yet
contain the first run's `chore(release)` commit, so both runs:

  * compute the SAME next umbrella version, and
  * rewrite the SAME `ingestion.toolboxImage` line to their own build tag.

The first run pushes its release commit; the second run's commit-back is
then rejected (non-fast-forward). The retry's `git pull --rebase` hits a
conflict in charts/insight/values.yaml — specifically on the
`ingestion.toolboxImage` line (the version line auto-merges because both
runs wrote the same value) — and aborts -> exit 1. A plain job re-run
repeats this forever because `checkout` re-pins the same stale SHA.

Secondary symptom: both runs `helm push` the same chart version with
different contents, so the chart published to GHCR diverges from what
main records as that version.

Fix: re-anchor the working tree to the live branch tip (`git fetch` +
`git reset --hard origin/$GITHUB_REF_NAME`) before any value is computed,
so version-compute, the toolboxImage ref and the commit-back are all
consistent with the branch tip — and a re-run recomputes against the
refreshed tip instead of replaying the stale SHA. Combined with the
existing per-ref `concurrency` serialisation the final push fast-forwards,
so the fragile rebase-retry is replaced with a fail-loud guard.

Reproduced by run 27535189783 (#1306 Figma then #1305 Workday merged
back-to-back, both touching src/ingestion/**).

Note: bump-descriptors shares the same rebase-retry pattern; left
untouched here to keep this fix scoped to the reproduced failure.

Co-authored-by: Roman Mitasov <Roman.Mitasov@constructor.tech>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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.

2 participants