Skip to content

feat(thrive): add Thrive Learning integration (47 tools + block)#5214

Merged
waleedlatif1 merged 4 commits into
stagingfrom
worktree-thrive-learning-integration
Jun 25, 2026
Merged

feat(thrive): add Thrive Learning integration (47 tools + block)#5214
waleedlatif1 merged 4 commits into
stagingfrom
worktree-thrive-learning-integration

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

Adds a complete Thrive Learning (LMS) integration covering the full public REST API.

  • 47 tools across the entire API surface:
    • Users — create, update, delete, suspend (v2 lifecycle), search, get by ID, get by ref
    • Audiences — list/create/get/update/delete, members (list/add/replace/remove), managers (list/add/replace/remove)
    • Assignments & Enrolments — list/create/get/update/delete assignments, list/get enrolments
    • Completions — list/get/create
    • Content & Activity — get/query content, get/query activity records
    • CPD — categories, entries, requirements, user summaries
    • Tags & Skills — list/get tags, add/remove user tags, update skills, get skill levels
  • Block with a single operation dropdown, conditional inputs per operation, a Region selector (Production / Staging, incl. MEA), and ThriveBlockMeta with 8 templates.
  • Auth: HTTP Basic — Tenant ID as username, API key as password (user-only credentials). No file endpoints exist in this API, so no file handling is needed.

Notes

  • User lifecycle endpoints correctly target /rest/v2; everything else /rest/v1.
  • Shared utils.ts builds the base URL/auth headers and tolerantly parses responses (handles empty-body deletes and JSON error bodies).
  • Outputs are fully typed from the OpenAPI schemas (no raw JSON dumps).

Validation

  • tsc --noEmit clean (0 errors)
  • biome lint clean
  • check:api-validation passes
  • Adversarially audited all 47 tools against the API specs and the block↔tool alignment (every required param has a conditioned, required input; outputs cover all tools).

Add a full Thrive Learning (LMS) integration covering the public REST API:
users lifecycle, audiences with members/managers, assignments and enrolments,
completions, content and activity records, CPD, tags, and skills. Uses HTTP
Basic auth (Tenant ID + API key) with a region selector for the v1/v2 hosts.
@vercel

vercel Bot commented Jun 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jun 25, 2026 10:06pm

Request Review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor

cursor Bot commented Jun 25, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Large additive surface area with HR-sensitive operations (user delete/suspend, audience membership replacement) and tenant API keys in workflows; no changes to core app auth.

Overview
Introduces a Thrive Learning HR/LMS integration so workflows can manage learners, audiences, compliance training, and CPD via the public REST API.

47 API tools are added under apps/sim/tools/thrive/ (shared Basic auth, region hosts, v1 vs v2 user lifecycle paths) and registered in the global tools registry. A thrive workflow block maps one operation dropdown to those tools with conditional fields, region selection, and eight starter templates.

Docs and catalog updates include ThriveIcon, icon mappings, integrations.json, integrations nav entry, and a large thrive.mdx reference for every action.

Reviewed by Cursor Bugbot for commit 9df1e68. Configure here.

Comment thread apps/sim/tools/thrive/list_assignments.ts
@greptile-apps

greptile-apps Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds a complete Thrive Learning (LMS) integration with 47 tools covering the full public API surface — user lifecycle, audiences, assignments, enrolments, completions, content, activities, CPD, tags, and skills — plus a block with 47-operation dropdown and region selector. A shared utils.ts handles auth header construction, query parameter appending, JSON parsing, and tolerant response parsing.

  • All 47 tools are registered in tools/registry.ts and wired to the new ThriveBlock in blocks/registry.ts; auth uses HTTP Basic with Tenant ID / API key marked user-only per the team's credential-visibility rule.
  • parseThriveArray now throws on malformed JSON (fixing the previously flagged silent-fallback issue), and parseThriveJsonObject throws for additionalFields in create/update user.
  • The block's config.params handles numeric and boolean type coercions (page, perPage, sso, apiControlled, etc.) and remaps userStatus/enrolmentStatus dropdowns to the single status param that tools expect.

