@salesforce/afv-skills 1.14.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/activating-datacloud/SKILL.md +0 -1
- package/skills/analyzing-omnistudio-dependencies/SKILL.md +0 -1
- package/skills/applying-slds/SKILL.md +322 -0
- package/skills/applying-slds/checklists.md +83 -0
- package/skills/applying-slds/examples.md +283 -0
- package/skills/applying-slds/guidance/README.md +83 -0
- package/skills/applying-slds/guidance/blueprints-index.md +213 -0
- package/skills/applying-slds/guidance/icons-guidance.md +186 -0
- package/skills/applying-slds/guidance/overviews/borders.md +236 -0
- package/skills/applying-slds/guidance/overviews/color.md +266 -0
- package/skills/applying-slds/guidance/overviews/display-density.md +366 -0
- package/skills/applying-slds/guidance/overviews/icons.md +240 -0
- package/skills/applying-slds/guidance/overviews/illustrations.md +235 -0
- package/skills/applying-slds/guidance/overviews/shadows.md +176 -0
- package/skills/applying-slds/guidance/overviews/spacing.md +216 -0
- package/skills/applying-slds/guidance/overviews/typography.md +323 -0
- package/skills/applying-slds/guidance/overviews/utilities.md +542 -0
- package/skills/applying-slds/guidance/slds-development-guide.md +288 -0
- package/skills/applying-slds/guidance/styling-hooks/borders.md +202 -0
- package/skills/applying-slds/guidance/styling-hooks/color/expressive-palette-hooks.md +153 -0
- package/skills/applying-slds/guidance/styling-hooks/color/index.md +171 -0
- package/skills/applying-slds/guidance/styling-hooks/color/semantic/accent-hooks.md +204 -0
- package/skills/applying-slds/guidance/styling-hooks/color/semantic/feedback-hooks.md +768 -0
- package/skills/applying-slds/guidance/styling-hooks/color/semantic/surface-hooks.md +337 -0
- package/skills/applying-slds/guidance/styling-hooks/color/system-hooks.md +132 -0
- package/skills/applying-slds/guidance/styling-hooks/index.md +327 -0
- package/skills/applying-slds/guidance/styling-hooks/shadows.md +238 -0
- package/skills/applying-slds/guidance/styling-hooks/spacing.md +254 -0
- package/skills/applying-slds/guidance/styling-hooks/typography.md +448 -0
- package/skills/applying-slds/guidance/utilities/alignment.md +119 -0
- package/skills/applying-slds/guidance/utilities/borders.md +131 -0
- package/skills/applying-slds/guidance/utilities/box.md +125 -0
- package/skills/applying-slds/guidance/utilities/color.md +165 -0
- package/skills/applying-slds/guidance/utilities/dark-mode.md +111 -0
- package/skills/applying-slds/guidance/utilities/description-list.md +168 -0
- package/skills/applying-slds/guidance/utilities/floats.md +117 -0
- package/skills/applying-slds/guidance/utilities/grid.md +264 -0
- package/skills/applying-slds/guidance/utilities/horizontal-list.md +110 -0
- package/skills/applying-slds/guidance/utilities/hyphenation.md +84 -0
- package/skills/applying-slds/guidance/utilities/index.md +205 -0
- package/skills/applying-slds/guidance/utilities/interactions.md +89 -0
- package/skills/applying-slds/guidance/utilities/layout.md +109 -0
- package/skills/applying-slds/guidance/utilities/line-clamp.md +131 -0
- package/skills/applying-slds/guidance/utilities/margin.md +155 -0
- package/skills/applying-slds/guidance/utilities/media-object.md +161 -0
- package/skills/applying-slds/guidance/utilities/name-value-list.md +152 -0
- package/skills/applying-slds/guidance/utilities/padding.md +155 -0
- package/skills/applying-slds/guidance/utilities/position.md +177 -0
- package/skills/applying-slds/guidance/utilities/print.md +114 -0
- package/skills/applying-slds/guidance/utilities/scrollable.md +126 -0
- package/skills/applying-slds/guidance/utilities/sizing.md +190 -0
- package/skills/applying-slds/guidance/utilities/themes.md +121 -0
- package/skills/applying-slds/guidance/utilities/truncate.md +127 -0
- package/skills/applying-slds/guidance/utilities/typography.md +166 -0
- package/skills/applying-slds/guidance/utilities/vertical-list.md +166 -0
- package/skills/applying-slds/guidance/utilities/visibility.md +228 -0
- package/skills/applying-slds/metadata/README.md +84 -0
- package/skills/applying-slds/metadata/blueprints/components/accordion.yaml +304 -0
- package/skills/applying-slds/metadata/blueprints/components/activity-timeline.yaml +92 -0
- package/skills/applying-slds/metadata/blueprints/components/alert.yaml +103 -0
- package/skills/applying-slds/metadata/blueprints/components/app-launcher.yaml +94 -0
- package/skills/applying-slds/metadata/blueprints/components/avatar-group.yaml +81 -0
- package/skills/applying-slds/metadata/blueprints/components/avatar.yaml +97 -0
- package/skills/applying-slds/metadata/blueprints/components/badges.yaml +102 -0
- package/skills/applying-slds/metadata/blueprints/components/brand-band.yaml +198 -0
- package/skills/applying-slds/metadata/blueprints/components/breadcrumbs.yaml +95 -0
- package/skills/applying-slds/metadata/blueprints/components/builder-header.yaml +192 -0
- package/skills/applying-slds/metadata/blueprints/components/button-groups.yaml +82 -0
- package/skills/applying-slds/metadata/blueprints/components/button-icons.yaml +295 -0
- package/skills/applying-slds/metadata/blueprints/components/buttons.yaml +230 -0
- package/skills/applying-slds/metadata/blueprints/components/cards.yaml +124 -0
- package/skills/applying-slds/metadata/blueprints/components/carousel.yaml +140 -0
- package/skills/applying-slds/metadata/blueprints/components/chat.yaml +179 -0
- package/skills/applying-slds/metadata/blueprints/components/checkbox-button-group.yaml +192 -0
- package/skills/applying-slds/metadata/blueprints/components/checkbox-button.yaml +204 -0
- package/skills/applying-slds/metadata/blueprints/components/checkbox-toggle.yaml +177 -0
- package/skills/applying-slds/metadata/blueprints/components/checkbox.yaml +108 -0
- package/skills/applying-slds/metadata/blueprints/components/color-picker.yaml +172 -0
- package/skills/applying-slds/metadata/blueprints/components/combobox.yaml +136 -0
- package/skills/applying-slds/metadata/blueprints/components/counter.yaml +147 -0
- package/skills/applying-slds/metadata/blueprints/components/data-tables.yaml +157 -0
- package/skills/applying-slds/metadata/blueprints/components/datepickers.yaml +130 -0
- package/skills/applying-slds/metadata/blueprints/components/datetime-picker.yaml +155 -0
- package/skills/applying-slds/metadata/blueprints/components/docked-composer.yaml +201 -0
- package/skills/applying-slds/metadata/blueprints/components/docked-form-footer.yaml +161 -0
- package/skills/applying-slds/metadata/blueprints/components/docked-utility-bar.yaml +175 -0
- package/skills/applying-slds/metadata/blueprints/components/drop-zone.yaml +115 -0
- package/skills/applying-slds/metadata/blueprints/components/dueling-picklist.yaml +196 -0
- package/skills/applying-slds/metadata/blueprints/components/dynamic-icons.yaml +128 -0
- package/skills/applying-slds/metadata/blueprints/components/dynamic-menu.yaml +141 -0
- package/skills/applying-slds/metadata/blueprints/components/expandable-section.yaml +115 -0
- package/skills/applying-slds/metadata/blueprints/components/expression.yaml +143 -0
- package/skills/applying-slds/metadata/blueprints/components/feeds.yaml +125 -0
- package/skills/applying-slds/metadata/blueprints/components/file-selector.yaml +154 -0
- package/skills/applying-slds/metadata/blueprints/components/files.yaml +119 -0
- package/skills/applying-slds/metadata/blueprints/components/form-element.yaml +145 -0
- package/skills/applying-slds/metadata/blueprints/components/global-header.yaml +120 -0
- package/skills/applying-slds/metadata/blueprints/components/global-navigation.yaml +100 -0
- package/skills/applying-slds/metadata/blueprints/components/icons.yaml +138 -0
- package/skills/applying-slds/metadata/blueprints/components/illustration.yaml +205 -0
- package/skills/applying-slds/metadata/blueprints/components/input.yaml +151 -0
- package/skills/applying-slds/metadata/blueprints/components/list-builder.yaml +127 -0
- package/skills/applying-slds/metadata/blueprints/components/lookups.yaml +132 -0
- package/skills/applying-slds/metadata/blueprints/components/map.yaml +118 -0
- package/skills/applying-slds/metadata/blueprints/components/menus.yaml +134 -0
- package/skills/applying-slds/metadata/blueprints/components/modals.yaml +152 -0
- package/skills/applying-slds/metadata/blueprints/components/notifications.yaml +88 -0
- package/skills/applying-slds/metadata/blueprints/components/page-headers.yaml +135 -0
- package/skills/applying-slds/metadata/blueprints/components/panels.yaml +149 -0
- package/skills/applying-slds/metadata/blueprints/components/path.yaml +154 -0
- package/skills/applying-slds/metadata/blueprints/components/picklist.yaml +125 -0
- package/skills/applying-slds/metadata/blueprints/components/pills.yaml +154 -0
- package/skills/applying-slds/metadata/blueprints/components/popovers.yaml +120 -0
- package/skills/applying-slds/metadata/blueprints/components/progress-bar.yaml +110 -0
- package/skills/applying-slds/metadata/blueprints/components/progress-indicator.yaml +133 -0
- package/skills/applying-slds/metadata/blueprints/components/progress-ring.yaml +102 -0
- package/skills/applying-slds/metadata/blueprints/components/prompt.yaml +126 -0
- package/skills/applying-slds/metadata/blueprints/components/publishers.yaml +178 -0
- package/skills/applying-slds/metadata/blueprints/components/radio-button-group.yaml +172 -0
- package/skills/applying-slds/metadata/blueprints/components/radio-group.yaml +112 -0
- package/skills/applying-slds/metadata/blueprints/components/rich-text-editor.yaml +135 -0
- package/skills/applying-slds/metadata/blueprints/components/scoped-notifications.yaml +188 -0
- package/skills/applying-slds/metadata/blueprints/components/scoped-tabs.yaml +97 -0
- package/skills/applying-slds/metadata/blueprints/components/select.yaml +127 -0
- package/skills/applying-slds/metadata/blueprints/components/setup-assistant.yaml +152 -0
- package/skills/applying-slds/metadata/blueprints/components/slider.yaml +111 -0
- package/skills/applying-slds/metadata/blueprints/components/spinners.yaml +135 -0
- package/skills/applying-slds/metadata/blueprints/components/split-view.yaml +112 -0
- package/skills/applying-slds/metadata/blueprints/components/summary-detail.yaml +103 -0
- package/skills/applying-slds/metadata/blueprints/components/tabs.yaml +138 -0
- package/skills/applying-slds/metadata/blueprints/components/textarea.yaml +116 -0
- package/skills/applying-slds/metadata/blueprints/components/tiles.yaml +108 -0
- package/skills/applying-slds/metadata/blueprints/components/timepicker.yaml +111 -0
- package/skills/applying-slds/metadata/blueprints/components/toast.yaml +154 -0
- package/skills/applying-slds/metadata/blueprints/components/tooltips.yaml +107 -0
- package/skills/applying-slds/metadata/blueprints/components/tree-grid.yaml +116 -0
- package/skills/applying-slds/metadata/blueprints/components/trees.yaml +116 -0
- package/skills/applying-slds/metadata/blueprints/components/trial-bar.yaml +112 -0
- package/skills/applying-slds/metadata/blueprints/components/vertical-navigation.yaml +130 -0
- package/skills/applying-slds/metadata/blueprints/components/vertical-tabs.yaml +140 -0
- package/skills/applying-slds/metadata/blueprints/components/visual-picker.yaml +150 -0
- package/skills/applying-slds/metadata/blueprints/components/welcome-mat.yaml +136 -0
- package/skills/applying-slds/metadata/hooks-index.json +6272 -0
- package/skills/applying-slds/metadata/icon-metadata.json +38466 -0
- package/skills/applying-slds/metadata/utilities-index.json +21912 -0
- package/skills/applying-slds/references/component-selection.md +112 -0
- package/skills/applying-slds/references/icons-decision-guide.md +124 -0
- package/skills/applying-slds/references/styling-decision-guide.md +228 -0
- package/skills/applying-slds/references/utilities-quick-ref.md +125 -0
- package/skills/applying-slds/scripts/search-blueprints.cjs +117 -0
- package/skills/applying-slds/scripts/search-hooks.cjs +139 -0
- package/skills/applying-slds/scripts/search-icons.cjs +174 -0
- package/skills/applying-slds/scripts/search-utilities.cjs +161 -0
- package/skills/building-mobile-apps/SKILL.md +0 -1
- package/skills/building-omnistudio-callable-apex/SKILL.md +0 -1
- package/skills/building-omnistudio-datamapper/SKILL.md +0 -1
- package/skills/building-omnistudio-flexcard/SKILL.md +0 -1
- package/skills/building-omnistudio-integration-procedure/SKILL.md +0 -1
- package/skills/building-omnistudio-omniscript/SKILL.md +0 -1
- package/skills/building-sf-integrations/SKILL.md +0 -1
- package/skills/configuring-connected-apps/SKILL.md +0 -1
- package/skills/connecting-datacloud/SKILL.md +0 -1
- package/skills/creating-b2b-commerce-store/SKILL.md +0 -1
- package/skills/debugging-apex-logs/SKILL.md +0 -1
- package/skills/deploying-metadata/SKILL.md +0 -1
- package/skills/deploying-omnistudio-datapacks/SKILL.md +0 -1
- package/skills/developing-agentforce/SKILL.md +0 -1
- package/skills/fetching-salesforce-docs/SKILL.md +0 -1
- package/skills/generating-custom-lightning-type/SKILL.md +17 -39
- package/skills/generating-custom-lightning-type/assets/primitive-types-and-constraints.md +41 -0
- package/skills/generating-custom-lightning-type/references/widget-rendition.md +124 -0
- package/skills/generating-lwc-components/SKILL.md +0 -1
- package/skills/generating-mermaid-diagrams/SKILL.md +0 -1
- package/skills/generating-visual-diagrams/SKILL.md +0 -1
- package/skills/handling-sf-data/SKILL.md +0 -1
- package/skills/harmonizing-datacloud/SKILL.md +0 -1
- package/skills/integrating-b2b-commerce-open-code-components/SKILL.md +0 -1
- package/skills/investigating-agentforce-architecture/README.md +156 -0
- package/skills/investigating-agentforce-architecture/SKILL.md +230 -0
- package/skills/investigating-agentforce-architecture/assets/cli/describe_sobject.yaml +16 -0
- package/skills/investigating-agentforce-architecture/assets/cli/describe_tooling_sobject.yaml +17 -0
- package/skills/investigating-agentforce-architecture/assets/cli/list_metadata_genaiprompttemplate.yaml +17 -0
- package/skills/investigating-agentforce-architecture/assets/cli/org_display.yaml +15 -0
- package/skills/investigating-agentforce-architecture/assets/cli/retrieve_genai_plugin.yaml +18 -0
- package/skills/investigating-agentforce-architecture/assets/cli/show_access_token.yaml +27 -0
- package/skills/investigating-agentforce-architecture/assets/mermaid/action_tree.mmd +20 -0
- package/skills/investigating-agentforce-architecture/assets/mermaid/data_flow.mmd +19 -0
- package/skills/investigating-agentforce-architecture/assets/mermaid/dependency_graph.mmd +19 -0
- package/skills/investigating-agentforce-architecture/assets/mermaid/invocation_sequence.mmd +20 -0
- package/skills/investigating-agentforce-architecture/assets/mermaid/planner_state.mmd +18 -0
- package/skills/investigating-agentforce-architecture/assets/soql/apex_class_bodies_by_ids.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/apex_class_bodies_by_names.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/bot_definition_details.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/bot_version_lookup.soql +4 -0
- package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_by_ids.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_ids_by_names.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_view_by_durable_ids.soql +4 -0
- package/skills/investigating-agentforce-architecture/assets/soql/flow_metadata_by_id.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/functions_by_plugins.soql +5 -0
- package/skills/investigating-agentforce-architecture/assets/soql/planner_attrs_by_parent_ids.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/planner_bundle_functions.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/planner_definition_by_agent_chain.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/plugin_functions_by_plugin_ids.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/plugin_instructions_by_plugin_ids.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/plugins_by_planner.soql +4 -0
- package/skills/investigating-agentforce-architecture/references/architecture_sections.md +243 -0
- package/skills/investigating-agentforce-architecture/references/contract.json +244 -0
- package/skills/investigating-agentforce-architecture/references/soql_fields.md +512 -0
- package/skills/investigating-agentforce-architecture/scripts/_shared/__init__.py +1 -0
- package/skills/investigating-agentforce-architecture/scripts/_shared/fs_guard.py +329 -0
- package/skills/investigating-agentforce-architecture/scripts/_shared/paths.py +110 -0
- package/skills/investigating-agentforce-architecture/scripts/_shared/runtime.py +59 -0
- package/skills/investigating-agentforce-architecture/scripts/_shared/sql.py +10 -0
- package/skills/investigating-agentforce-architecture/scripts/cache_check.py +234 -0
- package/skills/investigating-agentforce-architecture/scripts/config.py +131 -0
- package/skills/investigating-agentforce-architecture/scripts/fetch_soql.py +689 -0
- package/skills/investigating-agentforce-architecture/scripts/finalize.py +295 -0
- package/skills/investigating-agentforce-architecture/scripts/main.py +2835 -0
- package/skills/investigating-agentforce-architecture/scripts/metadata_listing.py +265 -0
- package/skills/investigating-agentforce-architecture/scripts/parallel_retrieve.py +69 -0
- package/skills/investigating-agentforce-architecture/scripts/parse_bundle.py +215 -0
- package/skills/investigating-agentforce-architecture/scripts/parse_wave.py +845 -0
- package/skills/investigating-agentforce-architecture/scripts/probe_channels.py +302 -0
- package/skills/investigating-agentforce-architecture/scripts/render_architecture.py +1043 -0
- package/skills/investigating-agentforce-architecture/scripts/resolve_bot.py +255 -0
- package/skills/investigating-agentforce-architecture/scripts/resolve_invocation_target.py +130 -0
- package/skills/investigating-agentforce-architecture/scripts/rest_client.py +763 -0
- package/skills/investigating-agentforce-architecture/scripts/retrieve_planner.py +13 -0
- package/skills/investigating-agentforce-architecture/scripts/sf_cli.py +242 -0
- package/skills/investigating-agentforce-architecture/scripts/soql_loader.py +253 -0
- package/skills/investigating-agentforce-architecture/scripts/summarize_tree.py +143 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/__init__.py +0 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/_bootstrap.py +23 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/fixtures/__init__.py +0 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/fixtures/genai_payloads.py +400 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_cache_check.py +307 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_cache_check_main.py +283 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_config.py +115 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_end_to_end_fixture.py +651 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_finalize.py +278 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_flow_children_inflation.py +582 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_fs_guard.py +113 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_iterative_wave_b.py +478 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_main_pipeline.py +3359 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_parallel_retrieve.py +131 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_bundle.py +400 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave.py +644 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_classifiers.py +224 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_helpers.py +380 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_main.py +397 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_per_branch_visited.py +244 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_probe_channels.py +359 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_probe_cli_recipes.py +185 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_render_architecture.py +810 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_bot.py +203 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_creds.py +157 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_invocation_target.py +145 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_rest_client.py +1253 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_runtime_override.py +100 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_sf_cli.py +261 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_signature_stamping.py +466 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_soql_loader.py +501 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_summarize_tree.py +241 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_write_emit_ctx.py +480 -0
- package/skills/investigating-agentforce-architecture/tools/emit_env.py +157 -0
- package/skills/investigating-agentforce-architecture/tools/emit_result.py +262 -0
- package/skills/investigating-agentforce-architecture/tools/sanitize.py +33 -0
- package/skills/investigating-agentforce-architecture/tools/write_emit_ctx.py +332 -0
- package/skills/investigating-agentforce-d360/README.md +123 -0
- package/skills/investigating-agentforce-d360/SKILL.md +163 -0
- package/skills/investigating-agentforce-d360/assets/dc/app_generation.sql +51 -0
- package/skills/investigating-agentforce-d360/assets/dc/content_category.sql +44 -0
- package/skills/investigating-agentforce-d360/assets/dc/content_quality.sql +41 -0
- package/skills/investigating-agentforce-d360/assets/dc/discover_sessions.sql +36 -0
- package/skills/investigating-agentforce-d360/assets/dc/feedback.sql +47 -0
- package/skills/investigating-agentforce-d360/assets/dc/feedback_details.sql +38 -0
- package/skills/investigating-agentforce-d360/assets/dc/gateway_records.sql +45 -0
- package/skills/investigating-agentforce-d360/assets/dc/gateway_request_llm.sql +50 -0
- package/skills/investigating-agentforce-d360/assets/dc/gateway_request_metadata.sql +44 -0
- package/skills/investigating-agentforce-d360/assets/dc/gateway_request_tags.sql +42 -0
- package/skills/investigating-agentforce-d360/assets/dc/gateway_requests.sql +89 -0
- package/skills/investigating-agentforce-d360/assets/dc/gateway_responses.sql +43 -0
- package/skills/investigating-agentforce-d360/assets/dc/generations.sql +52 -0
- package/skills/investigating-agentforce-d360/assets/dc/interactions.sql +53 -0
- package/skills/investigating-agentforce-d360/assets/dc/messages.sql +53 -0
- package/skills/investigating-agentforce-d360/assets/dc/messaging_session.sql +37 -0
- package/skills/investigating-agentforce-d360/assets/dc/moment_interactions.sql +34 -0
- package/skills/investigating-agentforce-d360/assets/dc/moments.sql +39 -0
- package/skills/investigating-agentforce-d360/assets/dc/participants.sql +48 -0
- package/skills/investigating-agentforce-d360/assets/dc/sessions.sql +78 -0
- package/skills/investigating-agentforce-d360/assets/dc/steps.sql +64 -0
- package/skills/investigating-agentforce-d360/assets/dc/tag_associations.sql +46 -0
- package/skills/investigating-agentforce-d360/assets/dc/tag_definition_associations.sql +37 -0
- package/skills/investigating-agentforce-d360/assets/dc/tag_definitions.sql +50 -0
- package/skills/investigating-agentforce-d360/assets/dc/tags.sql +37 -0
- package/skills/investigating-agentforce-d360/assets/dc/telemetry_spans.sql +55 -0
- package/skills/investigating-agentforce-d360/references/artifacts.md +50 -0
- package/skills/investigating-agentforce-d360/references/dc_dmo_fields.md +823 -0
- package/skills/investigating-agentforce-d360/references/dc_pipeline_contract.md +608 -0
- package/skills/investigating-agentforce-d360/scripts/_shared/__init__.py +2 -0
- package/skills/investigating-agentforce-d360/scripts/_shared/cli_override.py +98 -0
- package/skills/investigating-agentforce-d360/scripts/_shared/fs_guard.py +334 -0
- package/skills/investigating-agentforce-d360/scripts/_shared/paths.py +155 -0
- package/skills/investigating-agentforce-d360/scripts/_shared/runtime.py +59 -0
- package/skills/investigating-agentforce-d360/scripts/_shared/sql.py +14 -0
- package/skills/investigating-agentforce-d360/scripts/assemble_dc.py +1624 -0
- package/skills/investigating-agentforce-d360/scripts/config.py +45 -0
- package/skills/investigating-agentforce-d360/scripts/dc.py +188 -0
- package/skills/investigating-agentforce-d360/scripts/discover_sessions.py +556 -0
- package/skills/investigating-agentforce-d360/scripts/fetch_dc.py +1045 -0
- package/skills/investigating-agentforce-d360/scripts/render_dc.py +1750 -0
- package/skills/investigating-agentforce-d360/scripts/resolve_session.py +264 -0
- package/skills/investigating-agentforce-d360/scripts/storage.py +92 -0
- package/skills/investigating-agentforce-d360/scripts/tests/__init__.py +0 -0
- package/skills/investigating-agentforce-d360/scripts/tests/_bootstrap.py +15 -0
- package/skills/investigating-agentforce-d360/scripts/tests/fixtures/__init__.py +0 -0
- package/skills/investigating-agentforce-d360/scripts/tests/fixtures/synthetic_session.py +424 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_bootstrap_and_mode.py +115 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_gateway_direct.py +220 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_gateway_direct_integration.py +158 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_helpers.py +287 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_integration.py +247 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_dc_and_resolve_session.py +433 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_discover_sessions.py +458 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_discover_sessions_grep_ci.py +193 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_helpers.py +266 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_identity.py +528 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_main.py +251 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_waterfall.py +229 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_waterfall_full.py +283 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_identity_coherence.py +327 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_branches.py +256 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_gateway_direct.py +130 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_helpers.py +291 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_integration.py +220 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_planner_llm_calls.py +284 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_show_prompts_gating.py +215 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_resolve_from_disk.py +100 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_resolve_session_main.py +149 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_runtime_override.py +104 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_session_shape.py +95 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_session_shape_dropped_by_stdm.py +85 -0
- package/skills/managing-managed-event-subscription/SKILL.md +152 -0
- package/skills/managing-managed-event-subscription/assets/managed-event-subscription-template.xml +20 -0
- package/skills/managing-managed-event-subscription/references/delete-guide.md +57 -0
- package/skills/managing-managed-event-subscription/references/topic-name-formats.md +26 -0
- package/skills/managing-managed-event-subscription/references/update-constraints.md +30 -0
- package/skills/modeling-omnistudio-epc-catalog/SKILL.md +0 -1
- package/skills/observing-agentforce/SKILL.md +0 -1
- package/skills/orchestrating-datacloud/SKILL.md +0 -1
- package/skills/preparing-datacloud/SKILL.md +0 -1
- package/skills/querying-soql/SKILL.md +0 -1
- package/skills/retrieving-datacloud/SKILL.md +0 -1
- package/skills/running-apex-tests/SKILL.md +0 -1
- package/skills/running-code-analyzer/SKILL.md +0 -1
- package/skills/segmenting-datacloud/SKILL.md +0 -1
- package/skills/testing-agentforce/SKILL.md +0 -1
- package/skills/uplifting-components-to-slds2/SKILL.md +3 -2
- package/skills/uplifting-components-to-slds2/references/color-hooks-decision-guide.md +30 -9
- package/skills/uplifting-components-to-slds2/references/examples.md +24 -6
- package/skills/validating-slds/SKILL.md +262 -0
- package/skills/validating-slds/references/quality-checks.md +308 -0
- package/skills/validating-slds/references/report-format.md +302 -0
- package/skills/validating-slds/scripts/analyze-quality.cjs +521 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Tests for fs_guard Python-importable validators + api_version check."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import unittest
|
|
5
|
+
|
|
6
|
+
from . import _bootstrap # noqa: F401
|
|
7
|
+
|
|
8
|
+
from config import fs_guard # type: ignore — re-exported from _shared/
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ValidateApiNameTests(unittest.TestCase):
|
|
12
|
+
def test_valid_name_passes(self):
|
|
13
|
+
fs_guard.validate_api_name("MyAgent", label="name")
|
|
14
|
+
fs_guard.validate_api_name("Foo_v2", label="name")
|
|
15
|
+
fs_guard.validate_api_name("x", label="name")
|
|
16
|
+
|
|
17
|
+
def test_dotdot_rejected(self):
|
|
18
|
+
with self.assertRaises(fs_guard.ValidationError):
|
|
19
|
+
fs_guard.validate_api_name("..", label="agent_api_name")
|
|
20
|
+
|
|
21
|
+
def test_slash_rejected(self):
|
|
22
|
+
with self.assertRaises(fs_guard.ValidationError):
|
|
23
|
+
fs_guard.validate_api_name("foo/bar", label="agent_api_name")
|
|
24
|
+
|
|
25
|
+
def test_dotdot_in_path_segment_rejected(self):
|
|
26
|
+
with self.assertRaises(fs_guard.ValidationError):
|
|
27
|
+
fs_guard.validate_api_name("foo/../bar", label="agent_api_name")
|
|
28
|
+
|
|
29
|
+
def test_null_byte_rejected(self):
|
|
30
|
+
with self.assertRaises(fs_guard.ValidationError):
|
|
31
|
+
fs_guard.validate_api_name("foo\x00bar", label="name")
|
|
32
|
+
|
|
33
|
+
def test_empty_string_rejected(self):
|
|
34
|
+
with self.assertRaises(fs_guard.ValidationError):
|
|
35
|
+
fs_guard.validate_api_name("", label="name")
|
|
36
|
+
|
|
37
|
+
def test_none_rejected(self):
|
|
38
|
+
with self.assertRaises(fs_guard.ValidationError):
|
|
39
|
+
fs_guard.validate_api_name(None, label="name")
|
|
40
|
+
|
|
41
|
+
def test_non_string_rejected(self):
|
|
42
|
+
with self.assertRaises(fs_guard.ValidationError):
|
|
43
|
+
fs_guard.validate_api_name(42, label="name")
|
|
44
|
+
|
|
45
|
+
def test_validation_error_carries_label(self):
|
|
46
|
+
with self.assertRaises(fs_guard.ValidationError) as ctx:
|
|
47
|
+
fs_guard.validate_api_name("bad/name", label="agent_api_name")
|
|
48
|
+
self.assertEqual(ctx.exception.label, "agent_api_name")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ValidateApiVersionTests(unittest.TestCase):
|
|
52
|
+
def test_v60_0_ok(self):
|
|
53
|
+
fs_guard.validate_api_version("v60.0", label="api_version")
|
|
54
|
+
|
|
55
|
+
def test_v66_0_ok(self):
|
|
56
|
+
fs_guard.validate_api_version("v66.0", label="api_version")
|
|
57
|
+
|
|
58
|
+
def test_v60_no_minor_rejected(self):
|
|
59
|
+
with self.assertRaises(fs_guard.ValidationError):
|
|
60
|
+
fs_guard.validate_api_version("v60", label="api_version")
|
|
61
|
+
|
|
62
|
+
def test_bare_number_rejected(self):
|
|
63
|
+
with self.assertRaises(fs_guard.ValidationError):
|
|
64
|
+
fs_guard.validate_api_version("60.0", label="api_version")
|
|
65
|
+
|
|
66
|
+
def test_slash_rejected(self):
|
|
67
|
+
with self.assertRaises(fs_guard.ValidationError):
|
|
68
|
+
fs_guard.validate_api_version("v60.0/../", label="api_version")
|
|
69
|
+
|
|
70
|
+
def test_dotdot_rejected(self):
|
|
71
|
+
with self.assertRaises(fs_guard.ValidationError):
|
|
72
|
+
fs_guard.validate_api_version("..", label="api_version")
|
|
73
|
+
|
|
74
|
+
def test_empty_rejected(self):
|
|
75
|
+
with self.assertRaises(fs_guard.ValidationError):
|
|
76
|
+
fs_guard.validate_api_version("", label="api_version")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class ValidateOrgId15Tests(unittest.TestCase):
|
|
80
|
+
def test_valid_15_char_alnum_ok(self):
|
|
81
|
+
fs_guard.validate_org_id_15("00Dxx0000000000", label="org_id_15")
|
|
82
|
+
|
|
83
|
+
def test_slash_rejected(self):
|
|
84
|
+
with self.assertRaises(fs_guard.ValidationError):
|
|
85
|
+
fs_guard.validate_org_id_15("00D/x0000000000", label="org_id_15")
|
|
86
|
+
|
|
87
|
+
def test_wrong_length_rejected(self):
|
|
88
|
+
with self.assertRaises(fs_guard.ValidationError):
|
|
89
|
+
fs_guard.validate_org_id_15("00Dxx000000000", label="org_id_15")
|
|
90
|
+
with self.assertRaises(fs_guard.ValidationError):
|
|
91
|
+
fs_guard.validate_org_id_15("00Dxx0000000000X", label="org_id_15")
|
|
92
|
+
|
|
93
|
+
def test_underscore_rejected(self):
|
|
94
|
+
# ORG_ID_15_RE forbids underscores — stricter than api_name.
|
|
95
|
+
with self.assertRaises(fs_guard.ValidationError):
|
|
96
|
+
fs_guard.validate_org_id_15("00Dxx00000000_0", label="org_id_15")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class CliApiVersionCheckTests(unittest.TestCase):
|
|
100
|
+
"""The api_version check type is registered in VALID_CHECKS + CHECKS."""
|
|
101
|
+
|
|
102
|
+
def test_registered_in_valid_checks(self):
|
|
103
|
+
self.assertIn("api_version", fs_guard.VALID_CHECKS)
|
|
104
|
+
|
|
105
|
+
def test_registered_in_checks_dispatch(self):
|
|
106
|
+
self.assertIn("api_version", fs_guard.CHECKS)
|
|
107
|
+
|
|
108
|
+
def test_check_function_exists(self):
|
|
109
|
+
self.assertTrue(callable(fs_guard.check_api_version))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
if __name__ == "__main__":
|
|
113
|
+
unittest.main()
|
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
"""Tests for `main._iterate_wave_b` — iterative subflow + apex discovery.
|
|
2
|
+
|
|
3
|
+
The initial `_run_wave_b` only enumerates top-level flow refs from
|
|
4
|
+
`bundle_parsed`. When those flow bodies reference subflows (shared
|
|
5
|
+
utility flows like `handleFlowFault`) or new Apex classes, their bodies
|
|
6
|
+
were never fetched. `_iterate_wave_b` drives Wave B to a fixed point so
|
|
7
|
+
every reachable flow/apex body lands in the tree.
|
|
8
|
+
|
|
9
|
+
Assertions focus on observable behavior:
|
|
10
|
+
* Convergence shape — no extra fetches when there are no new refs.
|
|
11
|
+
* Correctness — a utility flow referenced from the top-level round is
|
|
12
|
+
fetched in the next round and its metadata lands in the merged dict.
|
|
13
|
+
* Safety — iteration cap surfaces remaining unfetched names via
|
|
14
|
+
`unresolved`; fetch failures are tolerated without crashing.
|
|
15
|
+
* Cross-kind discovery — a subflow's actionCall referencing a new
|
|
16
|
+
Apex class adds that class to `apex_rows`.
|
|
17
|
+
* Deduplication — the same subflow referenced twice is fetched once.
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import unittest
|
|
22
|
+
from unittest import mock
|
|
23
|
+
|
|
24
|
+
from . import _bootstrap # noqa: F401
|
|
25
|
+
|
|
26
|
+
import main # type: ignore
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
# Helpers
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _creds_provider():
|
|
35
|
+
return ("https://example.my.salesforce.com", "fake_token")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _refresh_fn():
|
|
39
|
+
return _creds_provider()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _flow_def_row(dev_name: str, version_id: str) -> dict:
|
|
43
|
+
"""Shape matches what `fetch_flow_definition_ids_by_names` returns —
|
|
44
|
+
the fields `_build_flow_children` + the pipeline downstream expect."""
|
|
45
|
+
return {
|
|
46
|
+
"Id": f"300{dev_name}",
|
|
47
|
+
"DeveloperName": dev_name,
|
|
48
|
+
"ActiveVersionId": version_id,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _flow_metadata_record(
|
|
53
|
+
version_id: str,
|
|
54
|
+
full_name: str,
|
|
55
|
+
*,
|
|
56
|
+
subflows: list[tuple[str, str]] | None = None,
|
|
57
|
+
action_calls: list[dict] | None = None,
|
|
58
|
+
) -> dict:
|
|
59
|
+
"""Shape a synthetic Flow.Metadata record the way `fetch_flow_metadata`
|
|
60
|
+
returns it — a dict with Id/FullName/Metadata. Subflow tuples are
|
|
61
|
+
`(element_name, flow_name)`; action_calls are raw dicts."""
|
|
62
|
+
md: dict = {}
|
|
63
|
+
if subflows is not None:
|
|
64
|
+
md["subflows"] = [
|
|
65
|
+
{"name": elem, "flowName": target} for elem, target in subflows
|
|
66
|
+
]
|
|
67
|
+
if action_calls is not None:
|
|
68
|
+
md["actionCalls"] = action_calls
|
|
69
|
+
return {"Id": version_id, "FullName": full_name, "Metadata": md}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _apex_row(name: str) -> dict:
|
|
73
|
+
"""Shape matches `fetch_apex_bodies_by_names` output — `_iterate_wave_b`
|
|
74
|
+
tracks already-fetched by `Name`."""
|
|
75
|
+
return {"Id": f"01p_{name}", "Name": name, "Body": "public class ..."}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# ---------------------------------------------------------------------------
|
|
79
|
+
# Tests
|
|
80
|
+
# ---------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class IterativeWaveBTests(unittest.TestCase):
|
|
84
|
+
def test_single_round_when_no_subflows(self):
|
|
85
|
+
"""Round 1 flow body references no subflows and no new apex →
|
|
86
|
+
`_extract_refs_from_flow_metadata` returns empty sets →
|
|
87
|
+
`_fetch_wave_b_by_names` is never invoked → initial wave_b passes
|
|
88
|
+
through unchanged."""
|
|
89
|
+
initial = {
|
|
90
|
+
"apex_rows": [],
|
|
91
|
+
"flow_def_rows": [_flow_def_row("TopFlow", "301TOP")],
|
|
92
|
+
"flow_metadata": {
|
|
93
|
+
"301TOP": _flow_metadata_record(
|
|
94
|
+
"301TOP", "TopFlow-1", subflows=[], action_calls=[],
|
|
95
|
+
),
|
|
96
|
+
},
|
|
97
|
+
"unresolved": [],
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
with mock.patch.object(main, "_fetch_wave_b_by_names") as mocked:
|
|
101
|
+
out = main._iterate_wave_b(
|
|
102
|
+
initial, _creds_provider, _refresh_fn,
|
|
103
|
+
api_version="v60.0", org_alias="test-org",
|
|
104
|
+
parallelism=2, max_iterations=5,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
mocked.assert_not_called()
|
|
108
|
+
self.assertEqual(
|
|
109
|
+
{r["DeveloperName"] for r in out["flow_def_rows"]},
|
|
110
|
+
{"TopFlow"},
|
|
111
|
+
)
|
|
112
|
+
self.assertEqual(out["unresolved"], [])
|
|
113
|
+
|
|
114
|
+
def test_two_rounds_for_shared_utility_flow(self):
|
|
115
|
+
"""Round 1: a top-level flow body references subflow `handleFlowFault`.
|
|
116
|
+
Round 2: fetches `handleFlowFault`'s metadata. Round 3: no new refs.
|
|
117
|
+
Converges. Final flow_metadata contains BOTH flows.
|
|
118
|
+
|
|
119
|
+
This is the real-org `MyAgent__v5` shape — `handleFlowFault`
|
|
120
|
+
is shared across every flow and should be fetched in one extra
|
|
121
|
+
round.
|
|
122
|
+
"""
|
|
123
|
+
initial = {
|
|
124
|
+
"apex_rows": [],
|
|
125
|
+
"flow_def_rows": [_flow_def_row("AGNT_Top_Flow", "301TOP")],
|
|
126
|
+
"flow_metadata": {
|
|
127
|
+
"301TOP": _flow_metadata_record(
|
|
128
|
+
"301TOP", "AGNT_Top_Flow-1",
|
|
129
|
+
subflows=[("Handle_Flow_Fault", "handleFlowFault")],
|
|
130
|
+
),
|
|
131
|
+
},
|
|
132
|
+
"unresolved": [],
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# Mock `_fetch_wave_b_by_names` for two rounds:
|
|
136
|
+
# Round 1: flow_names=[handleFlowFault] → returns metadata whose
|
|
137
|
+
# actionCalls reference a NEW apex (XCSF_FlowFaultMessage).
|
|
138
|
+
# Round 2: apex_names=[XCSF_FlowFaultMessage] → apex row returned.
|
|
139
|
+
# Round 3: no new refs → converge.
|
|
140
|
+
calls: list[dict] = []
|
|
141
|
+
|
|
142
|
+
def fake_fetch(**kwargs):
|
|
143
|
+
calls.append({
|
|
144
|
+
"flow_names": list(kwargs["flow_names"]),
|
|
145
|
+
"apex_names": list(kwargs["apex_names"]),
|
|
146
|
+
})
|
|
147
|
+
if kwargs["flow_names"] == ["handleFlowFault"]:
|
|
148
|
+
return {
|
|
149
|
+
"apex_rows": [],
|
|
150
|
+
"flow_def_rows": [
|
|
151
|
+
_flow_def_row("handleFlowFault", "301UTIL"),
|
|
152
|
+
],
|
|
153
|
+
"flow_metadata": {
|
|
154
|
+
"301UTIL": _flow_metadata_record(
|
|
155
|
+
"301UTIL", "handleFlowFault-1",
|
|
156
|
+
subflows=[],
|
|
157
|
+
action_calls=[{
|
|
158
|
+
"name": "Parse_and_log_fault",
|
|
159
|
+
"actionType": "apex",
|
|
160
|
+
"actionName": "XCSF_FlowFaultMessage",
|
|
161
|
+
}],
|
|
162
|
+
),
|
|
163
|
+
},
|
|
164
|
+
"unresolved": [],
|
|
165
|
+
}
|
|
166
|
+
if kwargs["apex_names"] == ["XCSF_FlowFaultMessage"]:
|
|
167
|
+
return {
|
|
168
|
+
"apex_rows": [_apex_row("XCSF_FlowFaultMessage")],
|
|
169
|
+
"flow_def_rows": [],
|
|
170
|
+
"flow_metadata": {},
|
|
171
|
+
"unresolved": [],
|
|
172
|
+
}
|
|
173
|
+
raise AssertionError(f"unexpected fetch call: {kwargs}")
|
|
174
|
+
|
|
175
|
+
with mock.patch.object(
|
|
176
|
+
main, "_fetch_wave_b_by_names", side_effect=fake_fetch,
|
|
177
|
+
) as mocked:
|
|
178
|
+
out = main._iterate_wave_b(
|
|
179
|
+
initial, _creds_provider, _refresh_fn,
|
|
180
|
+
api_version="v60.0", org_alias="test-org",
|
|
181
|
+
parallelism=2, max_iterations=5,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Exactly two rounds: round 1 picks up handleFlowFault, round 2
|
|
185
|
+
# picks up the apex discovered in round 1's subflow body, round 3
|
|
186
|
+
# converges without invoking the fetcher.
|
|
187
|
+
self.assertEqual(mocked.call_count, 2)
|
|
188
|
+
dev_names = {r["DeveloperName"] for r in out["flow_def_rows"]}
|
|
189
|
+
self.assertEqual(dev_names, {"AGNT_Top_Flow", "handleFlowFault"})
|
|
190
|
+
self.assertIn("301UTIL", out["flow_metadata"])
|
|
191
|
+
apex_names_out = {r["Name"] for r in out["apex_rows"]}
|
|
192
|
+
self.assertIn("XCSF_FlowFaultMessage", apex_names_out)
|
|
193
|
+
# Call-order sanity: flow round precedes apex round.
|
|
194
|
+
self.assertEqual(calls[0]["flow_names"], ["handleFlowFault"])
|
|
195
|
+
self.assertEqual(calls[1]["apex_names"], ["XCSF_FlowFaultMessage"])
|
|
196
|
+
|
|
197
|
+
def test_actioncall_discovers_new_apex(self):
|
|
198
|
+
"""A subflow body carries an actionCall referencing a new Apex
|
|
199
|
+
class. Round 2 fetches that Apex. `wave_b["apex_rows"]` grows."""
|
|
200
|
+
initial = {
|
|
201
|
+
"apex_rows": [],
|
|
202
|
+
"flow_def_rows": [_flow_def_row("TopFlow", "301TOP")],
|
|
203
|
+
"flow_metadata": {
|
|
204
|
+
"301TOP": _flow_metadata_record(
|
|
205
|
+
"301TOP", "TopFlow-1",
|
|
206
|
+
subflows=[],
|
|
207
|
+
action_calls=[{
|
|
208
|
+
"name": "Invoke_Helper",
|
|
209
|
+
"actionType": "apex",
|
|
210
|
+
"actionName": "NewApexHelper",
|
|
211
|
+
}],
|
|
212
|
+
),
|
|
213
|
+
},
|
|
214
|
+
"unresolved": [],
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
def fake_fetch(**kwargs):
|
|
218
|
+
# Must be the apex-only round — no new flow names.
|
|
219
|
+
self.assertEqual(kwargs["flow_names"], [])
|
|
220
|
+
self.assertEqual(kwargs["apex_names"], ["NewApexHelper"])
|
|
221
|
+
return {
|
|
222
|
+
"apex_rows": [_apex_row("NewApexHelper")],
|
|
223
|
+
"flow_def_rows": [],
|
|
224
|
+
"flow_metadata": {},
|
|
225
|
+
"unresolved": [],
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
with mock.patch.object(
|
|
229
|
+
main, "_fetch_wave_b_by_names", side_effect=fake_fetch,
|
|
230
|
+
) as mocked:
|
|
231
|
+
out = main._iterate_wave_b(
|
|
232
|
+
initial, _creds_provider, _refresh_fn,
|
|
233
|
+
api_version="v60.0", org_alias="test-org",
|
|
234
|
+
parallelism=2, max_iterations=5,
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
self.assertEqual(mocked.call_count, 1)
|
|
238
|
+
apex_names_seen = {r["Name"] for r in out["apex_rows"]}
|
|
239
|
+
self.assertEqual(apex_names_seen, {"NewApexHelper"})
|
|
240
|
+
|
|
241
|
+
def test_dedup_across_rounds(self):
|
|
242
|
+
"""The same subflow referenced from two parent flows is fetched
|
|
243
|
+
exactly once. `_iterate_wave_b` diffs against already-fetched
|
|
244
|
+
names — duplicates are filtered before dispatch."""
|
|
245
|
+
initial = {
|
|
246
|
+
"apex_rows": [],
|
|
247
|
+
"flow_def_rows": [
|
|
248
|
+
_flow_def_row("ParentA", "301A"),
|
|
249
|
+
_flow_def_row("ParentB", "301B"),
|
|
250
|
+
],
|
|
251
|
+
"flow_metadata": {
|
|
252
|
+
"301A": _flow_metadata_record(
|
|
253
|
+
"301A", "ParentA-1",
|
|
254
|
+
subflows=[("Handle_A", "SharedUtility")],
|
|
255
|
+
),
|
|
256
|
+
"301B": _flow_metadata_record(
|
|
257
|
+
"301B", "ParentB-1",
|
|
258
|
+
subflows=[("Handle_B", "SharedUtility")],
|
|
259
|
+
),
|
|
260
|
+
},
|
|
261
|
+
"unresolved": [],
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
call_log: list[list[str]] = []
|
|
265
|
+
|
|
266
|
+
def fake_fetch(**kwargs):
|
|
267
|
+
call_log.append(list(kwargs["flow_names"]))
|
|
268
|
+
return {
|
|
269
|
+
"apex_rows": [],
|
|
270
|
+
"flow_def_rows": [_flow_def_row("SharedUtility", "301UTIL")],
|
|
271
|
+
"flow_metadata": {
|
|
272
|
+
"301UTIL": _flow_metadata_record(
|
|
273
|
+
"301UTIL", "SharedUtility-1", subflows=[],
|
|
274
|
+
),
|
|
275
|
+
},
|
|
276
|
+
"unresolved": [],
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
with mock.patch.object(
|
|
280
|
+
main, "_fetch_wave_b_by_names", side_effect=fake_fetch,
|
|
281
|
+
) as mocked:
|
|
282
|
+
main._iterate_wave_b(
|
|
283
|
+
initial, _creds_provider, _refresh_fn,
|
|
284
|
+
api_version="v60.0", org_alias="test-org",
|
|
285
|
+
parallelism=2, max_iterations=5,
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
# Exactly ONE round of fetching — round 1 queries [SharedUtility]
|
|
289
|
+
# (deduped across the two parents); round 2 finds no new refs.
|
|
290
|
+
self.assertEqual(mocked.call_count, 1)
|
|
291
|
+
self.assertEqual(call_log[0], ["SharedUtility"])
|
|
292
|
+
|
|
293
|
+
def test_iteration_cap_surfaces_pending(self):
|
|
294
|
+
"""Pathological graph where every round introduces a new subflow
|
|
295
|
+
reference (chain: F1 → F2 → F3 → ...). After `max_iterations`
|
|
296
|
+
rounds, remaining unfetched names are surfaced in `unresolved`
|
|
297
|
+
so the pipeline can degrade to PARTIAL_OK with a clear cause.
|
|
298
|
+
|
|
299
|
+
Use `max_iterations=3` to keep the test fast; the shape is
|
|
300
|
+
identical to the production cap of 5.
|
|
301
|
+
"""
|
|
302
|
+
initial = {
|
|
303
|
+
"apex_rows": [],
|
|
304
|
+
"flow_def_rows": [_flow_def_row("F0", "301_V0")],
|
|
305
|
+
"flow_metadata": {
|
|
306
|
+
"301_V0": _flow_metadata_record(
|
|
307
|
+
"301_V0", "F0-1", subflows=[("Chain", "F1")],
|
|
308
|
+
),
|
|
309
|
+
},
|
|
310
|
+
"unresolved": [],
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
round_counter = {"i": 1}
|
|
314
|
+
|
|
315
|
+
def fake_fetch(**kwargs):
|
|
316
|
+
"""Each round returns a flow whose body references the next
|
|
317
|
+
flow in the chain — unbounded growth."""
|
|
318
|
+
requested = kwargs["flow_names"][0]
|
|
319
|
+
next_flow = f"F{round_counter['i'] + 1}"
|
|
320
|
+
round_counter["i"] += 1
|
|
321
|
+
return {
|
|
322
|
+
"apex_rows": [],
|
|
323
|
+
"flow_def_rows": [_flow_def_row(requested, f"301_V{requested}")],
|
|
324
|
+
"flow_metadata": {
|
|
325
|
+
f"301_V{requested}": _flow_metadata_record(
|
|
326
|
+
f"301_V{requested}", f"{requested}-1",
|
|
327
|
+
subflows=[("Chain", next_flow)],
|
|
328
|
+
),
|
|
329
|
+
},
|
|
330
|
+
"unresolved": [],
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
with mock.patch.object(
|
|
334
|
+
main, "_fetch_wave_b_by_names", side_effect=fake_fetch,
|
|
335
|
+
) as mocked:
|
|
336
|
+
out = main._iterate_wave_b(
|
|
337
|
+
initial, _creds_provider, _refresh_fn,
|
|
338
|
+
api_version="v60.0", org_alias="test-org",
|
|
339
|
+
parallelism=2, max_iterations=3,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
# We exhausted exactly max_iterations rounds.
|
|
343
|
+
self.assertEqual(mocked.call_count, 3)
|
|
344
|
+
# The unfetched chain-tail lands in `unresolved` with the cap
|
|
345
|
+
# reason so the pipeline can reflect it in the RESULT block.
|
|
346
|
+
iter_cap_entries = [
|
|
347
|
+
u for u in out["unresolved"]
|
|
348
|
+
if "wave-b-iteration-cap" in (u.get("reason") or "")
|
|
349
|
+
]
|
|
350
|
+
self.assertTrue(iter_cap_entries)
|
|
351
|
+
# The next-round flow reference (F4, unfetched) must be among them.
|
|
352
|
+
pending_names = {u.get("api_name") for u in iter_cap_entries}
|
|
353
|
+
self.assertIn("F4", pending_names)
|
|
354
|
+
|
|
355
|
+
def test_fetch_failure_tolerated(self):
|
|
356
|
+
"""If the round-N fetch raises (managed-package flow not readable,
|
|
357
|
+
permission denied, etc.), the iteration doesn't crash. The
|
|
358
|
+
affected name stays absent from `flow_def_rows` and surfaces via
|
|
359
|
+
the pending-fetches path downstream; other rounds continue.
|
|
360
|
+
|
|
361
|
+
Concretely: we simulate the failure by having the mocked
|
|
362
|
+
`_fetch_wave_b_by_names` append an `unresolved` entry instead of
|
|
363
|
+
returning metadata for the troubled name, which is exactly the
|
|
364
|
+
contract `_fetch_wave_b_by_names` follows internally (it
|
|
365
|
+
catches RestClientError / SoqlParamError and logs to
|
|
366
|
+
`unresolved` without raising).
|
|
367
|
+
"""
|
|
368
|
+
initial = {
|
|
369
|
+
"apex_rows": [],
|
|
370
|
+
"flow_def_rows": [_flow_def_row("TopFlow", "301TOP")],
|
|
371
|
+
"flow_metadata": {
|
|
372
|
+
"301TOP": _flow_metadata_record(
|
|
373
|
+
"301TOP", "TopFlow-1",
|
|
374
|
+
subflows=[
|
|
375
|
+
("Good_Sub", "GoodSubflow"),
|
|
376
|
+
("Bad_Sub", "UnreachableFlow"),
|
|
377
|
+
],
|
|
378
|
+
),
|
|
379
|
+
},
|
|
380
|
+
"unresolved": [],
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
def fake_fetch(**kwargs):
|
|
384
|
+
# Only the reachable one resolves. The unreachable one is
|
|
385
|
+
# reported via the `unresolved` channel by the inner fetcher.
|
|
386
|
+
return {
|
|
387
|
+
"apex_rows": [],
|
|
388
|
+
"flow_def_rows": [_flow_def_row("GoodSubflow", "301GOOD")],
|
|
389
|
+
"flow_metadata": {
|
|
390
|
+
"301GOOD": _flow_metadata_record(
|
|
391
|
+
"301GOOD", "GoodSubflow-1", subflows=[],
|
|
392
|
+
),
|
|
393
|
+
},
|
|
394
|
+
"unresolved": [{
|
|
395
|
+
"kind": "FLOW",
|
|
396
|
+
"reason": "flow-def-by-name-failed:managed-package-403",
|
|
397
|
+
}],
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
with mock.patch.object(
|
|
401
|
+
main, "_fetch_wave_b_by_names", side_effect=fake_fetch,
|
|
402
|
+
):
|
|
403
|
+
out = main._iterate_wave_b(
|
|
404
|
+
initial, _creds_provider, _refresh_fn,
|
|
405
|
+
api_version="v60.0", org_alias="test-org",
|
|
406
|
+
parallelism=2, max_iterations=5,
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
dev_names = {r["DeveloperName"] for r in out["flow_def_rows"]}
|
|
410
|
+
# GoodSubflow made it in; UnreachableFlow did not.
|
|
411
|
+
self.assertIn("GoodSubflow", dev_names)
|
|
412
|
+
self.assertNotIn("UnreachableFlow", dev_names)
|
|
413
|
+
# The inner-fetch's unresolved entry propagates.
|
|
414
|
+
reasons = [u.get("reason") or "" for u in out["unresolved"]]
|
|
415
|
+
self.assertTrue(
|
|
416
|
+
any("managed-package-403" in r for r in reasons),
|
|
417
|
+
f"expected propagated failure reason; got {reasons}",
|
|
418
|
+
)
|
|
419
|
+
# UnreachableFlow is still referenced but not fetched — after
|
|
420
|
+
# iteration converges on subsequent rounds (no new discoveries
|
|
421
|
+
# because GoodSubflow is leaf), it lands as iteration-cap
|
|
422
|
+
# pending. In this test max_iterations=5 and the chain stops at
|
|
423
|
+
# round 1; UnreachableFlow is still missing from fetched names,
|
|
424
|
+
# so the convergence scan at the end flags it? Actually no —
|
|
425
|
+
# convergence only surfaces via the cap. Let's verify that the
|
|
426
|
+
# pipeline's parse_wave path would still mark it as pending via
|
|
427
|
+
# visited-vs-referenced — but that's out of scope for this unit
|
|
428
|
+
# test. The contract here is: iteration doesn't crash, good
|
|
429
|
+
# flows are merged, bad-flow signal propagates.
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
class ExtractRefsFromFlowMetadataTests(unittest.TestCase):
|
|
433
|
+
"""Direct coverage for `_extract_refs_from_flow_metadata` — the
|
|
434
|
+
load-bearing extractor that drives iteration. Its permissiveness
|
|
435
|
+
(tolerating missing/None/malformed records) is a correctness
|
|
436
|
+
property: if it raised on edge cases, iteration would crash on any
|
|
437
|
+
flow with a weird metadata shape."""
|
|
438
|
+
|
|
439
|
+
def test_extracts_subflow_and_apex_names(self):
|
|
440
|
+
flow_metadata = {
|
|
441
|
+
"301A": _flow_metadata_record(
|
|
442
|
+
"301A", "A-1",
|
|
443
|
+
subflows=[("E1", "SubA"), ("E2", "SubB")],
|
|
444
|
+
action_calls=[
|
|
445
|
+
{"name": "ApexCall", "actionType": "apex",
|
|
446
|
+
"actionName": "MyApex"},
|
|
447
|
+
# non-apex actionCall shouldn't appear in apex set
|
|
448
|
+
{"name": "StdCall", "actionType": "emailSimple",
|
|
449
|
+
"actionName": "SendEmail"},
|
|
450
|
+
],
|
|
451
|
+
),
|
|
452
|
+
}
|
|
453
|
+
subs, apex = main._extract_refs_from_flow_metadata(flow_metadata)
|
|
454
|
+
self.assertEqual(subs, {"SubA", "SubB"})
|
|
455
|
+
self.assertEqual(apex, {"MyApex"})
|
|
456
|
+
|
|
457
|
+
def test_tolerates_missing_fields(self):
|
|
458
|
+
"""None records, non-dict metadata, missing keys, blank names —
|
|
459
|
+
none of these should raise; they all drop silently."""
|
|
460
|
+
flow_metadata = {
|
|
461
|
+
"301A": None,
|
|
462
|
+
"301B": {"Metadata": None},
|
|
463
|
+
"301C": {"Metadata": "not-a-dict"},
|
|
464
|
+
"301D": {"Metadata": {}},
|
|
465
|
+
"301E": {"Metadata": {
|
|
466
|
+
"subflows": [{"name": "noName"}, "not-a-dict",
|
|
467
|
+
{"name": "E", "flowName": ""}],
|
|
468
|
+
"actionCalls": [{"actionType": "apex", "actionName": ""},
|
|
469
|
+
"not-a-dict"],
|
|
470
|
+
}},
|
|
471
|
+
}
|
|
472
|
+
subs, apex = main._extract_refs_from_flow_metadata(flow_metadata)
|
|
473
|
+
self.assertEqual(subs, set())
|
|
474
|
+
self.assertEqual(apex, set())
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
if __name__ == "__main__":
|
|
478
|
+
unittest.main()
|