|
| 1 | +#!/usr/bin/env bun |
| 2 | +/** |
| 3 | + * Filters Example |
| 4 | + * |
| 5 | + * This example demonstrates how to use filters with the HRIS list employees endpoint. |
| 6 | + * It showcases the deep object serialization implementation that properly converts |
| 7 | + * nested filter objects to OpenAPI deepObject style query parameters. |
| 8 | + * |
| 9 | + * Key features demonstrated: |
| 10 | + * 1. Basic filter usage (updated_after, email, employee_number) |
| 11 | + * 2. Proxy parameter usage for provider-specific filters |
| 12 | + * 3. Complex nested filter combinations |
| 13 | + * 4. Proper serialization of filter objects to query parameters |
| 14 | + * |
| 15 | + * Usage: |
| 16 | + * |
| 17 | + * ```bash |
| 18 | + * bun run examples/filters.ts |
| 19 | + * ``` |
| 20 | + */ |
| 21 | + |
| 22 | +import assert from 'node:assert'; |
| 23 | +import { StackOneToolSet } from '../src'; |
| 24 | + |
| 25 | +type DryRunResult = { url: string }; |
| 26 | + |
| 27 | +const hriseEmployeeFilters = async (): Promise<void> => { |
| 28 | + // Initialize the toolset |
| 29 | + const toolset = new StackOneToolSet(); |
| 30 | + const accountId = 'test-account-id'; |
| 31 | + |
| 32 | + // Get the HRIS tools with account ID |
| 33 | + const tools = toolset.getStackOneTools('hris_*', accountId); |
| 34 | + const employeesTool = tools.getTool('hris_list_employees'); |
| 35 | + |
| 36 | + assert(employeesTool !== undefined, 'Expected to find hris_list_employees tool'); |
| 37 | + |
| 38 | + console.log('🧪 Testing HRIS Employee Filters with Deep Object Serialization\n'); |
| 39 | + |
| 40 | + /* |
| 41 | + * Example 1: Basic date filter |
| 42 | + * Demonstrates filtering employees updated after a specific date |
| 43 | + */ |
| 44 | + console.log('1️⃣ Basic Date Filter Test'); |
| 45 | + const basicDateFilter = (await employeesTool.execute( |
| 46 | + { |
| 47 | + filter: { |
| 48 | + updated_after: '2023-01-01T00:00:00.000Z', |
| 49 | + }, |
| 50 | + }, |
| 51 | + { dryRun: true } |
| 52 | + )) as DryRunResult; |
| 53 | + |
| 54 | + console.log('Filter object:', { filter: { updated_after: '2023-01-01T00:00:00.000Z' } }); |
| 55 | + console.log('Serialized URL:', basicDateFilter.url); |
| 56 | + |
| 57 | + // Verify that the filter is properly serialized as deepObject style |
| 58 | + assert( |
| 59 | + basicDateFilter.url.includes('filter%5Bupdated_after%5D=2023-01-01T00%3A00%3A00.000Z'), |
| 60 | + 'Expected URL to contain properly serialized date filter' |
| 61 | + ); |
| 62 | + console.log('✅ Date filter serialized correctly\n'); |
| 63 | + |
| 64 | + /* |
| 65 | + * Example 2: Email filter |
| 66 | + * Demonstrates filtering employees by email address |
| 67 | + */ |
| 68 | + console.log('2️⃣ Email Filter Test'); |
| 69 | + const emailFilter = (await employeesTool.execute( |
| 70 | + { |
| 71 | + filter: { |
| 72 | + email: 'john.doe@company.com', |
| 73 | + }, |
| 74 | + }, |
| 75 | + { dryRun: true } |
| 76 | + )) as DryRunResult; |
| 77 | + |
| 78 | + console.log('Filter object:', { filter: { email: 'john.doe@company.com' } }); |
| 79 | + console.log('Serialized URL:', emailFilter.url); |
| 80 | + |
| 81 | + assert( |
| 82 | + emailFilter.url.includes('filter%5Bemail%5D=john.doe%40company.com'), |
| 83 | + 'Expected URL to contain properly serialized email filter' |
| 84 | + ); |
| 85 | + console.log('✅ Email filter serialized correctly\n'); |
| 86 | + |
| 87 | + /* |
| 88 | + * Example 3: Employee number filter |
| 89 | + * Demonstrates filtering employees by employee number |
| 90 | + */ |
| 91 | + console.log('3️⃣ Employee Number Filter Test'); |
| 92 | + const employeeNumberFilter = (await employeesTool.execute( |
| 93 | + { |
| 94 | + filter: { |
| 95 | + employee_number: 'EMP001', |
| 96 | + }, |
| 97 | + }, |
| 98 | + { dryRun: true } |
| 99 | + )) as DryRunResult; |
| 100 | + |
| 101 | + console.log('Filter object:', { filter: { employee_number: 'EMP001' } }); |
| 102 | + console.log('Serialized URL:', employeeNumberFilter.url); |
| 103 | + |
| 104 | + assert( |
| 105 | + employeeNumberFilter.url.includes('filter%5Bemployee_number%5D=EMP001'), |
| 106 | + 'Expected URL to contain properly serialized employee number filter' |
| 107 | + ); |
| 108 | + console.log('✅ Employee number filter serialized correctly\n'); |
| 109 | + |
| 110 | + /* |
| 111 | + * Example 4: Multiple filters combined |
| 112 | + * Demonstrates using multiple filter parameters together |
| 113 | + */ |
| 114 | + console.log('4️⃣ Multiple Filters Combined Test'); |
| 115 | + const multipleFilters = (await employeesTool.execute( |
| 116 | + { |
| 117 | + filter: { |
| 118 | + updated_after: '2023-06-01T00:00:00.000Z', |
| 119 | + email: 'jane.smith@company.com', |
| 120 | + employee_number: 'EMP002', |
| 121 | + }, |
| 122 | + }, |
| 123 | + { dryRun: true } |
| 124 | + )) as DryRunResult; |
| 125 | + |
| 126 | + console.log('Filter object:', { |
| 127 | + filter: { |
| 128 | + updated_after: '2023-06-01T00:00:00.000Z', |
| 129 | + email: 'jane.smith@company.com', |
| 130 | + employee_number: 'EMP002', |
| 131 | + }, |
| 132 | + }); |
| 133 | + console.log('Serialized URL:', (multipleFilters as { url: string }).url); |
| 134 | + |
| 135 | + // Verify all filters are present in the URL |
| 136 | + assert( |
| 137 | + multipleFilters.url.includes('filter%5Bupdated_after%5D=2023-06-01T00%3A00%3A00.000Z'), |
| 138 | + 'Expected URL to contain date filter' |
| 139 | + ); |
| 140 | + assert( |
| 141 | + multipleFilters.url.includes('filter%5Bemail%5D=jane.smith%40company.com'), |
| 142 | + 'Expected URL to contain email filter' |
| 143 | + ); |
| 144 | + assert( |
| 145 | + multipleFilters.url.includes('filter%5Bemployee_number%5D=EMP002'), |
| 146 | + 'Expected URL to contain employee number filter' |
| 147 | + ); |
| 148 | + console.log('✅ Multiple filters serialized correctly\n'); |
| 149 | + |
| 150 | + /* |
| 151 | + * Example 5: Proxy parameters for provider-specific filtering |
| 152 | + * Demonstrates using proxy parameters which also use deepObject serialization |
| 153 | + */ |
| 154 | + console.log('5️⃣ Proxy Parameters Test'); |
| 155 | + const proxyParameters = (await employeesTool.execute( |
| 156 | + { |
| 157 | + proxy: { |
| 158 | + custom_field: 'value123', |
| 159 | + provider_filter: { |
| 160 | + department: 'Engineering', |
| 161 | + status: 'active', |
| 162 | + }, |
| 163 | + }, |
| 164 | + }, |
| 165 | + { dryRun: true } |
| 166 | + )) as DryRunResult; |
| 167 | + |
| 168 | + console.log('Proxy object:', { |
| 169 | + proxy: { |
| 170 | + custom_field: 'value123', |
| 171 | + provider_filter: { |
| 172 | + department: 'Engineering', |
| 173 | + status: 'active', |
| 174 | + }, |
| 175 | + }, |
| 176 | + }); |
| 177 | + console.log('Serialized URL:', proxyParameters.url); |
| 178 | + |
| 179 | + // Verify proxy parameters are properly serialized |
| 180 | + assert( |
| 181 | + proxyParameters.url.includes('proxy%5Bcustom_field%5D=value123'), |
| 182 | + 'Expected URL to contain proxy custom_field parameter' |
| 183 | + ); |
| 184 | + assert( |
| 185 | + proxyParameters.url.includes('proxy%5Bprovider_filter%5D%5Bdepartment%5D=Engineering'), |
| 186 | + 'Expected URL to contain nested proxy department parameter' |
| 187 | + ); |
| 188 | + assert( |
| 189 | + proxyParameters.url.includes('proxy%5Bprovider_filter%5D%5Bstatus%5D=active'), |
| 190 | + 'Expected URL to contain nested proxy status parameter' |
| 191 | + ); |
| 192 | + console.log('✅ Proxy parameters with nested objects serialized correctly\n'); |
| 193 | + |
| 194 | + /* |
| 195 | + * Example 6: Complex combined scenario |
| 196 | + * Demonstrates combining filters, proxy parameters, and other query parameters |
| 197 | + */ |
| 198 | + console.log('6️⃣ Complex Combined Scenario Test'); |
| 199 | + const complexScenario = (await employeesTool.execute( |
| 200 | + { |
| 201 | + filter: { |
| 202 | + updated_after: '2023-09-01T00:00:00.000Z', |
| 203 | + email: 'admin@company.com', |
| 204 | + }, |
| 205 | + proxy: { |
| 206 | + include_terminated: 'false', |
| 207 | + custom_sorting: { |
| 208 | + field: 'hire_date', |
| 209 | + order: 'desc', |
| 210 | + }, |
| 211 | + }, |
| 212 | + fields: 'id,first_name,last_name,email,hire_date', |
| 213 | + page_size: '50', |
| 214 | + }, |
| 215 | + { dryRun: true } |
| 216 | + )) as DryRunResult; |
| 217 | + |
| 218 | + console.log('Complex parameters:', { |
| 219 | + filter: { |
| 220 | + updated_after: '2023-09-01T00:00:00.000Z', |
| 221 | + email: 'admin@company.com', |
| 222 | + }, |
| 223 | + proxy: { |
| 224 | + include_terminated: 'false', |
| 225 | + custom_sorting: { |
| 226 | + field: 'hire_date', |
| 227 | + order: 'desc', |
| 228 | + }, |
| 229 | + }, |
| 230 | + fields: 'id,first_name,last_name,email,hire_date', |
| 231 | + page_size: '50', |
| 232 | + }); |
| 233 | + console.log('Serialized URL:', complexScenario.url); |
| 234 | + |
| 235 | + // Verify complex scenario serialization |
| 236 | + assert( |
| 237 | + complexScenario.url.includes('filter%5Bupdated_after%5D=2023-09-01T00%3A00%3A00.000Z'), |
| 238 | + 'Expected URL to contain complex date filter' |
| 239 | + ); |
| 240 | + assert( |
| 241 | + complexScenario.url.includes('filter%5Bemail%5D=admin%40company.com'), |
| 242 | + 'Expected URL to contain complex email filter' |
| 243 | + ); |
| 244 | + assert( |
| 245 | + complexScenario.url.includes('proxy%5Binclude_terminated%5D=false'), |
| 246 | + 'Expected URL to contain proxy boolean parameter' |
| 247 | + ); |
| 248 | + assert( |
| 249 | + complexScenario.url.includes('proxy%5Bcustom_sorting%5D%5Bfield%5D=hire_date'), |
| 250 | + 'Expected URL to contain nested proxy field parameter' |
| 251 | + ); |
| 252 | + assert( |
| 253 | + complexScenario.url.includes('proxy%5Bcustom_sorting%5D%5Border%5D=desc'), |
| 254 | + 'Expected URL to contain nested proxy order parameter' |
| 255 | + ); |
| 256 | + assert( |
| 257 | + complexScenario.url.includes('fields=id%2Cfirst_name%2Clast_name%2Cemail%2Chire_date'), |
| 258 | + 'Expected URL to contain fields parameter' |
| 259 | + ); |
| 260 | + assert( |
| 261 | + complexScenario.url.includes('page_size=50'), |
| 262 | + 'Expected URL to contain page_size parameter' |
| 263 | + ); |
| 264 | + console.log('✅ Complex combined scenario serialized correctly\n'); |
| 265 | + |
| 266 | + /* |
| 267 | + * Example 7: Edge case - Empty filter objects |
| 268 | + * Demonstrates handling of empty filter objects |
| 269 | + */ |
| 270 | + console.log('7️⃣ Edge Case - Empty Filter Objects Test'); |
| 271 | + const emptyFilterTest = (await employeesTool.execute( |
| 272 | + { |
| 273 | + filter: {}, |
| 274 | + fields: 'id,first_name,last_name', |
| 275 | + }, |
| 276 | + { dryRun: true } |
| 277 | + )) as DryRunResult; |
| 278 | + |
| 279 | + console.log('Empty filter object:', { filter: {}, fields: 'id,first_name,last_name' }); |
| 280 | + console.log('Serialized URL:', emptyFilterTest.url); |
| 281 | + |
| 282 | + // Verify that empty filter objects don't create problematic parameters |
| 283 | + assert( |
| 284 | + emptyFilterTest.url.includes('fields=id%2Cfirst_name%2Clast_name'), |
| 285 | + 'Expected URL to contain fields parameter even with empty filter' |
| 286 | + ); |
| 287 | + // Empty objects should not create parameters |
| 288 | + assert( |
| 289 | + !emptyFilterTest.url.includes('filter='), |
| 290 | + 'Expected URL to not contain empty filter parameter' |
| 291 | + ); |
| 292 | + console.log('✅ Empty filter objects handled correctly\n'); |
| 293 | +}; |
| 294 | + |
| 295 | +// Run the example |
| 296 | +hriseEmployeeFilters(); |
0 commit comments