Confidence Score: 5/5

Safe to merge. All 47 tools follow established integration patterns, credentials are handled correctly with user-only visibility, and the previously flagged silent-fallback issues in parseThriveArray and additionalFields have been resolved.

The integration is thorough and internally consistent. Auth is correctly built using HTTP Basic over HTTPS in all tools, JSON parsing throws on bad input rather than silently degrading, and the block conditions and required-field mappings align with tool parameter definitions across all 47 operations. The one remaining edge case — parseThriveArray wrapping non-array valid JSON in a single-element array — surfaces as an API-level error rather than silently corrupting data, so it carries no merge risk.

apps/sim/tools/thrive/utils.ts — the parseThriveArray fallback path that wraps non-array parsed values.

Important Files Changed

Filename Overview
apps/sim/tools/thrive/utils.ts Core utility module; parseThriveArray now throws on invalid JSON, but wraps non-array valid JSON values in a single-element array instead of rejecting them.
apps/sim/blocks/blocks/thrive.ts Block definition with 47-operation dropdown, region selector, and conditional subBlocks; config.params correctly handles type coercions and status field remapping.
apps/sim/tools/thrive/types.ts Comprehensive TypeScript type definitions for all 47 tool params and responses; well-structured with shared output property constants.
apps/sim/tools/thrive/index.ts Re-exports all 47 tools with consistent thrive_ prefix naming for registry consumption.
apps/sim/tools/registry.ts All 47 thrive tools correctly registered with their thrive_ prefixed IDs.
apps/sim/tools/thrive/create_user.ts Creates a user via v2 endpoint; additionalFields parsed with parseThriveJsonObject (throws on invalid JSON); required fields correctly validated.
apps/sim/tools/thrive/update_user.ts PATCH via v2 /users/ref/{ref}; only includes fields that are explicitly set; additionalFields correctly throws on invalid JSON.
apps/sim/tools/thrive/add_audience_members.ts Sends a bare JSON array body (correct for the Thrive API); uses parseThriveArray which now throws on malformed input.
apps/sim/tools/thrive/replace_audience_members.ts PATCH with bare JSON array body; API explicitly requires a non-empty array, and parseThriveArray now throws on invalid JSON.
apps/sim/tools/thrive/query_cpd_user_summaries.ts CPD user summaries query with required date range params; returns paginated envelope correctly.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant U as User / LLM
    participant B as ThriveBlock
    participant F as Sim Framework
    participant T as Thrive Tool (e.g. create_user)
    participant A as Thrive REST API

    U->>B: Select operation + fill inputs
    B->>F: config.tool() → "thrive_create_user"
    B->>F: "config.params() → {page: Number, sso: bool, status: …}"
    F->>T: Merge block inputs + config.params overrides
    T->>T: getThriveBaseUrl(host, "v2")
    T->>T: getThriveHeaders(tenantId, apiKey) → Basic auth
    T->>T: body() – parseThriveJsonObject / parseThriveArray
    T->>A: HTTPS request (GET/POST/PATCH/DELETE)
    A-->>T: JSON response (or empty body for DELETEs)
    T->>T: parseThriveResponse() – throws on non-2xx
    T-->>F: "{ success, output }"
    F-->>U: Tool output
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant U as User / LLM
    participant B as ThriveBlock
    participant F as Sim Framework
    participant T as Thrive Tool (e.g. create_user)
    participant A as Thrive REST API

    U->>B: Select operation + fill inputs
    B->>F: config.tool() → "thrive_create_user"
    B->>F: "config.params() → {page: Number, sso: bool, status: …}"
    F->>T: Merge block inputs + config.params overrides
    T->>T: getThriveBaseUrl(host, "v2")
    T->>T: getThriveHeaders(tenantId, apiKey) → Basic auth
    T->>T: body() – parseThriveJsonObject / parseThriveArray
    T->>A: HTTPS request (GET/POST/PATCH/DELETE)
    A-->>T: JSON response (or empty body for DELETEs)
    T->>T: parseThriveResponse() – throws on non-2xx
    T-->>F: "{ success, output }"
    F-->>U: Tool output
