hap-cli 0.9.1__tar.gz → 0.9.2__tar.gz
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.
- {hap_cli-0.9.1 → hap_cli-0.9.2}/PKG-INFO +1 -1
- {hap_cli-0.9.1 → hap_cli-0.9.2}/app_live_tests/smoke.py +208 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/__init__.py +1 -1
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/worksheet_cmd.py +1 -1
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/editor/ops.py +119 -3
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/editor/reader.py +137 -1
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/record.py +6 -2
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/record_form_checks.py +30 -4
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/worksheet.py +157 -37
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_core.py +171 -0
- hap_cli-0.9.2/hap_cli/tests/test_editor_inline_subtable.py +239 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_g6_advanced_fields.py +5 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_record_form_checks.py +126 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli.egg-info/PKG-INFO +1 -1
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli.egg-info/SOURCES.txt +1 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/MANIFEST.in +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/README.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/app_live_tests/__init__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/app_live_tests/__main__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/app_live_tests/config.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/app_live_tests/fixtures/__init__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/app_live_tests/fixtures/feedback_scenario.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/app_live_tests/harness.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/app_live_tests/state.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/README.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/README_CN.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/__main__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/_entry.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/__init__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/app_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/app_creator_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/app_editor_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/attachment_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/auth_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/calendar_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/chart_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/chat_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/chatbot_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/config_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/contact_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/department_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/group_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/guide_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/icon_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/instance_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/node_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/optionset_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/page_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/post_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/record_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/region_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/role_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/update_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/upload_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/v3_registry.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/commands/workflow_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/context.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/__init__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/action_spec_adapter.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/__init__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/_hapmeta/__init__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/_hapmeta/control_type_codes.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/_hapmeta/worksheet_templates.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/charts.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/cleanup.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/compiler.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/config.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/design/design.schema.json +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/errors.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/examples/minimal.design.json +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/executor.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/fields.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/hap.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/merge.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/recording/__init__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/recording/console.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/recording/jsonl.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/recording/mirror.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/recording/report.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/schema.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/seed/INSTRUCTIONS.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/seed/__init__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/seed/cli.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/seed/executor.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/seed/resources/attachments.json +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/seed/resources/sample.docx +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/seed/resources/sample.pdf +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/seed/resources/sample_images.json +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/seed/template.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/selftest.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/smoke.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/steps.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/store.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/validate.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/app_creator/workflow_dsl.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/attachment.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/auth.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/calendar_mod.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/chart.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/chat.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/chatbot.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/cli_access.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/condition_dsl.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/contact.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/control_type_codes.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/department.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/editor/__init__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/editor/_hapmeta/__init__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/editor/_hapmeta/control_type_codes.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/editor/apply.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/editor/componentlower.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/editor/editspec/component.schema.json +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/editor/editspec/custom-action.schema.json +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/editor/editspec/envelope.schema.json +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/editor/editspec/field.schema.json +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/editor/editspec.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/editor/errors.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/editor/fieldlower.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/editor/jsonschema_mini.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/editor/models.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/editor/planner.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/field_normalizer.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/field_spec_adapter.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/filter_translator.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/filter_v3.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/flow_node.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/global_meta.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/group.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/guide.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/icon_index.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/instance.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/layout_check.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/logger.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/optionset.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/page.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/post.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/record_defaults.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/record_lookups.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/record_rules.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/relation_scope.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/response_crypto.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/role.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/role_perm_builder.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/rule_spec_adapter.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/session.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/token_crypto.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/update.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/upload.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/v3/__init__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/v3/dispatcher.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/v3/render.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/v3/schema.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/view_spec_adapter.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/workflow.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/workflow_node_dsl.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/worksheet_form.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/worksheet_reference.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/core/worksheet_templates.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/en/approval.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/en/chart.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/en/messages.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/en/overview.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/en/post.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/en/record.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/en/setup.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/en/skills.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/en/workflow.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/en/worksheet.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/index.json +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/zh-Hans/approval.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/zh-Hans/chart.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/zh-Hans/messages.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/zh-Hans/overview.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/zh-Hans/post.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/zh-Hans/record.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/zh-Hans/setup.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/zh-Hans/skills.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/zh-Hans/workflow.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/guide/zh-Hans/worksheet.md +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/i18n.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/locale/__init__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/locale/messages.json +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/locale/messages.schema.json +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/__init__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/conftest.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_action_mdpublic_parity.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_advanced_setting_guard.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_app_creator.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_app_editor.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_app_export_usage.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_app_light_color.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_app_section_name.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_approval_submit_fill_cli.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_approval_wire.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_atomic_write.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_attachment_share.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_auth_prerelease.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_batch_add_atomic.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_bug_036430.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_bug_036750.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_calendar_date_range.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_calendar_discussion_cli.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_chart.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_chart_filter_merge.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_chat_attachment_send.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_chat_messages_cli.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_chat_recall_cli.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_chat_send_socket.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_cli_access.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_coded_responses.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_condition_dsl.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_condition_dsl_entries.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_condition_dsl_readback.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_config_cmd.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_content_language.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_control_lookup_guard.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_destructive_confirm_cli.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_dsl_existing_node_refs.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_dsl_multi_record_guard.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_engine_core_parity.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_arg_consistency.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_bidirectional.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_discussion_mentions.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_entry_module.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_field_record_ref.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_http_error_body.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_json_anywhere.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_json_file_arg.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_layout_check.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_node_get_detail.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_node_summary.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_post_cursor.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_publish_reasons.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_replicate_workflow.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_save_fields_shape.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_subgroups.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_subtable_read.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_worksheet_references.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_worksheet_visibility.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fb_worksheet_workflows.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_fields_mdpublic_parity.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_full_e2e.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_g1_chart_parity.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_g1_record_parity.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_g1_upload_parity.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_g1_worksheet_parity.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_g2_coverage_parity.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_g3_workflow_parity.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_g4_approval_parity.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_g5_low_frequency_parity.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_g6_fields_roundtrip.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_g7_misc_gaps.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_g7b_permanent_delete.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_g8_filter_operator.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_g8b_pivot_operators.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_g8c_empty_filter_and_help_truth.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_g9_appid_resolve.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_global_meta.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_guide.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_guide_sections.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_i18n.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_inner_flow_batch_add.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_instance_envelope.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_instance_history_cli.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_integration.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_integration_approval.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_integration_approval_actions.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_integration_calendar.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_integration_destructive.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_integration_misc.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_integration_post.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_integration_social.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_integration_v3.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_integration_workflow.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_integration_worksheet_extra.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_jsonschema_mini_copy.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_member_relation_array_wrap.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_multi_profile.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_no_workflow_unsupported.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_noninteractive_confirm.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_org_approval_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_org_id_cli.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_org_id_docs.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_page_filters_group_read.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_page_filters_group_update.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_page_save_page_id.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_parameter_conventions.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_parameter_mapping_registry.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_post_scope_cli.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_record_batch_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_record_business_rules.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_record_defaults.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_record_doc_id_hidden.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_record_field_alias.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_record_run_action.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_record_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_record_write_fixes.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_relation_scope.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_rmw_mdpublic_parity.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_role_app_scope.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_rule_filters_and_field_warning.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_save_action_silent_drop.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_save_rule_spec.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_select_add_option.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_select_rejects_relation_value.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_semantic_types_registry.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_subtable_mdpublic_parity.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_update.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_update_mode.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_upload_allow_down.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_upload_host_resolution.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_upload_mdpublic_parity.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_v3_api_schema_loader.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_v3_bool_flags.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_v3_dispatcher.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_v3_registry_coverage.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_v3_response_format_json.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_v3_session.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_v3_yaml_translation.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_view_mdpublic_parity.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_wf_config_keys.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_wf_inner_scope.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_wf_json_file_arg.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_wf_node_delete_missing.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_wf_save_action_dsl_fields.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_wf_structure_listing.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_wf_trigger_outcome.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_wf_trigger_preserve.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_wo4_result_handling.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_workflow_code_node.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_workflow_condition_field_types.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_workflow_condition_slots.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_workflow_delete_cascade.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_workflow_envelope_errors.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_workflow_lifecycle_wire.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_workflow_list_cli.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_workflow_node_wire.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_workflow_publish_inner.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_workflow_read_write_keys.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_workflow_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_worksheet_create_section.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_worksheet_crud_cli.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_worksheet_filter_read.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_worksheet_switch.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/tests/test_worksheet_v3_semantic_cli.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/utils/__init__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/utils/atomic_write.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/utils/confirm.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/utils/formatting.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/utils/options.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli/utils/parameter_mapping.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli.egg-info/dependency_links.txt +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli.egg-info/entry_points.txt +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli.egg-info/requires.txt +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/hap_cli.egg-info/top_level.txt +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/__init__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/__main__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/_wftest.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/charts.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/cleanup.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/compiler.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/config.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/errors.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/executor.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/fields.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/hap.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/recording/__init__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/recording/console.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/recording/jsonl.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/recording/mirror.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/recording/report.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/schema.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/seed/__init__.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/seed/cli.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/seed/executor.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/seed/template.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/selftest.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/smoke.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/step.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/steps.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/store.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/live_tests/workflow_dsl.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/setup.cfg +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/setup.py +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/INDEX.json +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/add_member_to_role.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/batch_delete_records.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/create_optionset.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/create_role.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/delete_optionset.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/delete_record.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/delete_role.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/delete_worksheet.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/get_app_info.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/get_app_knowledge_list.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/get_app_worksheets_list.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/get_optionset_list.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/get_record_details.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/get_record_discussions.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/get_record_list.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/get_record_logs.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/get_record_pivot_data.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/get_record_relations.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/get_record_share_link.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/get_regions.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/get_role_details.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/get_role_list.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/knowledge_search.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/leave_all_roles.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/remove_member_from_role.yaml +0 -0
- {hap_cli-0.9.1 → hap_cli-0.9.2}/sources/v3-api-schema/update_optionset.yaml +0 -0
|
@@ -226,6 +226,146 @@ def case_custom_action(app_id: str, section_id: str) -> None:
|
|
|
226
226
|
hap(["worksheet", "delete", ws_id, "-a", app_id, "-y"], check=False)
|
|
227
227
|
|
|
228
228
|
|
|
229
|
+
def case_custom_action_update(app_id: str, section_id: str) -> None:
|
|
230
|
+
"""bug #455751: --action-spec with --btn-id edits the action in place."""
|
|
231
|
+
print("scenario: custom-action update by id")
|
|
232
|
+
ws_id = _make_worksheet(app_id, section_id, "LT-action-update")
|
|
233
|
+
try:
|
|
234
|
+
ok, res = _try(["worksheet", "create-custom-action", ws_id, "-a",
|
|
235
|
+
app_id, "--action-spec", json.dumps(
|
|
236
|
+
{"name": "ORIGINAL", "type": "triggerWorkflow",
|
|
237
|
+
"color": "#2196F3"})])
|
|
238
|
+
check("update: workflow action created", ok, str(res)[:160])
|
|
239
|
+
btn_id = res.get("actionId") if ok else ""
|
|
240
|
+
proc_id = res.get("processId") if ok else ""
|
|
241
|
+
ok, res = _try(["worksheet", "create-custom-action", ws_id, "-a",
|
|
242
|
+
app_id, "--btn-id", btn_id, "--action-spec",
|
|
243
|
+
json.dumps({"name": "UPDATED"})])
|
|
244
|
+
check("update: edit succeeds", ok, str(res)[:160])
|
|
245
|
+
check("update: the id is unchanged",
|
|
246
|
+
ok and res.get("actionId") == btn_id, str(res)[:160])
|
|
247
|
+
check("update: the same workflow is reported",
|
|
248
|
+
ok and res.get("processId") == proc_id, str(res)[:160])
|
|
249
|
+
acts = _actions(ws_id)
|
|
250
|
+
check("update: still one action", len(acts) == 1,
|
|
251
|
+
str([(a.get("btnId"), a.get("name")) for a in acts]))
|
|
252
|
+
got = acts[0] if acts else {}
|
|
253
|
+
check("update: renamed in place",
|
|
254
|
+
got.get("btnId") == btn_id and got.get("name") == "UPDATED",
|
|
255
|
+
str((got.get("btnId"), got.get("name"))))
|
|
256
|
+
check("update: unnamed settings kept",
|
|
257
|
+
got.get("color") == "#2196F3" and got.get("clickType") == 1,
|
|
258
|
+
str((got.get("color"), got.get("clickType"))))
|
|
259
|
+
wf = [r for r in _workflow_rows(app_id)
|
|
260
|
+
if r.get("id") == proc_id or r.get("processId") == proc_id]
|
|
261
|
+
check("update: the action's workflow survives", bool(wf),
|
|
262
|
+
proc_id)
|
|
263
|
+
|
|
264
|
+
# A fill action edited by id stays a fill action and keeps its fields.
|
|
265
|
+
title = next((c["controlId"] for c in _fields(ws_id)
|
|
266
|
+
if c.get("attribute") == 1), "")
|
|
267
|
+
ok, res = _try(["worksheet", "create-custom-action", ws_id, "-a",
|
|
268
|
+
app_id, "--action-spec", json.dumps(
|
|
269
|
+
{"name": "FILL", "type": "updateCurrentRecord",
|
|
270
|
+
"updateFields": [title]})])
|
|
271
|
+
fill_id = res.get("actionId") if ok else ""
|
|
272
|
+
ok, res = _try(["worksheet", "create-custom-action", ws_id, "-a",
|
|
273
|
+
app_id, "--btn-id", fill_id, "--action-spec",
|
|
274
|
+
json.dumps({"name": "FILL2"})])
|
|
275
|
+
check("update: fill action edit succeeds", ok, str(res)[:160])
|
|
276
|
+
acts = _actions(ws_id)
|
|
277
|
+
fill = next((a for a in acts if a.get("btnId") == fill_id), {})
|
|
278
|
+
check("update: two actions after editing the second", len(acts) == 2,
|
|
279
|
+
str([(a.get("btnId"), a.get("name")) for a in acts]))
|
|
280
|
+
check("update: fill action keeps its kind and field",
|
|
281
|
+
fill.get("name") == "FILL2" and fill.get("clickType") == 3
|
|
282
|
+
and [w.get("controlId") for w in fill.get("writeControls")
|
|
283
|
+
or []] == [title],
|
|
284
|
+
str((fill.get("name"), fill.get("clickType"),
|
|
285
|
+
fill.get("writeControls"))))
|
|
286
|
+
|
|
287
|
+
ok, res = _try(["worksheet", "create-custom-action", ws_id, "-a",
|
|
288
|
+
app_id, "--btn-id", "000000000000000000000000",
|
|
289
|
+
"--action-spec", json.dumps({"name": "GHOST"})])
|
|
290
|
+
check("update: unknown id is refused", not ok, str(res)[:160])
|
|
291
|
+
check("update: nothing created for an unknown id",
|
|
292
|
+
len(_actions(ws_id)) == 2, str(len(_actions(ws_id))))
|
|
293
|
+
finally:
|
|
294
|
+
hap(["worksheet", "delete", ws_id, "-a", app_id, "-y"], check=False)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def case_hidden_defaults(app_id: str, section_id: str) -> None:
|
|
298
|
+
"""bug #455757: a hidden field's default goes in with a new record.
|
|
299
|
+
|
|
300
|
+
The form submits every non-empty cell on create, hidden ones included,
|
|
301
|
+
so the defaults the CLI fills in must not be judged as the caller
|
|
302
|
+
writing a hidden field. Writing one yourself is still refused.
|
|
303
|
+
"""
|
|
304
|
+
print("scenario: hidden defaults on create")
|
|
305
|
+
ws_id = _make_worksheet(app_id, section_id, "LT-hidden-defaults")
|
|
306
|
+
|
|
307
|
+
def static(value):
|
|
308
|
+
return {"defsource": json.dumps(
|
|
309
|
+
[{"cid": "", "rcid": "", "staticValue": value}])}
|
|
310
|
+
try:
|
|
311
|
+
ok, res = _try(["worksheet", "add-fields", ws_id, "--controls",
|
|
312
|
+
json.dumps([
|
|
313
|
+
{"type": 2, "controlName": "Stage",
|
|
314
|
+
"fieldPermission": "110",
|
|
315
|
+
"advancedSetting": static("Draft")},
|
|
316
|
+
{"type": 2, "controlName": "Status",
|
|
317
|
+
"fieldPermission": "110",
|
|
318
|
+
"advancedSetting": static("Open")}])])
|
|
319
|
+
check("hidden: fields added", ok, str(res)[:160])
|
|
320
|
+
ids = {c.get("controlName"): c.get("controlId")
|
|
321
|
+
for c in _fields(ws_id)}
|
|
322
|
+
name_id = next((c["controlId"] for c in _fields(ws_id)
|
|
323
|
+
if c.get("attribute") == 1), "")
|
|
324
|
+
stage, status = ids.get("Stage"), ids.get("Status")
|
|
325
|
+
|
|
326
|
+
ok, res = _try(["worksheet", "record", "create", ws_id, "-a", app_id,
|
|
327
|
+
"--fields-json", json.dumps(
|
|
328
|
+
[{"id": name_id, "value": "visible-only"}])])
|
|
329
|
+
check("hidden: create with only the visible field succeeds", ok,
|
|
330
|
+
str(res)[:200])
|
|
331
|
+
row_id = ((res.get("data") or {}).get("rowid")
|
|
332
|
+
if ok and isinstance(res, dict) else "")
|
|
333
|
+
got = (hap(["worksheet", "record", "get", ws_id, row_id, "-a",
|
|
334
|
+
app_id]) or {}) if row_id else {}
|
|
335
|
+
got = got.get("data", got) if isinstance(got, dict) else {}
|
|
336
|
+
check("hidden: the hidden defaults were stored",
|
|
337
|
+
got.get(stage) == "Draft" and got.get(status) == "Open",
|
|
338
|
+
str({k: got.get(k) for k in (name_id, stage, status)}))
|
|
339
|
+
|
|
340
|
+
ok, res = _try(["worksheet", "record", "create", ws_id, "-a", app_id,
|
|
341
|
+
"--fields-json", json.dumps(
|
|
342
|
+
[{"id": name_id, "value": "sneaky"},
|
|
343
|
+
{"id": stage, "value": "Won"}])])
|
|
344
|
+
check("hidden: writing a hidden field yourself is refused",
|
|
345
|
+
not ok and "Stage" in str(res) and "Status" not in str(res),
|
|
346
|
+
str(res)[:240])
|
|
347
|
+
|
|
348
|
+
rows = json.dumps([{name_id: "batch-1"}, {name_id: "batch-2"}])
|
|
349
|
+
ok, res = _try(["worksheet", "record", "batch-create", ws_id,
|
|
350
|
+
"-a", app_id, "--rows", rows])
|
|
351
|
+
check("hidden: batch create with only visible fields succeeds", ok,
|
|
352
|
+
str(res)[:200])
|
|
353
|
+
rows = json.dumps([{name_id: "batch-3"},
|
|
354
|
+
{name_id: "batch-4", stage: "Won"}])
|
|
355
|
+
ok, res = _try(["worksheet", "record", "batch-create", ws_id,
|
|
356
|
+
"-a", app_id, "--rows", rows])
|
|
357
|
+
check("hidden: batch writing a hidden field is refused on its row",
|
|
358
|
+
not ok and "row 2" in str(res) and "row 1" not in str(res),
|
|
359
|
+
str(res)[:240])
|
|
360
|
+
listed = (hap(["worksheet", "record", "list", ws_id, "-a", app_id])
|
|
361
|
+
or {}).get("rows") or []
|
|
362
|
+
blob = json.dumps(listed, ensure_ascii=False)
|
|
363
|
+
check("hidden: the refused batch wrote nothing",
|
|
364
|
+
"batch-3" not in blob and "batch-1" in blob, blob[:200])
|
|
365
|
+
finally:
|
|
366
|
+
hap(["worksheet", "delete", ws_id, "-a", app_id, "-y"], check=False)
|
|
367
|
+
|
|
368
|
+
|
|
229
369
|
def _items(app_id: str) -> dict[str, str]:
|
|
230
370
|
"""All sidebar items (worksheets, pages, chatbots) name -> id."""
|
|
231
371
|
detail = hap(["app", "info", "-a", app_id])
|
|
@@ -3397,6 +3537,71 @@ def case_fb_subtable_read(app_id: str, section_id: str) -> None:
|
|
|
3397
3537
|
hap(["worksheet", "delete", parent, "--yes"], check=False)
|
|
3398
3538
|
|
|
3399
3539
|
|
|
3540
|
+
def case_editor_inline_subtable(app_id: str, section_id: str) -> None:
|
|
3541
|
+
"""Bug #455768: edit-spec field ops on an inline sub-table's own sheet."""
|
|
3542
|
+
print("\nscenario: editor field ops on an inline sub-table (#455768)")
|
|
3543
|
+
import json as _json
|
|
3544
|
+
name = "BUG455768-sub-parent"
|
|
3545
|
+
parent = _make_worksheet(app_id, section_id, name)
|
|
3546
|
+
check("inline-subtable: a throwaway parent was created", bool(parent))
|
|
3547
|
+
if not parent:
|
|
3548
|
+
return
|
|
3549
|
+
try:
|
|
3550
|
+
hap(["worksheet", "update-fields", parent, "--title-name", "名称",
|
|
3551
|
+
"--no-verify", "--fields", _json.dumps([
|
|
3552
|
+
{"type": "TEXT", "name": "名称"},
|
|
3553
|
+
{"type": "SUB_LIST", "name": "内嵌明细", "child_fields": [
|
|
3554
|
+
{"type": "TEXT", "name": "行名称"},
|
|
3555
|
+
{"type": "NUMBER", "name": "行数量"}]},
|
|
3556
|
+
], ensure_ascii=False)], check=False)
|
|
3557
|
+
child = next((c.get("dataSource") for c in _fields(parent)
|
|
3558
|
+
if c.get("type") == 34), "")
|
|
3559
|
+
check("inline-subtable: the parent names its child sheet",
|
|
3560
|
+
bool(child), str(child))
|
|
3561
|
+
if not child:
|
|
3562
|
+
return
|
|
3563
|
+
|
|
3564
|
+
def _child_cols() -> list[dict]:
|
|
3565
|
+
got = hap(["worksheet", "fields", child, "--raw",
|
|
3566
|
+
"--parent", parent], check=False)
|
|
3567
|
+
return [c for c in got if len(c.get("controlId") or "") == 24] \
|
|
3568
|
+
if isinstance(got, list) else []
|
|
3569
|
+
|
|
3570
|
+
rc, out, err = editor_apply({"app": app_id, "ops": [
|
|
3571
|
+
{"type": "field.update", "worksheet": child, "field": "行名称",
|
|
3572
|
+
"set": {"hint": "按id改"}}]})
|
|
3573
|
+
check("inline-subtable: field.update by sheet id applies", rc == 0,
|
|
3574
|
+
(err or out)[-300:])
|
|
3575
|
+
cols = {c["controlName"]: c for c in _child_cols()}
|
|
3576
|
+
check("inline-subtable: the hint reads back",
|
|
3577
|
+
(cols.get("行名称") or {}).get("hint") == "按id改",
|
|
3578
|
+
str(cols.get("行名称"))[:200])
|
|
3579
|
+
|
|
3580
|
+
rc, out, err = editor_apply({"app": app_id, "ops": [
|
|
3581
|
+
{"type": "field.update", "worksheet": f"{name}/内嵌明细",
|
|
3582
|
+
"field": "行数量", "rename": "数量", "set": {}},
|
|
3583
|
+
{"type": "field.add", "worksheet": child,
|
|
3584
|
+
"field": {"name": "行备注", "type": "Text"}},
|
|
3585
|
+
{"type": "field.reorder", "worksheet": child,
|
|
3586
|
+
"order": ["行备注", "数量"]},
|
|
3587
|
+
{"type": "field.delete", "worksheet": child, "field": "行名称",
|
|
3588
|
+
"confirm": True}]})
|
|
3589
|
+
check("inline-subtable: add / reorder / delete apply", rc == 0,
|
|
3590
|
+
(err or out)[-300:])
|
|
3591
|
+
names = [c["controlName"] for c in
|
|
3592
|
+
sorted(_child_cols(), key=lambda c: c.get("row", 0))]
|
|
3593
|
+
check("inline-subtable: columns read back renamed, added, "
|
|
3594
|
+
"reordered and deleted", names == ["行备注", "数量"],
|
|
3595
|
+
str(names))
|
|
3596
|
+
sub = next((c for c in _fields(parent) if c.get("type") == 34), {})
|
|
3597
|
+
shown = {c["controlId"]: c["controlName"] for c in _child_cols()}
|
|
3598
|
+
check("inline-subtable: the new column is shown in the sub-table",
|
|
3599
|
+
[shown.get(i) for i in sub.get("showControls") or []]
|
|
3600
|
+
== ["行备注", "数量"], str(sub.get("showControls")))
|
|
3601
|
+
finally:
|
|
3602
|
+
hap(["worksheet", "delete", parent, "-a", app_id, "-y"], check=False)
|
|
3603
|
+
|
|
3604
|
+
|
|
3400
3605
|
def case_fb_subgroups(app_id: str, section_id: str) -> None:
|
|
3401
3606
|
"""WO-FB 3.1: the editor can see worksheets in second-level groups."""
|
|
3402
3607
|
print("\nscenario: sub-group visibility (WO-FB 3.1)")
|
|
@@ -4546,6 +4751,8 @@ _CASES = {
|
|
|
4546
4751
|
"view": case_view,
|
|
4547
4752
|
"role": case_role,
|
|
4548
4753
|
"custom-action": case_custom_action,
|
|
4754
|
+
"custom-action-update": case_custom_action_update,
|
|
4755
|
+
"hidden-defaults": case_hidden_defaults,
|
|
4549
4756
|
"chatbot": case_chatbot,
|
|
4550
4757
|
"custom-page": case_custom_page,
|
|
4551
4758
|
"component": case_component,
|
|
@@ -4585,6 +4792,7 @@ _CASES = {
|
|
|
4585
4792
|
"fb-layout-check": case_fb_layout_check,
|
|
4586
4793
|
"fb-pair-relation": case_fb_pair_relation,
|
|
4587
4794
|
"fb-subtable-read": case_fb_subtable_read,
|
|
4795
|
+
"editor-inline-subtable": case_editor_inline_subtable,
|
|
4588
4796
|
"fb-subgroups": case_fb_subgroups,
|
|
4589
4797
|
"fb-button-target": case_fb_button_target,
|
|
4590
4798
|
"g6-advanced-fields": case_g6_advanced_fields,
|
|
@@ -1299,7 +1299,7 @@ def worksheet_save_button(ctx, worksheet_id, btn_config, action_spec,
|
|
|
1299
1299
|
eff_app = app_id or getattr(session, "default_app_id", "") or ""
|
|
1300
1300
|
data = ws_mod.create_custom_action(
|
|
1301
1301
|
session, worksheet_id, json.loads(action_spec),
|
|
1302
|
-
app_id=eff_app,
|
|
1302
|
+
app_id=eff_app, btn_id=btn_id,
|
|
1303
1303
|
)
|
|
1304
1304
|
else:
|
|
1305
1305
|
btn_data = json.loads(btn_config)
|
|
@@ -15,6 +15,8 @@ the plain CLI commands — see ``editspec.RETIRED_OPS``.
|
|
|
15
15
|
"""
|
|
16
16
|
from __future__ import annotations
|
|
17
17
|
|
|
18
|
+
import json
|
|
19
|
+
import uuid
|
|
18
20
|
from typing import Any, Callable
|
|
19
21
|
|
|
20
22
|
from hap_cli.core import page as page_mod
|
|
@@ -31,6 +33,61 @@ def _ws_field_save(s, ws_id, controls):
|
|
|
31
33
|
return ws_mod.save_controls(s, ws_id, controls)
|
|
32
34
|
|
|
33
35
|
|
|
36
|
+
# ── inline sub-table sheets: written through the parent (bug #455768) ───
|
|
37
|
+
# Such a sheet cannot be saved on its own; the product edits its columns
|
|
38
|
+
# as the parent's sub-table column ``relationControls`` and saves the
|
|
39
|
+
# parent. See ``AppIndex._subtable_map`` for the evidence.
|
|
40
|
+
def _subtable_columns(idx: AppIndex, ws_ref: str):
|
|
41
|
+
"""``(parent_id, parent controls, host column)`` copies, or None.
|
|
42
|
+
|
|
43
|
+
The host column and its ``relationControls`` are copied so the
|
|
44
|
+
mutation never leaks into the index cache.
|
|
45
|
+
"""
|
|
46
|
+
host = idx.subtable_host(ws_ref)
|
|
47
|
+
if host is None:
|
|
48
|
+
return None
|
|
49
|
+
parent_id, column_id = host
|
|
50
|
+
controls = [dict(c) for c in idx.controls(parent_id)]
|
|
51
|
+
column = next((c for c in controls if c.get("controlId") == column_id),
|
|
52
|
+
None)
|
|
53
|
+
if column is None:
|
|
54
|
+
raise ResolveError(
|
|
55
|
+
f"the sub-table column hosting worksheet {ws_ref!r} is gone")
|
|
56
|
+
column["relationControls"] = [dict(r) for r in
|
|
57
|
+
column.get("relationControls") or []]
|
|
58
|
+
return parent_id, controls, column
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _sorts_of(column: dict) -> tuple[dict, list]:
|
|
62
|
+
adv = column.get("advancedSetting")
|
|
63
|
+
if isinstance(adv, str):
|
|
64
|
+
try:
|
|
65
|
+
adv = json.loads(adv) if adv else {}
|
|
66
|
+
except ValueError:
|
|
67
|
+
adv = {}
|
|
68
|
+
adv = dict(adv or {})
|
|
69
|
+
try:
|
|
70
|
+
sorts = json.loads(adv.get("controlssorts") or "[]")
|
|
71
|
+
except (TypeError, ValueError):
|
|
72
|
+
sorts = []
|
|
73
|
+
return adv, sorts if isinstance(sorts, list) else []
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _set_display(column: dict, show: list, sorts: list) -> None:
|
|
77
|
+
"""Keep ``showControls`` and ``controlssorts`` in step with the
|
|
78
|
+
columns — the product updates both on every add/delete/reorder, or
|
|
79
|
+
the sub-table renders its columns out of order (MDPublic
|
|
80
|
+
ConfigureControls.jsx ``updateControlsWithSorts``)."""
|
|
81
|
+
adv, _ = _sorts_of(column)
|
|
82
|
+
adv["controlssorts"] = json.dumps(sorts)
|
|
83
|
+
column["advancedSetting"] = adv
|
|
84
|
+
column["showControls"] = show
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _sub_label(ws_ref: str) -> str:
|
|
88
|
+
return f"'{ws_ref}' (sub-table, saved through its parent)"
|
|
89
|
+
|
|
90
|
+
|
|
34
91
|
def _far_worksheet_of(controls: list[dict], bridge_id: str) -> str:
|
|
35
92
|
"""The worksheet a bridge column reaches, from its own dataSource."""
|
|
36
93
|
for c in controls:
|
|
@@ -144,6 +201,27 @@ def _field_add(op: dict[str, Any], idx: AppIndex) -> list[Action]:
|
|
|
144
201
|
# place.
|
|
145
202
|
control = lower_field(field)
|
|
146
203
|
|
|
204
|
+
sub = _subtable_columns(idx, op["worksheet"])
|
|
205
|
+
if sub is not None:
|
|
206
|
+
if control.get("type") == 34:
|
|
207
|
+
raise ResolveError(
|
|
208
|
+
f"cannot add sub-table {field.get('name')!r} inside the "
|
|
209
|
+
f"sub-table {op['worksheet']!r}")
|
|
210
|
+
parent_id, controls, column = sub
|
|
211
|
+
control = dict(control)
|
|
212
|
+
# A dashed placeholder: the server mints the real id and rewrites
|
|
213
|
+
# it in showControls too (verified live 2026-10-08).
|
|
214
|
+
control["controlId"] = str(uuid.uuid4())
|
|
215
|
+
column["relationControls"].append(control)
|
|
216
|
+
_, sorts = _sorts_of(column)
|
|
217
|
+
_set_display(column,
|
|
218
|
+
list(column.get("showControls") or [])
|
|
219
|
+
+ [control["controlId"]],
|
|
220
|
+
sorts + [control["controlId"]])
|
|
221
|
+
return [Action(f"add field '{op['field']['name']}' to "
|
|
222
|
+
f"{_sub_label(op['worksheet'])}",
|
|
223
|
+
lambda s: _ws_field_save(s, parent_id, controls))]
|
|
224
|
+
|
|
147
225
|
# Mounting an existing worksheet is a two-step handshake — build the
|
|
148
226
|
# SUB_LIST, then wire the child side's back-relation — and
|
|
149
227
|
# `mount_existing_subtable` is the composite that already does both.
|
|
@@ -179,11 +257,20 @@ def _match_control(controls: list, ref: str) -> dict:
|
|
|
179
257
|
|
|
180
258
|
def _field_update(op: dict[str, Any], idx: AppIndex) -> list[Action]:
|
|
181
259
|
ws_id = idx.worksheet_id(op["worksheet"])
|
|
182
|
-
|
|
183
|
-
|
|
260
|
+
sub = _subtable_columns(idx, op["worksheet"])
|
|
261
|
+
if sub is not None:
|
|
262
|
+
parent_id, controls, column = sub
|
|
263
|
+
target = _match_control(column["relationControls"], op["field"])
|
|
264
|
+
else:
|
|
265
|
+
controls = [dict(c) for c in idx.controls(op["worksheet"])]
|
|
266
|
+
target = _match_control(controls, op["field"])
|
|
184
267
|
if op.get("rename"):
|
|
185
268
|
target["controlName"] = op["rename"]
|
|
186
269
|
target.update(op.get("set", {}))
|
|
270
|
+
if sub is not None:
|
|
271
|
+
return [Action(f"update field '{op['field']}' on "
|
|
272
|
+
f"{_sub_label(op['worksheet'])}",
|
|
273
|
+
lambda s: _ws_field_save(s, parent_id, controls))]
|
|
187
274
|
return [Action(f"update field '{op['field']}' on '{op['worksheet']}' "
|
|
188
275
|
f"(write-back {len(controls)} controls)",
|
|
189
276
|
lambda s: _ws_field_save(s, ws_id, controls))]
|
|
@@ -191,6 +278,21 @@ def _field_update(op: dict[str, Any], idx: AppIndex) -> list[Action]:
|
|
|
191
278
|
|
|
192
279
|
def _field_delete(op: dict[str, Any], idx: AppIndex) -> list[Action]:
|
|
193
280
|
ws_id = idx.worksheet_id(op["worksheet"])
|
|
281
|
+
sub = _subtable_columns(idx, op["worksheet"])
|
|
282
|
+
if sub is not None:
|
|
283
|
+
parent_id, controls, column = sub
|
|
284
|
+
gone = _match_control(column["relationControls"],
|
|
285
|
+
op["field"]).get("controlId")
|
|
286
|
+
column["relationControls"] = [r for r in column["relationControls"]
|
|
287
|
+
if r.get("controlId") != gone]
|
|
288
|
+
_, sorts = _sorts_of(column)
|
|
289
|
+
_set_display(column,
|
|
290
|
+
[i for i in column.get("showControls") or []
|
|
291
|
+
if i != gone],
|
|
292
|
+
[i for i in sorts if i != gone])
|
|
293
|
+
return [Action(f"delete field '{op['field']}' from "
|
|
294
|
+
f"{_sub_label(op['worksheet'])}",
|
|
295
|
+
lambda s: _ws_field_save(s, parent_id, controls))]
|
|
194
296
|
controls = idx.controls(op["worksheet"])
|
|
195
297
|
target = _match_control(controls, op["field"])
|
|
196
298
|
remaining = [c for c in controls
|
|
@@ -202,7 +304,12 @@ def _field_delete(op: dict[str, Any], idx: AppIndex) -> list[Action]:
|
|
|
202
304
|
|
|
203
305
|
def _field_reorder(op: dict[str, Any], idx: AppIndex) -> list[Action]:
|
|
204
306
|
ws_id = idx.worksheet_id(op["worksheet"])
|
|
205
|
-
|
|
307
|
+
sub = _subtable_columns(idx, op["worksheet"])
|
|
308
|
+
if sub is not None:
|
|
309
|
+
parent_id, parent_controls, column = sub
|
|
310
|
+
controls = column["relationControls"]
|
|
311
|
+
else:
|
|
312
|
+
controls = [dict(c) for c in idx.controls(op["worksheet"])]
|
|
206
313
|
by_ref: dict[str, dict] = {}
|
|
207
314
|
for c in controls:
|
|
208
315
|
for k in (c.get("controlId"), c.get("controlName"), c.get("alias")):
|
|
@@ -225,6 +332,15 @@ def _field_reorder(op: dict[str, Any], idx: AppIndex) -> list[Action]:
|
|
|
225
332
|
for i, c in enumerate(ordered):
|
|
226
333
|
c["row"] = i
|
|
227
334
|
c["col"] = 0
|
|
335
|
+
if sub is not None:
|
|
336
|
+
column["relationControls"] = ordered
|
|
337
|
+
ids = [c.get("controlId") for c in ordered]
|
|
338
|
+
shown = set(column.get("showControls") or [])
|
|
339
|
+
_set_display(column, [i for i in ids if i in shown], ids)
|
|
340
|
+
return [Action(f"reorder {len(op['order'])} field(s) on "
|
|
341
|
+
f"{_sub_label(op['worksheet'])}",
|
|
342
|
+
lambda s: _ws_field_save(s, parent_id,
|
|
343
|
+
parent_controls))]
|
|
228
344
|
return [Action(f"reorder {len(op['order'])} field(s) on '{op['worksheet']}'",
|
|
229
345
|
lambda s: _ws_field_save(s, ws_id, ordered))]
|
|
230
346
|
|
|
@@ -13,6 +13,7 @@ reference an element a previous op just created (intra-spec chaining).
|
|
|
13
13
|
"""
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
|
+
import logging
|
|
16
17
|
from typing import Any, Optional
|
|
17
18
|
|
|
18
19
|
from hap_cli.core import app as app_mod
|
|
@@ -24,6 +25,8 @@ from hap_cli.core import workflow as wf_mod
|
|
|
24
25
|
from hap_cli.core.editor.errors import ResolveError
|
|
25
26
|
from hap_cli.core.session import Session
|
|
26
27
|
|
|
28
|
+
logger = logging.getLogger("hap_cli.editor")
|
|
29
|
+
|
|
27
30
|
|
|
28
31
|
class AppIndex:
|
|
29
32
|
def __init__(self, session: Session, app_id: str, org_id: str = "",
|
|
@@ -35,6 +38,8 @@ class AppIndex:
|
|
|
35
38
|
self._sections: list[dict] = []
|
|
36
39
|
self._ws_controls: dict[str, list] = {}
|
|
37
40
|
self._ws_views: dict[str, list] = {}
|
|
41
|
+
self._subtables: Optional[dict[str, dict]] = None
|
|
42
|
+
self._subtable_kind: dict[str, Any] = {}
|
|
38
43
|
if app_detail:
|
|
39
44
|
self._index_sections(app_detail)
|
|
40
45
|
|
|
@@ -115,6 +120,8 @@ class AppIndex:
|
|
|
115
120
|
self._index_sections(detail)
|
|
116
121
|
self._ws_controls.clear()
|
|
117
122
|
self._ws_views.clear()
|
|
123
|
+
self._subtables = None
|
|
124
|
+
self._subtable_kind.clear()
|
|
118
125
|
for attr in ("_roles_cache", "_btn_cache", "_wf_cache"):
|
|
119
126
|
if hasattr(self, attr):
|
|
120
127
|
delattr(self, attr)
|
|
@@ -145,8 +152,133 @@ class AppIndex:
|
|
|
145
152
|
for _sid, item in self._ws_items():
|
|
146
153
|
if item.get("id") == ref or item.get("name") == ref:
|
|
147
154
|
return item.get("id")
|
|
155
|
+
child = self._subtable_sheet(ref)
|
|
156
|
+
if child:
|
|
157
|
+
return child
|
|
148
158
|
raise ResolveError(f"worksheet {ref!r} not found in app {self.app_id}")
|
|
149
159
|
|
|
160
|
+
# ── inline sub-table sheets (not in navigation) ─────────────────
|
|
161
|
+
#
|
|
162
|
+
# An inline sub-table keeps its rows in a worksheet of its own, named
|
|
163
|
+
# by the sub-table column's ``dataSource``. That sheet never appears
|
|
164
|
+
# among the app's navigation items, so resolving only those left it
|
|
165
|
+
# unreachable (bug #455768: field.update on it -> "worksheet not
|
|
166
|
+
# found"). It is found here instead by scanning the type-34 columns
|
|
167
|
+
# of the navigation worksheets — only after a navigation miss, so an
|
|
168
|
+
# ordinary reference costs no extra request.
|
|
169
|
+
#
|
|
170
|
+
# The product edits such a sheet's columns through the parent: they
|
|
171
|
+
# ride in the parent column's ``relationControls`` and the parent's
|
|
172
|
+
# SaveWorksheetControls stores them (MDPublic src/pages/widgetConfig/
|
|
173
|
+
# widgetSetting/components/sublist/ConfigureControls.jsx
|
|
174
|
+
# handleControlDataChange / addControl / handleDeleteWidget; identical
|
|
175
|
+
# in pd-openweb). Reading or saving the child sheet directly is
|
|
176
|
+
# refused (verified live 2026-10-08: GetWorksheetControls -> 权限不足,
|
|
177
|
+
# SaveWorksheetControls sourceId=<child> -> code 16). So the index
|
|
178
|
+
# records which parent column hosts each child; field ops write
|
|
179
|
+
# through it (see ``ops._subtable_write``).
|
|
180
|
+
def _subtable_map(self) -> dict[str, dict]:
|
|
181
|
+
if self._subtables is not None:
|
|
182
|
+
return self._subtables
|
|
183
|
+
nav_ids = {item.get("id") for _sid, item in self._ws_items()}
|
|
184
|
+
found: dict[str, dict] = {}
|
|
185
|
+
for _sid, item in self._ws_items():
|
|
186
|
+
parent_id = item.get("id")
|
|
187
|
+
try:
|
|
188
|
+
controls = self.controls(parent_id)
|
|
189
|
+
except Exception as exc: # noqa: BLE001 — one unreadable sheet
|
|
190
|
+
logger.warning("could not read controls of %s while looking "
|
|
191
|
+
"for sub-table sheets: %s", parent_id, exc)
|
|
192
|
+
continue
|
|
193
|
+
for c in controls:
|
|
194
|
+
if c.get("type") != 34:
|
|
195
|
+
continue
|
|
196
|
+
child = (c.get("dataSource") or "").strip("$")
|
|
197
|
+
# A sub-table showing a worksheet that is itself in
|
|
198
|
+
# navigation resolves there; a placeholder (dashed uuid)
|
|
199
|
+
# has no sheet yet.
|
|
200
|
+
if not child or child in nav_ids or "-" in child:
|
|
201
|
+
continue
|
|
202
|
+
found.setdefault(child, {
|
|
203
|
+
"parent": parent_id,
|
|
204
|
+
"parent_name": item.get("name") or "",
|
|
205
|
+
"column": c.get("controlId") or "",
|
|
206
|
+
"column_name": c.get("controlName") or "",
|
|
207
|
+
})
|
|
208
|
+
self._subtables = found
|
|
209
|
+
return found
|
|
210
|
+
|
|
211
|
+
def _subtable_sheet(self, ref: str) -> str:
|
|
212
|
+
"""Resolve ``ref`` to an inline sub-table sheet id, or "".
|
|
213
|
+
|
|
214
|
+
Accepts the sheet id, ``<parent>/<sub-table column>`` (each side
|
|
215
|
+
by name or id), or the bare column name when only one parent has
|
|
216
|
+
a sub-table column of that name.
|
|
217
|
+
"""
|
|
218
|
+
subs = self._subtable_map()
|
|
219
|
+
if not subs:
|
|
220
|
+
return ""
|
|
221
|
+
hit = ""
|
|
222
|
+
if ref in subs:
|
|
223
|
+
hit = ref
|
|
224
|
+
elif "/" in ref:
|
|
225
|
+
parent_ref, col_ref = ref.rsplit("/", 1)
|
|
226
|
+
for child, h in subs.items():
|
|
227
|
+
if parent_ref in (h["parent"], h["parent_name"]) \
|
|
228
|
+
and col_ref in (h["column"], h["column_name"]):
|
|
229
|
+
hit = child
|
|
230
|
+
break
|
|
231
|
+
else:
|
|
232
|
+
named = [child for child, h in subs.items()
|
|
233
|
+
if ref == h["column_name"]]
|
|
234
|
+
if len(named) > 1:
|
|
235
|
+
where = ", ".join(f"'{subs[c]['parent_name']}/{ref}'"
|
|
236
|
+
for c in named)
|
|
237
|
+
raise ResolveError(
|
|
238
|
+
f"worksheet {ref!r} is ambiguous: more than one "
|
|
239
|
+
f"sub-table has that name — write {where}")
|
|
240
|
+
hit = named[0] if named else ""
|
|
241
|
+
if hit:
|
|
242
|
+
self._check_inline(hit)
|
|
243
|
+
return hit
|
|
244
|
+
|
|
245
|
+
def _check_inline(self, child: str) -> None:
|
|
246
|
+
"""Refuse a sub-table that shows a worksheet from another app.
|
|
247
|
+
|
|
248
|
+
Its columns belong to that worksheet, which the parent's
|
|
249
|
+
write-back cannot change — the product only edits a sub-table's
|
|
250
|
+
columns when the sheet was created for it (GetWorksheetInfo
|
|
251
|
+
``type`` 2; MDPublic sub_list.jsx: ``res.type === 2 ? 'new' :
|
|
252
|
+
'relate'``).
|
|
253
|
+
"""
|
|
254
|
+
if child not in self._subtable_kind:
|
|
255
|
+
try:
|
|
256
|
+
info = ws_mod.get_worksheet_info(self.session, child)
|
|
257
|
+
except Exception as exc: # noqa: BLE001
|
|
258
|
+
logger.warning("could not read sub-table sheet %s: %s",
|
|
259
|
+
child, exc)
|
|
260
|
+
info = {}
|
|
261
|
+
payload = info.get("data") if isinstance(info, dict) \
|
|
262
|
+
and isinstance(info.get("data"), dict) else info
|
|
263
|
+
self._subtable_kind[child] = (payload or {}).get("type") \
|
|
264
|
+
if isinstance(payload, dict) else None
|
|
265
|
+
if self._subtable_kind[child] != ws_mod.SUBTABLE_WORKSHEET_TYPE:
|
|
266
|
+
h = self._subtables[child]
|
|
267
|
+
raise ResolveError(
|
|
268
|
+
f"worksheet {child!r} is shown by the sub-table "
|
|
269
|
+
f"'{h['parent_name']}/{h['column_name']}' but belongs to "
|
|
270
|
+
f"another app; edit its fields in that app")
|
|
271
|
+
|
|
272
|
+
def subtable_host(self, ref: str) -> Optional[tuple[str, str]]:
|
|
273
|
+
"""``(parent worksheet id, sub-table column id)`` when ``ref`` is
|
|
274
|
+
an inline sub-table sheet, else ``None``."""
|
|
275
|
+
ws_id = self.worksheet_id(ref)
|
|
276
|
+
for _sid, item in self._ws_items():
|
|
277
|
+
if item.get("id") == ws_id:
|
|
278
|
+
return None
|
|
279
|
+
h = self._subtable_map().get(ws_id)
|
|
280
|
+
return (h["parent"], h["column"]) if h else None
|
|
281
|
+
|
|
150
282
|
def worksheet_exists(self, ref: str) -> bool:
|
|
151
283
|
try:
|
|
152
284
|
self.worksheet_id(ref)
|
|
@@ -165,7 +297,11 @@ class AppIndex:
|
|
|
165
297
|
def controls(self, ws_ref: str) -> list[dict]:
|
|
166
298
|
ws_id = self.worksheet_id(ws_ref)
|
|
167
299
|
if ws_id not in self._ws_controls:
|
|
168
|
-
|
|
300
|
+
host = self.subtable_host(ws_id)
|
|
301
|
+
# A sub-table's sheet is only readable with its parent named.
|
|
302
|
+
data = ws_mod.get_worksheet_controls(
|
|
303
|
+
self.session, ws_id, self.app_id,
|
|
304
|
+
parent_worksheet_id=host[0] if host else "")
|
|
169
305
|
self._ws_controls[ws_id] = data if isinstance(data, list) else []
|
|
170
306
|
return self._ws_controls[ws_id]
|
|
171
307
|
|
|
@@ -1141,8 +1141,11 @@ def _enforce_rules(
|
|
|
1141
1141
|
return None
|
|
1142
1142
|
# Read-only / required are the form's own checks, not business rules;
|
|
1143
1143
|
# they live in their own module so the two can be worked on apart.
|
|
1144
|
+
# The cells filled in above are the form's, not the caller's: they
|
|
1145
|
+
# go in even when hidden, so only what the caller wrote is judged.
|
|
1144
1146
|
record_form_checks.check_form(session, worksheet_id, controls,
|
|
1145
|
-
row_id=row_id, app_id=app_id
|
|
1147
|
+
row_id=row_id, app_id=app_id,
|
|
1148
|
+
defaulted_ids=defaulted)
|
|
1146
1149
|
check = record_rules.check_pending_write(
|
|
1147
1150
|
session, worksheet_id, controls, row_id=row_id, app_id=app_id,
|
|
1148
1151
|
defaulted_ids=defaulted)
|
|
@@ -1179,7 +1182,8 @@ def _enforce_rules_for_batch(
|
|
|
1179
1182
|
# Same form checks as the single-row path, judged before any row is
|
|
1180
1183
|
# written — see _enforce_rules.
|
|
1181
1184
|
record_form_checks.check_form_batch(session, worksheet_id, items,
|
|
1182
|
-
app_id=app_id
|
|
1185
|
+
app_id=app_id,
|
|
1186
|
+
defaulted_ids=defaulted)
|
|
1183
1187
|
rules, controls = record_rules.load_rules_and_fields(
|
|
1184
1188
|
session, worksheet_id, app_id)
|
|
1185
1189
|
merged = record_rules.RuleCheck()
|