diff --git a/.github/workflows/validate-structure.yml b/.github/workflows/validate-structure.yml index d9da4db770..b3507275c1 100644 --- a/.github/workflows/validate-structure.yml +++ b/.github/workflows/validate-structure.yml @@ -26,14 +26,20 @@ jobs: run: cp .github/scripts/validate-structure.js "$RUNNER_TEMP/validate-structure.js" - name: Fetch pull request head + id: fetch_head env: - PR_REMOTE_URL: https://x-access-token:${{ github.token }}@github.com/${{ github.event.pull_request.head.repo.full_name }}.git + PR_REMOTE_URL: https://github.com/${{ github.event.pull_request.head.repo.full_name }}.git PR_HEAD_REF: ${{ github.event.pull_request.head.ref }} run: | git remote remove pr >/dev/null 2>&1 || true git remote add pr "$PR_REMOTE_URL" - git fetch pr "$PR_HEAD_REF":pr-head --depth=1 - git checkout pr-head + if git fetch pr "$PR_HEAD_REF":pr-head --depth=1; then + git checkout pr-head + echo "fetched=true" >> "$GITHUB_OUTPUT" + else + echo "::warning::Unable to fetch fork repository. Skipping structure validation." + echo "fetched=false" >> "$GITHUB_OUTPUT" + fi - name: Use Node.js 18 uses: actions/setup-node@v4 @@ -41,6 +47,7 @@ jobs: node-version: 18 - name: Validate folder layout + if: ${{ steps.fetch_head.outputs.fetched == 'true' }} id: validate continue-on-error: true run: node "$RUNNER_TEMP/validate-structure.js" origin/${{ github.event.pull_request.base.ref }}...HEAD @@ -59,7 +66,7 @@ jobs: owner, repo, issue_number: pullNumber, - body: `Thank you for your contribution. However, it doesn't comply with our contributing guidelines. As a reminder, the general requirements (as outlined in the [CONTRIBUTING.md file](https://github.com/ServiceNowDevProgram/code-snippets/blob/main/CONTRIBUTING.md)) are the following: follow the folder+subfolder+snippetfolder guidelines and include a README.md file explaining what the code snippet does. Review your contribution against the guidelines and make the necessary adjustments. Closing this for now. Once you make additional changes, feel free to re-open this Pull Request or create a new one.`.trim() + body: `Thank you for your contribution. However, it doesn't comply with our contributing guidelines. As a reminder, the general requirements (as outlined in the [CONTRIBUTING.md file](https://github.com/ServiceNowDevProgram/code-snippets/blob/main/CONTRIBUTING.md)) are the following: follow the folder+subfolder guidelines and include a README.md file explaining what the code snippet does. Review your contribution against the guidelines and make the necessary adjustments. Closing this for now. Once you make additional changes, feel free to re-open this Pull Request or create a new one.`.trim() }); await github.rest.pulls.update({ @@ -72,4 +79,3 @@ jobs: - name: Mark job as failed if validation failed if: ${{ steps.validate.outcome == 'failure' }} run: exit 1 - diff --git a/.gitignore b/.gitignore index e43b0f9889..68a3cd2015 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ -.DS_Store +.DS_Store + +# Claude Code settings +.claude/ +settings.local.json diff --git a/Core ServiceNow APIs/GlideAggregate/Snippet2/readme.md b/Core ServiceNow APIs/GlideAggregate/Snippet2/readme.md new file mode 100644 index 0000000000..679f40eee6 --- /dev/null +++ b/Core ServiceNow APIs/GlideAggregate/Snippet2/readme.md @@ -0,0 +1 @@ +this is my readme file diff --git a/Core ServiceNow APIs/GlideAggregate/Snippet2/snippet.js b/Core ServiceNow APIs/GlideAggregate/Snippet2/snippet.js new file mode 100644 index 0000000000..05c85c59d7 --- /dev/null +++ b/Core ServiceNow APIs/GlideAggregate/Snippet2/snippet.js @@ -0,0 +1 @@ +//vargr diff --git a/Core ServiceNow APIs/GlideQuery/Conditional Field Selection/README.md b/Core ServiceNow APIs/GlideQuery/Conditional Field Selection/README.md new file mode 100644 index 0000000000..1bf21ae9ff --- /dev/null +++ b/Core ServiceNow APIs/GlideQuery/Conditional Field Selection/README.md @@ -0,0 +1,24 @@ +# Conditional Field Selection with GlideQuery + +This snippet demonstrates how to dynamically select different sets of fields based on conditions using GlideQuery. This pattern is useful when you need to optimize queries by selecting only the fields you actually need based on runtime conditions, or when building flexible APIs that return different data sets based on user permissions or preferences. + +## Use Cases + +- **Permission-based field selection**: Select different fields based on user roles or permissions +- **Performance optimization**: Only fetch expensive fields when needed +- **API flexibility**: Return different data sets based on request parameters +- **Conditional aggregations**: Include summary fields only when specific conditions are met + +## Key Benefits + +- **Reduced data transfer**: Only fetch the fields you need +- **Performance optimization**: Avoid expensive field calculations when unnecessary +- **Security**: Dynamically exclude sensitive fields based on permissions +- **Maintainable code**: Centralized logic for field selection patterns + +## Examples Included + +1. **Role-based field selection**: Different fields for different user roles +2. **Performance-optimized queries**: Conditional inclusion of expensive fields +3. **Dynamic field arrays**: Building field lists programmatically +4. **Chained conditional selection**: Multiple condition-based selections \ No newline at end of file diff --git a/Core ServiceNow APIs/GlideQuery/Conditional Field Selection/conditional_field_selection.js b/Core ServiceNow APIs/GlideQuery/Conditional Field Selection/conditional_field_selection.js new file mode 100644 index 0000000000..6e2055fead --- /dev/null +++ b/Core ServiceNow APIs/GlideQuery/Conditional Field Selection/conditional_field_selection.js @@ -0,0 +1,205 @@ +// Conditional Field Selection with GlideQuery +// Demonstrates dynamically selecting different fields based on runtime conditions + +/** + * Example 1: Role-based Field Selection + * Select different incident fields based on user's role + */ +function getIncidentsByRole(userRole, assignedTo) { + // Define base fields that everyone can see + let baseFields = ['number', 'short_description', 'state', 'priority', 'sys_created_on']; + + // Define additional fields based on role + let additionalFields = []; + + if (userRole === 'admin' || userRole === 'security_admin') { + additionalFields = ['caller_id', 'assigned_to', 'assignment_group', 'work_notes', 'comments']; + } else if (userRole === 'itil') { + additionalFields = ['caller_id', 'assigned_to', 'assignment_group']; + } else if (userRole === 'agent') { + additionalFields = ['assigned_to', 'assignment_group']; + } + + // Combine base and additional fields + let fieldsToSelect = baseFields.concat(additionalFields); + + return new GlideQuery('incident') + .where('assigned_to', assignedTo) + .where('state', '!=', 7) // Not closed + .select(fieldsToSelect) + .orderByDesc('sys_created_on') + .toArray(50); +} + +/** + * Example 2: Performance-optimized Field Selection + * Only include expensive fields when specifically requested + */ +function getTasksWithOptionalFields(options) { + options = options || {}; + + // Always include these lightweight fields + let fields = ['sys_id', 'number', 'short_description', 'state']; + + // Conditionally add more expensive fields + if (options.includeUserDetails) { + fields.push('caller_id.name', 'caller_id.email', 'assigned_to.name'); + } + + if (options.includeTimeTracking) { + fields.push('work_start', 'work_end', 'business_duration'); + } + + if (options.includeApprovalInfo) { + fields.push('approval', 'approval_history'); + } + + if (options.includeRelatedRecords) { + fields.push('parent.number', 'caused_by.number'); + } + + let query = new GlideQuery('task') + .where('active', true) + .select(fields); + + if (options.assignmentGroup) { + query.where('assignment_group', options.assignmentGroup); + } + + return query.toArray(100); +} + +/** + * Example 3: Dynamic Field Array Building + * Build field selection based on table structure and permissions + */ +function getDynamicFieldSelection(tableName, userPermissions, includeMetadata) { + let fields = []; + + // Always include sys_id + fields.push('sys_id'); + + // Add fields based on table type + if (tableName === 'incident' || tableName === 'sc_request') { + fields.push('number', 'short_description', 'state', 'priority'); + + if (userPermissions.canViewCaller) { + fields.push('caller_id'); + } + + if (userPermissions.canViewAssignment) { + fields.push('assigned_to', 'assignment_group'); + } + } else if (tableName === 'cmdb_ci') { + fields.push('name', 'operational_status', 'install_status'); + + if (userPermissions.canViewConfiguration) { + fields.push('ip_address', 'fqdn', 'serial_number'); + } + } + + // Add metadata fields if requested + if (includeMetadata) { + fields.push('sys_created_on', 'sys_created_by', 'sys_updated_on', 'sys_updated_by'); + } + + return new GlideQuery(tableName) + .select(fields) + .limit(100) + .toArray(); +} + +/** + * Example 4: Chained Conditional Selection with Method Chaining + * Demonstrate building complex queries with multiple conditions + */ +function getConditionalIncidentData(filters) { + let query = new GlideQuery('incident'); + + // Build base field list + let fields = ['sys_id', 'number', 'short_description', 'state']; + + // Apply filters and modify field selection accordingly + if (filters.priority && filters.priority.length > 0) { + query.where('priority', 'IN', filters.priority); + fields.push('priority'); // Include priority field when filtering by it + } + + if (filters.assignmentGroup) { + query.where('assignment_group', filters.assignmentGroup); + fields.push('assignment_group', 'assigned_to'); // Include assignment fields + } + + if (filters.dateRange) { + query.where('sys_created_on', '>=', filters.dateRange.start) + .where('sys_created_on', '<=', filters.dateRange.end); + fields.push('sys_created_on'); // Include date when filtering by it + } + + if (filters.includeComments) { + fields.push('comments', 'work_notes'); + } + + if (filters.includeResolution) { + fields.push('close_code', 'close_notes', 'resolved_by'); + } + + return query.select(fields) + .orderByDesc('sys_created_on') + .toArray(filters.limit || 50); +} + +/** + * Example 5: Security-conscious Field Selection + * Exclude sensitive fields based on user context + */ +function getSecureUserData(requestingUser, targetUserId) { + let baseFields = ['sys_id', 'name', 'user_name', 'active']; + + // Check if requesting user can see additional details + if (gs.hasRole('user_admin') || requestingUser === targetUserId) { + // Full access - include all standard fields + return new GlideQuery('sys_user') + .where('sys_id', targetUserId) + .select(['sys_id', 'name', 'user_name', 'email', 'phone', 'department', 'title', 'manager', 'active']) + .toArray(1); + } else if (gs.hasRole('hr_admin')) { + // HR access - include HR-relevant fields but not IT details + return new GlideQuery('sys_user') + .where('sys_id', targetUserId) + .select(['sys_id', 'name', 'user_name', 'department', 'title', 'manager', 'active']) + .toArray(1); + } else { + // Limited access - only public information + return new GlideQuery('sys_user') + .where('sys_id', targetUserId) + .select(baseFields) + .toArray(1); + } +} + +// Usage Examples: + +// Role-based selection +var adminIncidents = getIncidentsByRole('admin', gs.getUserID()); + +// Performance-optimized query +var tasksWithDetails = getTasksWithOptionalFields({ + includeUserDetails: true, + includeTimeTracking: false, + assignmentGroup: 'hardware' +}); + +// Dynamic field building +var dynamicData = getDynamicFieldSelection('incident', { + canViewCaller: true, + canViewAssignment: false +}, true); + +// Complex conditional query +var filteredIncidents = getConditionalIncidentData({ + priority: [1, 2], + assignmentGroup: 'network', + includeComments: true, + limit: 25 +}); \ No newline at end of file