Loading

Reviews (6): Last reviewed commit: "fix(thrive): split status into context-s..." | Re-trigger Greptile

Comment thread apps/sim/tools/thrive/utils.ts Outdated
Comment thread apps/sim/tools/thrive/create_user.ts
@greptile-apps

greptile-apps Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds a full Thrive Learning integration with 47 tools covering user lifecycle (v2), audiences, assignments, enrolments, completions, content, activities, CPD, tags, and skills — plus a block with conditional subblock UI, a region dropdown, and 8 template suggestions.

  • 47 new tool files (apps/sim/tools/thrive/) each targeting the correct API version (v1 for most, v2 for user lifecycle), with shared utilities in utils.ts and complete TypeScript types in types.ts.
  • Block config (apps/sim/blocks/blocks/thrive.ts) wires 47 operations to tools via a single dropdown, applies numeric/boolean coercions in tools.config.params, and gates every input behind the correct condition/required constraints.
  • Two get-by-ID endpoint paths (/user/{id} and /activity/{id}) deviate from the plural base paths used by every other endpoint pair in the integration and should be verified against the Thrive API spec before deploying.

Confidence Score: 3/5

Two get-by-ID endpoint paths deviate from the plural base paths used by every other get/list pair in this integration; if those paths are wrong, those tools will 404 on every call without any indication of misconfiguration.

The integration is large and mostly well-structured, but get_user_by_id constructs the URL as /user/{id} while all other user endpoints consistently use /users/, and get_activity uses /activity/{id} while its pair uses /activities. Both paths stand out against every other endpoint in the PR. If either is wrong, the affected tool silently returns a 404 that the executor surfaces only as a generic API error. The rest of the 47 tools, the block config, types, registry wiring, and auth handling all look correct.

apps/sim/tools/thrive/get_user_by_id.ts and apps/sim/tools/thrive/get_activity.ts need the URL paths verified against the Thrive API spec before this is merged.

Important Files Changed

