Skip to content

Commit 817de75

Browse files
waleedlatif1claude
andcommitted
fix(rippling): add wandConfig, clean titles, and migrate legacy operation values
- Remove "(JSON)" suffix from all subBlock titles - Add wandConfig with AI prompts for filter, expand, orderBy, query, data, records, and dataType fields - Add OPERATION_VALUE_MIGRATIONS to migrate old operation values (list_employees → list_workers, etc.) preventing runtime errors on saved workflows Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent aac4200 commit 817de75

2 files changed

Lines changed: 127 additions & 23 deletions

File tree

apps/sim/blocks/blocks/rippling.ts

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,18 @@ export const RipplingBlock: BlockConfig = {
559559
placeholder: '{ "type": "TEXT" }',
560560
condition: { field: 'operation', value: [...CUSTOM_OBJECT_FIELD_WRITE_OPS] },
561561
required: { field: 'operation', value: 'create_custom_object_field' },
562+
wandConfig: {
563+
enabled: true,
564+
prompt: `Generate a JSON object for a Rippling custom object field data type based on the user's description.
565+
Common types: TEXT, NUMBER, BOOLEAN, DATE, DATETIME, LOOKUP, FORMULA, ROLLUP.
566+
Examples:
567+
- "text field" -> { "type": "TEXT" }
568+
- "number field" -> { "type": "NUMBER" }
569+
- "date field" -> { "type": "DATE" }
570+
- "lookup to workers" -> { "type": "LOOKUP", "referenced_object": "worker" }
571+
Return ONLY the JSON - no explanations, no extra text.`,
572+
placeholder: 'Describe the field type',
573+
},
562574
},
563575
{
564576
id: 'fieldRequired',
@@ -595,23 +607,23 @@ export const RipplingBlock: BlockConfig = {
595607
},
596608
{
597609
id: 'rqlDefinition',
598-
title: 'RQL Definition (JSON)',
610+
title: 'RQL Definition',
599611
type: 'long-input',
600612
placeholder: '{ ... }',
601613
mode: 'advanced',
602614
condition: { field: 'operation', value: [...CUSTOM_OBJECT_FIELD_WRITE_OPS] },
603615
},
604616
{
605617
id: 'formulaAttrMetas',
606-
title: 'Formula Attr Metas (JSON)',
618+
title: 'Formula Attr Metas',
607619
type: 'long-input',
608620
placeholder: '{ ... }',
609621
mode: 'advanced',
610622
condition: { field: 'operation', value: [...CUSTOM_OBJECT_FIELD_WRITE_OPS] },
611623
},
612624
{
613625
id: 'fieldSection',
614-
title: 'Section (JSON)',
626+
title: 'Section',
615627
type: 'long-input',
616628
placeholder: '{ ... }',
617629
mode: 'advanced',
@@ -627,15 +639,15 @@ export const RipplingBlock: BlockConfig = {
627639
},
628640
{
629641
id: 'derivedAggregatedField',
630-
title: 'Derived Aggregated Field (JSON)',
642+
title: 'Derived Aggregated Field',
631643
type: 'long-input',
632644
placeholder: '{ ... }',
633645
mode: 'advanced',
634646
condition: { field: 'operation', value: 'create_custom_object_field' },
635647
},
636648
{
637649
id: 'nameFieldDetails',
638-
title: 'Name Field Details (JSON)',
650+
title: 'Name Field Details',
639651
type: 'long-input',
640652
placeholder: '{ ... }',
641653
mode: 'advanced',
@@ -725,15 +737,15 @@ export const RipplingBlock: BlockConfig = {
725737
},
726738
{
727739
id: 'formatDateFields',
728-
title: 'Format Date Fields (JSON)',
740+
title: 'Format Date Fields',
729741
type: 'long-input',
730742
placeholder: '{ "date_format": "ISO_8601" }',
731743
mode: 'advanced',
732744
condition: { field: 'operation', value: 'trigger_report_run' },
733745
},
734746
{
735747
id: 'formatCurrencyFields',
736-
title: 'Format Currency Fields (JSON)',
748+
title: 'Format Currency Fields',
737749
type: 'long-input',
738750
placeholder: '{ ... }',
739751
mode: 'advanced',
@@ -753,10 +765,19 @@ export const RipplingBlock: BlockConfig = {
753765
// Data JSON - only for passthrough operations (custom object records, draft hires, supergroup ops)
754766
{
755767
id: 'data',
756-
title: 'Data (JSON)',
768+
title: 'Data',
757769
type: 'long-input',
758770
placeholder: '{ "key": "value" }',
759771
condition: { field: 'operation', value: [...DATA_PASSTHROUGH_OPS] },
772+
wandConfig: {
773+
enabled: true,
774+
prompt: `Generate a JSON object for a Rippling API request based on the user's description.
775+
For custom object records: { "field_name": "value", ... }
776+
For draft hires: { "draft_hires": [{ "personal_info": { "first_name": "", "last_name": "", "email": "" }, "employment_info": { "start_date": "", "title": "" }, "work_location_info": { "work_location_id": "" } }] }
777+
For supergroup member updates: { "Operations": [{ "op": "add", "path": "members", "value": [{ "value": "worker_id" }] }] }
778+
Return ONLY the JSON - no explanations, no extra text.`,
779+
placeholder: 'Describe the data to send',
780+
},
760781
required: {
761782
field: 'operation',
762783
value: [
@@ -770,9 +791,9 @@ export const RipplingBlock: BlockConfig = {
770791
// Records JSON for bulk operations
771792
{
772793
id: 'records',
773-
title: 'Records (JSON)',
794+
title: 'Records',
774795
type: 'long-input',
775-
placeholder: '[{ "fields": { ... } }, ...]',
796+
placeholder: '[{ "data": { ... } }, ...]',
776797
condition: {
777798
field: 'operation',
778799
value: [
@@ -816,6 +837,13 @@ export const RipplingBlock: BlockConfig = {
816837
type: 'long-input',
817838
placeholder: 'Enter query expression',
818839
condition: { field: 'operation', value: 'query_custom_object_records' },
840+
wandConfig: {
841+
enabled: true,
842+
prompt: `Generate a query expression for the Rippling Custom Object Records Query API based on the user's description.
843+
The query should filter custom object records by their field values.
844+
Return ONLY the query expression - no explanations, no extra text.`,
845+
placeholder: 'Describe which records to find',
846+
},
819847
},
820848
{
821849
id: 'limit',
@@ -833,6 +861,16 @@ export const RipplingBlock: BlockConfig = {
833861
placeholder: 'OData filter expression',
834862
mode: 'advanced',
835863
condition: { field: 'operation', value: [...FILTER_OPS] },
864+
wandConfig: {
865+
enabled: true,
866+
prompt: `Generate an OData filter expression for the Rippling API based on the user's description.
867+
Examples:
868+
- "active workers" -> status eq 'ACTIVE'
869+
- "workers in engineering" -> department eq 'Engineering'
870+
- "created after January" -> created_at gt '2024-01-01'
871+
Return ONLY the filter expression - no explanations, no extra text.`,
872+
placeholder: 'Describe what to filter for',
873+
},
836874
},
837875
{
838876
id: 'expand',
@@ -841,6 +879,13 @@ export const RipplingBlock: BlockConfig = {
841879
placeholder: 'Fields to expand',
842880
mode: 'advanced',
843881
condition: { field: 'operation', value: [...EXPAND_OPS] },
882+
wandConfig: {
883+
enabled: true,
884+
prompt: `Generate a comma-separated list of fields to expand for a Rippling API request based on the user's description.
885+
Common expandable fields: department, parent, worker, business_partner_group, client_group, parent_legal_entity, legal_entities, department_hierarchy.
886+
Return ONLY the comma-separated field names - no explanations, no extra text.`,
887+
placeholder: 'Describe which related data to include',
888+
},
844889
},
845890
{
846891
id: 'orderBy',
@@ -849,6 +894,17 @@ export const RipplingBlock: BlockConfig = {
849894
placeholder: 'e.g., name asc',
850895
mode: 'advanced',
851896
condition: { field: 'operation', value: [...ORDER_BY_OPS] },
897+
wandConfig: {
898+
enabled: true,
899+
prompt: `Generate a sort expression for the Rippling API based on the user's description.
900+
Format: field_name for ascending, -field_name for descending. Common fields: id, created_at, updated_at, name.
901+
Examples:
902+
- "newest first" -> -created_at
903+
- "alphabetical" -> name
904+
- "recently updated" -> -updated_at
905+
Return ONLY the sort expression - no explanations, no extra text.`,
906+
placeholder: 'Describe how to sort results',
907+
},
852908
},
853909
{
854910
id: 'cursor',

apps/sim/lib/workflows/migrations/subblock-migrations.ts

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,33 @@ export const SUBBLOCK_ID_MIGRATIONS: Record<string, Record<string, string>> = {
5353
},
5454
}
5555

56+
/**
57+
* Maps old Rippling operation values to their current equivalents.
58+
*
59+
* When the integration was expanded from 16 to 86 tools, several
60+
* operations were renamed or removed. Saved workflows still carry
61+
* the old operation value. Without this mapping the tool selector
62+
* returns an unregistered tool ID, causing a runtime error.
63+
*/
64+
export const OPERATION_VALUE_MIGRATIONS: Record<string, Record<string, string>> = {
65+
rippling: {
66+
list_employees: 'list_workers',
67+
get_employee: 'get_worker',
68+
list_employees_with_terminated: 'list_workers',
69+
get_company: 'list_companies',
70+
get_company_activity: '_removed_get_company_activity',
71+
list_levels: '_removed_list_levels',
72+
list_leave_requests: '_removed_list_leave_requests',
73+
process_leave_request: '_removed_process_leave_request',
74+
list_leave_balances: '_removed_list_leave_balances',
75+
get_leave_balance: '_removed_get_leave_balance',
76+
list_leave_types: '_removed_list_leave_types',
77+
create_group: '_removed_create_group',
78+
update_group: '_removed_update_group',
79+
push_candidate: '_removed_push_candidate',
80+
},
81+
}
82+
5683
/**
5784
* Migrates legacy subblock IDs inside a single block's subBlocks map.
5885
* Returns a new subBlocks record if anything changed, or the original if not.
@@ -103,22 +130,43 @@ export function migrateSubblockIds(blocks: Record<string, BlockState>): {
103130

104131
for (const [blockId, block] of Object.entries(blocks)) {
105132
const renames = SUBBLOCK_ID_MIGRATIONS[block.type]
106-
if (!renames || !block.subBlocks) {
107-
result[blockId] = block
108-
continue
133+
const opMigrations = OPERATION_VALUE_MIGRATIONS[block.type]
134+
let currentBlock = block
135+
136+
if (renames && block.subBlocks) {
137+
const { subBlocks, migrated } = migrateBlockSubblockIds(block.subBlocks, renames)
138+
if (migrated) {
139+
logger.info('Migrated legacy subblock IDs', {
140+
blockId: block.id,
141+
blockType: block.type,
142+
})
143+
anyMigrated = true
144+
currentBlock = { ...currentBlock, subBlocks }
145+
}
109146
}
110147

111-
const { subBlocks, migrated } = migrateBlockSubblockIds(block.subBlocks, renames)
112-
if (migrated) {
113-
logger.info('Migrated legacy subblock IDs', {
114-
blockId: block.id,
115-
blockType: block.type,
116-
})
117-
anyMigrated = true
118-
result[blockId] = { ...block, subBlocks }
119-
} else {
120-
result[blockId] = block
148+
if (opMigrations && currentBlock.subBlocks?.operation) {
149+
const oldOp = String(currentBlock.subBlocks.operation.value ?? '')
150+
const newOp = opMigrations[oldOp]
151+
if (newOp) {
152+
logger.info('Migrated legacy operation value', {
153+
blockId: currentBlock.id,
154+
blockType: currentBlock.type,
155+
from: oldOp,
156+
to: newOp,
157+
})
158+
anyMigrated = true
159+
currentBlock = {
160+
...currentBlock,
161+
subBlocks: {
162+
...currentBlock.subBlocks,
163+
operation: { ...currentBlock.subBlocks.operation, value: newOp },
164+
},
165+
}
166+
}
121167
}
168+
169+
result[blockId] = currentBlock
122170
}
123171

124172
return { blocks: result, migrated: anyMigrated }

0 commit comments

Comments
 (0)