Filename Overview
apps/sim/tools/thrive/get_user_by_id.ts Get-user-by-ID tool uses URL path /user/{id} (singular) while every other user endpoint in the integration uses the plural /users/ base — likely a typo that would cause 404s on every call.
apps/sim/tools/thrive/get_activity.ts Get-activity tool uses /activity/{id} (singular) while the paired query_activities uses /activities (plural), inconsistent with every other get/list pair in the codebase.
apps/sim/tools/thrive/utils.ts Shared utilities for auth headers, URL building, query-param appending, and response parsing. parseThriveArray silently returns [] on invalid JSON, which downstream tools pass to the API without pre-flight validation.
apps/sim/tools/thrive/list_completions.ts Exposes both perPage and limit params simultaneously in the query string; both are surfaced in the block UI for this operation, which may confuse users or produce conflicting API behaviour.
apps/sim/blocks/blocks/thrive.ts Well-structured 1043-line block config covering all 47 operations with correct conditional inputs, required-field guards, operation→tool mapping, and type coercion for numeric/boolean block inputs.
apps/sim/tools/thrive/types.ts Comprehensive 861-line type file with full interface definitions and output property constants for all 47 tools; well-organised with no obvious issues.
apps/sim/tools/thrive/index.ts Clean barrel export re-exporting all 47 tool instances; all exports are present and correctly named.
apps/sim/tools/registry.ts All 47 Thrive tools are correctly registered under their expected thrive_* keys; no mismatches between the registry and the tool IDs defined in each tool file.
apps/sim/tools/thrive/create_user.ts User creation targeting v2 lifecycle endpoint; credentials correctly user-only, all optional fields guarded, additionalFields JSON-parsed with silent fallback.
apps/sim/tools/thrive/replace_audience_members.ts Correctly PATCHes the members list; body is pre-stringified via parseThriveArrayJSON.stringify, which is valid since the executor passes strings through as-is. Invalid input silently produces an empty array (see utils.ts comment).

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant UI as Block UI
    participant Executor as GenericBlockHandler
    participant Utils as tools/utils.ts
    participant ThriveAPI as Thrive REST API

    UI->>Executor: params (operation, tenantId, apiKey, host, ...)
    Executor->>Executor: "resolve tool ID via thrive_${operation}"
    Executor->>Executor: coerce numeric/boolean fields via config.params()
    Executor->>Utils: executeTool(toolId, finalInputs)
    Utils->>Utils: "build URL (getThriveBaseUrl v1|v2 + path)"
    Utils->>Utils: build headers (Basic btoa(tenantId:apiKey))
    Utils->>Utils: "build body (JSON.stringify object | pass-through string)"
    Utils->>ThriveAPI: HTTP request (GET/POST/PATCH/DELETE)
    ThriveAPI-->>Utils: Response (JSON or empty body)
    Utils-->>Executor: "transformResponse() → { success, output }"
    Executor-->>UI: typed output fields
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant UI as Block UI
    participant Executor as GenericBlockHandler
    participant Utils as tools/utils.ts
    participant ThriveAPI as Thrive REST API

    UI->>Executor: params (operation, tenantId, apiKey, host, ...)
    Executor->>Executor: "resolve tool ID via thrive_${operation}"
    Executor->>Executor: coerce numeric/boolean fields via config.params()
    Executor->>Utils: executeTool(toolId, finalInputs)
    Utils->>Utils: "build URL (getThriveBaseUrl v1|v2 + path)"
    Utils->>Utils: build headers (Basic btoa(tenantId:apiKey))
    Utils->>Utils: "build body (JSON.stringify object | pass-through string)"
    Utils->>ThriveAPI: HTTP request (GET/POST/PATCH/DELETE)
    ThriveAPI-->>Utils: Response (JSON or empty body)
    Utils-->>Executor: "transformResponse() → { success, output }"
    Executor-->>UI: typed output fields
Loading

Reviews (2): Last reviewed commit: "feat(thrive): add Thrive Learning integr..." | Re-trigger Greptile

Comment thread apps/sim/tools/thrive/get_user_by_id.ts
Comment thread apps/sim/tools/thrive/get_activity.ts
Comment thread apps/sim/tools/thrive/list_completions.ts Outdated
Comment thread apps/sim/tools/thrive/utils.ts Outdated
- parseThriveArray/parseThriveJsonObject now throw a descriptive error on
  malformed JSON instead of silently sending an empty/omitted value
- additionalFields parse errors are surfaced to the caller
- remove the redundant 'limit' query param (perPage already covers paging and
  the API prioritises perPage over limit) from the five list tools and block
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile I've pushed 47a2c42 addressing the review: parseThriveArray/additionalFields now throw descriptive errors on malformed JSON instead of failing silently, and the redundant limit param was removed (perPage covers paging). The /user/{id} and /activity/{id} singular paths and the bare-array list responses are intentional and match the Thrive OpenAPI spec. Please re-review.

…ParamId

The block test forbids reusing a canonicalParamId across different operation
conditions. Replace the two canonical status subblocks (search_users vs
list_enrolments) with one 'status' dropdown whose options are labelled by
context, fixing the canonical-pair validation failures.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/blocks/blocks/thrive.ts
Addresses review feedback that one shared status dropdown mixed user
lifecycle values (active/inactive/expired/new) with enrolment values
(archived/complete/open/...). Use separate userStatus and enrolmentStatus
dropdowns (no canonicalParamId) remapped to the tool's 'status' param so each
operation only offers valid options.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile latest is 9df1e68 — split the shared status dropdown into context-specific userStatus/enrolmentStatus fields per your/Cursor's feedback. Please re-review the latest commit.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 9df1e68. Configure here.

@waleedlatif1 waleedlatif1 merged commit 371cc94 into staging Jun 25, 2026
16 checks passed
@waleedlatif1 waleedlatif1 deleted the worktree-thrive-learning-integration branch June 25, 2026 22:26
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