openmodule 13.0.2__tar.gz → 13.1.0rc0__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.
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/AUTHORS +0 -1
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/ChangeLog +14 -27
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/PKG-INFO +1 -1
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/access_service.md +25 -1
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/config.py +5 -4
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/models/access_service.py +8 -10
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/models/presence.py +19 -1
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/models/validation.py +1 -1
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/models/vehicle.py +7 -1
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/rpc/client.py +3 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/sentry.py +0 -1
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/threading.py +0 -1
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/utils/eventlog.py +5 -1
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/utils/kv_store.py +77 -51
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/utils/matching.py +0 -1
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/utils/presence.py +67 -54
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule.egg-info/PKG-INFO +1 -1
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule.egg-info/SOURCES.txt +6 -0
- openmodule-13.1.0rc0/openmodule.egg-info/pbr.json +1 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule.egg-info/requires.txt +1 -1
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/database.py +1 -1
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/presence.py +24 -6
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/requirements.txt +1 -1
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/config.py +2 -0
- openmodule-13.1.0rc0/tests/resources/translation/locale/de/LC_MESSAGES/translation.mo +0 -0
- openmodule-13.1.0rc0/tests/resources/translation/locale/de/LC_MESSAGES/translation.po +21 -0
- openmodule-13.1.0rc0/tests/resources/translation/locale/en/LC_MESSAGES/translation.mo +0 -0
- openmodule-13.1.0rc0/tests/resources/translation/locale/en/LC_MESSAGES/translation.po +21 -0
- openmodule-13.1.0rc0/tests/resources/translation/locale/translation.pot +8 -0
- openmodule-13.1.0rc0/tests/resources/translation/translate.sh +3 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_rpc.py +3 -3
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_utils_access_service.py +22 -1
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_utils_eventlog.py +8 -4
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_utils_kv_store.py +91 -30
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_utils_presence.py +384 -3
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_utils_vehicle.py +5 -2
- openmodule-13.0.2/openmodule.egg-info/pbr.json +0 -1
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/.gitlab-ci.yml +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/LICENSE +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/README.md +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/coding_standard.md +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/commands.md +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/connection_status_listener.md +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/core.md +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/csv_export.md +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/database.md +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/deprecated_code/README.md +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/deprecated_code/access_service/openmodule/models/access_service.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/deprecated_code/access_service/openmodule/utils/access_service.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/deprecated_code/access_service/openmodule_test/access_service.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/deprecated_code/access_service/tests/test_utils_access_service.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/deprecated_code/api/openmodule/utils/api.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/deprecated_code/api/openmodule_test/api.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/deprecated_code/api/tests/test_utils_api.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/deprecated_code/package_reader/openmodule/utils/package_reader.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/deprecated_code/package_reader/openmodule_test/fake_package_creator.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/deprecated_code/package_reader/tests/test_package_reader.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/event_sending.md +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/health.md +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/known_issues.md +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/migrations.md +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/package_reader.md +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/qc.md +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/rpc.md +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/settings.md +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/testing.md +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/docs/translation.md +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/__init__.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/alert.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/checks.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/connection_status.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/core.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/database/custom_types.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/database/database.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/database/env.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/dispatcher.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/health.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/logging.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/messaging.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/models/__init__.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/models/alert.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/models/base.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/models/io.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/models/kv_store.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/models/privacy.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/models/rpc.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/models/settings.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/rpc/__init__.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/rpc/common.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/rpc/server.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/utils/__init__.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/utils/access_service.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/utils/charset.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/utils/csv_export.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/utils/databox.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/utils/db_helper.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/utils/io.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/utils/misc_functions.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/utils/package_reader.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/utils/schema.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/utils/settings.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/utils/translation.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule/utils/validation.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule.egg-info/dependency_links.txt +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule.egg-info/not-zip-safe +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule.egg-info/top_level.txt +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_commands/__init__.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_commands/setup.cfg +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_commands/setup.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_commands/translate.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/__init__.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/alert.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/connection_status.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/core.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/eventlistener.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/files.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/gate.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/health.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/interrupt.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/io_simulator.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/package_reader.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/requirements.txt +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/rpc.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/settings.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/setup.cfg +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/setup.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/utils.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/openmodule_test/zeromq.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/setup.cfg +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/setup.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/test-requirements.txt +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/__init__.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/database_models_migration.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/database_models_test.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/invalid_database/alembic/README +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/invalid_database/alembic/__init__.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/invalid_database/alembic/env.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/invalid_database/alembic/script.py.mako +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/invalid_database/alembic/versions/ff26e54332f9_datetime_models.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/invalid_database/alembic.ini +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/invalid_database/makemigration.sh +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/migration_test_database/__init__.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/migration_test_database/alembic/__init__.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/migration_test_database/alembic/env.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/migration_test_database/alembic/script.py.mako +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/migration_test_database/alembic/versions/19789aa5361c_initial.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/migration_test_database/alembic/versions/19d887929ae7_alter.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/migration_test_database/alembic/versions/__init__.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/migration_test_database/alembic.ini +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/migration_test_database/makemigration.sh +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/resources/configs/config.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/resources/configs/test_config.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/resources/configs/test_config_1.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/resources/standard_schemes/DEFAULT-10.yml +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/resources/standard_schemes/DEFAULT-20.yml +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/resources/standard_schemes/LEGACY-0.yml +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/resources/utils_matching/A-10.yml +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/resources/utils_matching/A-20.yml +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/resources/utils_matching/DEFAULT-10.yml +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/resources/utils_matching/DEFAULT-20.yml +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/resources/utils_matching/DEFAULT-30.yml +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/resources/utils_matching/LEGACY-0.yml +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/resources/utils_matching/TEST-10.yml +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/resources/utils_matching/TEST-20.yml +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/resources/utils_matching/TEST-30.yml +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/resources/utils_matching/TEST-40.yml +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_access_service_database/alembic/__init__.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_access_service_database/alembic/env.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_access_service_database/alembic/script.py.mako +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_access_service_database/alembic/versions/7bd4fcd38fde_removed_nfc_and_pin.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_access_service_database/alembic/versions/9ca98a2e5674_added_parksettings_id.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_access_service_database/alembic/versions/c821971f9230_initial.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_access_service_database/alembic.ini +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_access_service_database/makemigration.sh +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_alert.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_checks.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_config.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_connection_status.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_core.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_database/alembic/__init__.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_database/alembic/env.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_database/alembic/script.py.mako +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_database/alembic/versions/32b8c728abbf_initial.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_database/alembic.ini +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_database/makemigration.sh +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_database.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_dispatcher.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_health.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_interrupt.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_io_listen.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_kv_store_database/alembic/__init__.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_kv_store_database/alembic/env.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_kv_store_database/alembic/script.py.mako +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_kv_store_database/alembic/versions/9c5c944221f4_deprecated_kv_entry_example.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_kv_store_database/alembic/versions/c55a69026a25_initial.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_kv_store_database/alembic.ini +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_kv_store_database/makemigration.sh +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_kv_store_multiple_database/alembic/README +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_kv_store_multiple_database/alembic/__init__.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_kv_store_multiple_database/alembic/env.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_kv_store_multiple_database/alembic/script.py.mako +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_kv_store_multiple_database/alembic/versions/cdb3214131a9_initial.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_kv_store_multiple_database/alembic.ini +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_kv_store_multiple_database/makemigration.sh +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_messaging.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_mockrpcclient.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_model.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_schema.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_sentry.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_test_alert.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_test_gate.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_test_zeromq.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_utils_charset.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_utils_csv_export.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_utils_databox.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_utils_kv_store_multiple.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_utils_matching.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_utils_misc_functions.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_utils_package_reader.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_utils_settings.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tests/test_utils_validation.py +0 -0
- {openmodule-13.0.2 → openmodule-13.1.0rc0}/tox.ini +0 -0
|
@@ -4,7 +4,6 @@ Matthias Mietschnig <m.mietschnig@arivo.co>
|
|
|
4
4
|
Philipp Reitter <i@philipp.ninja>
|
|
5
5
|
Philipp Reitter <p.reitter@accessio.at>
|
|
6
6
|
Philipp Reitter <p.reitter@gmail.com>
|
|
7
|
-
Stefan Bräuer <s.braeuer@ariv.co>
|
|
8
7
|
Stefan Bräuer <s.braeuer@arivo.co>
|
|
9
8
|
Thomas Senfter <t.senfter@accessio.at>
|
|
10
9
|
Thomas Senfter <t.senfter@arivo.co>
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
CHANGES
|
|
2
2
|
=======
|
|
3
3
|
|
|
4
|
+
v13.1.0.rc0
|
|
5
|
+
-----------
|
|
6
|
+
|
|
7
|
+
* OM-61 Openmodule Anpassungen für Access Events
|
|
8
|
+
* [OM-56] added enter\_time and leave\_time to vehicle and implemented presence message changes
|
|
9
|
+
* fixes and refactoring of presencelistener
|
|
10
|
+
* Resolve OM-78 improved KVStore performance
|
|
11
|
+
* fixes INBOX-2510
|
|
12
|
+
|
|
13
|
+
v13.0.3
|
|
14
|
+
-------
|
|
15
|
+
|
|
16
|
+
* added garage\_settings mode "barrier\_bypass" in settings models removal of some unused imports
|
|
17
|
+
|
|
4
18
|
v13.0.2
|
|
5
19
|
-------
|
|
6
20
|
|
|
@@ -128,8 +142,6 @@ v11.1.0.rc0
|
|
|
128
142
|
* removing ="asdf" for strings
|
|
129
143
|
* added decimal separator support for english
|
|
130
144
|
* some testcases for csv exporter, incorrect default value + static missing [skip ci]
|
|
131
|
-
* fixed comment
|
|
132
|
-
* added csv\_export library (no testcases)
|
|
133
145
|
* removed mock rpcs from schema
|
|
134
146
|
|
|
135
147
|
v11.0.3
|
|
@@ -217,28 +229,3 @@ v8.1.2
|
|
|
217
229
|
|
|
218
230
|
* dont register schemas for mock handlers
|
|
219
231
|
* fixes a race condition in the io tests
|
|
220
|
-
* update known issues
|
|
221
|
-
|
|
222
|
-
v8.1.1
|
|
223
|
-
------
|
|
224
|
-
|
|
225
|
-
* adds multiprocessing logging to openmodule requirements
|
|
226
|
-
|
|
227
|
-
v8.1.0
|
|
228
|
-
------
|
|
229
|
-
|
|
230
|
-
* changelog and breaking changes updated
|
|
231
|
-
* \* Adds an IO Listener \* Adds a SigTERM interrupt handler which converts to KeyboardInterrupt so shutdown in Docker containers is easier \* The main Test Mixin was refactored to start a separate process and raise a Interrupt. The other test methods were removed due to being hacky and crashing tox/pytest from time to time. Since this only breaks test code we don't bump the major version. \* The openmodule now waits for a connection with the message broker on startup \* Fixes an issue in the RPCServerTestMixin
|
|
232
|
-
* RPCServerTestMixin now only registers one random channel instead of all channels on the server. (Author: Philipp Reitter)
|
|
233
|
-
* add infos to CountMessage
|
|
234
|
-
* Added line to exception\_in\_function to call new \_signal\_in\_function if exception is a signal or KeyboardInterrupt
|
|
235
|
-
|
|
236
|
-
v8.0.1
|
|
237
|
-
------
|
|
238
|
-
|
|
239
|
-
* fixed loading empty yamls fixes rpc server not logging info in debug log
|
|
240
|
-
|
|
241
|
-
v8.0.0
|
|
242
|
-
------
|
|
243
|
-
|
|
244
|
-
* fix in ci job
|
|
@@ -21,6 +21,11 @@ You have to implement the `rpc_check_access` function and probably add some othe
|
|
|
21
21
|
|
|
22
22
|
An example implementations is shown below.
|
|
23
23
|
|
|
24
|
+
**Important:** Every Access Service has to add their human-readable description in the `AccessCheckResponse`.
|
|
25
|
+
The default value of `readable_service_name` is `_("__READABLE_NAME")`, so you do not have to add
|
|
26
|
+
this variable explicitly.
|
|
27
|
+
|
|
28
|
+
|
|
24
29
|
```python
|
|
25
30
|
def rpc_check_access(self, request: AccessCheckRequest, _) -> AccessCheckResponse:
|
|
26
31
|
"""
|
|
@@ -44,9 +49,28 @@ def rpc_check_access(self, request: AccessCheckRequest, _) -> AccessCheckRespons
|
|
|
44
49
|
|
|
45
50
|
self.log.info(f"Found {len(accesses)} matching accesses "
|
|
46
51
|
f"where {len([a for a in accesses if a.accepted])} are valid")
|
|
47
|
-
return AccessCheckResponse(success=True, accesses=accesses)
|
|
52
|
+
return AccessCheckResponse(success=True, accesses=accesses, readable_service_name=_("Test Access Service"))
|
|
48
53
|
```
|
|
49
54
|
|
|
55
|
+
### Access Service Reject Reasons
|
|
56
|
+
|
|
57
|
+
In the enum `AccessCheckRejectReason` common reject reasons can be found.
|
|
58
|
+
|
|
59
|
+
#### Reject Reason "wrong_gate"
|
|
60
|
+
|
|
61
|
+
This reject reason is automatically set if the function `check_accesses_valid_at_gate()` is used.
|
|
62
|
+
|
|
63
|
+
#### Reject Reason "wrong_time"
|
|
64
|
+
|
|
65
|
+
If your access service has some time restriction the reject reason `wrong_time` can be returned.
|
|
66
|
+
**Do not** use this for accesses with start and end (e.g. permanent access for a month)
|
|
67
|
+
as these accesses should be ignored if outside start and end.
|
|
68
|
+
|
|
69
|
+
#### Reject Reason "custom"
|
|
70
|
+
|
|
71
|
+
If the given e.g. license plate should be rejected for a specific other reason the value `custom` should
|
|
72
|
+
be used. If the reject reason `custom` is used then the string field `supplementary_infos` has to be set!
|
|
73
|
+
|
|
50
74
|
## AccessServiceWithSessions
|
|
51
75
|
|
|
52
76
|
In addition to the Access Service stuff implement the session handling functions `check_in_session`, `check_out_session`
|
|
@@ -234,12 +234,13 @@ def testing():
|
|
|
234
234
|
|
|
235
235
|
|
|
236
236
|
def database_folder() -> str:
|
|
237
|
-
path = string("DATABASE_FOLDER", f"/data/sqlite/")
|
|
238
237
|
if testing():
|
|
239
|
-
|
|
238
|
+
default_path = "../sqlite/test/"
|
|
240
239
|
elif debug():
|
|
241
|
-
|
|
242
|
-
|
|
240
|
+
default_path = "../sqlite/debug/"
|
|
241
|
+
else:
|
|
242
|
+
default_path = "/data/sqlite/"
|
|
243
|
+
return string("DATABASE_FOLDER", default_path)
|
|
243
244
|
|
|
244
245
|
|
|
245
246
|
def run_checks() -> bool:
|
|
@@ -1,18 +1,12 @@
|
|
|
1
|
+
from datetime import datetime
|
|
1
2
|
from enum import Enum
|
|
2
|
-
from typing import List, Optional, Dict
|
|
3
|
+
from typing import List, Optional, Dict
|
|
3
4
|
|
|
4
5
|
from pydantic import Field, root_validator
|
|
5
6
|
|
|
6
7
|
from openmodule.models.base import OpenModuleModel, timezone_validator
|
|
7
8
|
from openmodule.models.vehicle import Medium, LPRMedium, MakeModel
|
|
8
|
-
|
|
9
|
-
from dateutil import tz
|
|
10
|
-
from dateutil.parser import parse
|
|
11
|
-
from dateutil.rrule import rrulestr
|
|
12
|
-
from dateutil.tz import UTC
|
|
13
|
-
from pydantic import validator
|
|
14
|
-
import re
|
|
15
|
-
from datetime import datetime, tzinfo, timedelta
|
|
9
|
+
from openmodule.utils.translation import _
|
|
16
10
|
|
|
17
11
|
|
|
18
12
|
class AccessCategory(str, Enum):
|
|
@@ -72,6 +66,7 @@ class AccessCheckAccess(OpenModuleModel):
|
|
|
72
66
|
category: AccessCategory # category used for sorting and eventlog
|
|
73
67
|
used_medium: Medium # medium of the access
|
|
74
68
|
access_data: Dict # complete access data, will be used only for display and debug purposes
|
|
69
|
+
supplementary_infos: Optional[str] # field has to be if reject reason is "custom"
|
|
75
70
|
|
|
76
71
|
accepted: bool # if access service decided access can enter
|
|
77
72
|
reject_reason: Optional[AccessCheckRejectReason] # only if not accepted: reason for not accepted
|
|
@@ -88,9 +83,12 @@ class AccessCheckAccess(OpenModuleModel):
|
|
|
88
83
|
if values["category"] in [AccessCategory.shortterm, AccessCategory.unknown] \
|
|
89
84
|
and values["group_limit"] is not None:
|
|
90
85
|
raise ValueError("group_limit must not be set for shortterm and unknown accesses")
|
|
86
|
+
if values.get("reject_reason") == AccessCheckRejectReason.custom and not values.get("supplementary_infos"):
|
|
87
|
+
raise ValueError("supplementary_infos must be set if reject_reason is custom")
|
|
91
88
|
return values
|
|
92
89
|
|
|
93
90
|
|
|
94
91
|
class AccessCheckResponse(OpenModuleModel):
|
|
95
92
|
success: bool
|
|
96
|
-
accesses: List[AccessCheckAccess] = []
|
|
93
|
+
accesses: List[AccessCheckAccess] = [] # list of all matched accesses (including already rejected ones)
|
|
94
|
+
readable_service_name: str = _("__READABLE_NAME") # readable name of the service
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from typing import Optional, List
|
|
3
3
|
|
|
4
|
-
from pydantic import Field
|
|
4
|
+
from pydantic import Field, root_validator
|
|
5
5
|
|
|
6
6
|
from openmodule.models.base import ZMQMessage, OpenModuleModel, Gateway, timezone_validator
|
|
7
7
|
from openmodule.models.vehicle import Medium, LPRMedium, MakeModel, PresenceAllIds, EnterDirection, QRMedium
|
|
@@ -24,14 +24,23 @@ class PresenceBaseData(OpenModuleModel):
|
|
|
24
24
|
make_model: Optional[MakeModel]
|
|
25
25
|
all_ids: PresenceAllIds
|
|
26
26
|
enter_direction: EnterDirection = EnterDirection.unknown
|
|
27
|
+
enter_time: datetime
|
|
27
28
|
|
|
28
29
|
_tz_last_update = timezone_validator("last_update")
|
|
30
|
+
_tz_enter_time = timezone_validator("enter_time")
|
|
29
31
|
|
|
30
32
|
class Config:
|
|
31
33
|
# allows setting attributes both via the alias, and the field name.
|
|
32
34
|
# is used to rename old variables which are hard to understand by their name (e.g. id -> medium id)
|
|
33
35
|
allow_population_by_field_name = True
|
|
34
36
|
|
|
37
|
+
@root_validator(pre=True)
|
|
38
|
+
def set_enter_time_default(cls, values):
|
|
39
|
+
"""for backward compatibility, set enter_time based on vehicle_id"""
|
|
40
|
+
if values.get("enter_time") is None:
|
|
41
|
+
values["enter_time"] = values["vehicle_id"] / 1e9 # timestamp from vehicle_id
|
|
42
|
+
return values
|
|
43
|
+
|
|
35
44
|
|
|
36
45
|
class PresenceBaseMessage(PresenceBaseData, ZMQMessage):
|
|
37
46
|
pass
|
|
@@ -58,6 +67,15 @@ class PresenceForwardMessage(PresenceBaseMessage):
|
|
|
58
67
|
class PresenceLeaveMessage(PresenceBaseMessage):
|
|
59
68
|
type: str = "leave"
|
|
60
69
|
num_presents: int = Field(0, alias="num-presents")
|
|
70
|
+
leave_time: datetime = Field(..., alias="leave-time")
|
|
71
|
+
|
|
72
|
+
_tz_leave_time = timezone_validator("leave_time")
|
|
73
|
+
|
|
74
|
+
@root_validator(pre=True)
|
|
75
|
+
def set_leave_time_default(cls, values):
|
|
76
|
+
if values.get("leave_time") is None:
|
|
77
|
+
values["leave_time"] = values["timestamp"] # timestamp from message timestamp
|
|
78
|
+
return values
|
|
61
79
|
|
|
62
80
|
|
|
63
81
|
class PresenceEnterMessage(PresenceBaseMessage):
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
from datetime import datetime
|
|
1
2
|
from enum import Enum
|
|
2
3
|
from typing import Optional, List, Iterable, Union
|
|
3
4
|
|
|
4
|
-
from openmodule.models.base import OpenModuleModel
|
|
5
|
+
from openmodule.models.base import OpenModuleModel, timezone_validator
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
def _truncate(x):
|
|
@@ -115,6 +116,11 @@ class Vehicle(OpenModuleModel):
|
|
|
115
116
|
make_model: Optional[MakeModel]
|
|
116
117
|
all_ids: PresenceAllIds = []
|
|
117
118
|
enter_direction: EnterDirection = EnterDirection.unknown
|
|
119
|
+
enter_time: datetime
|
|
120
|
+
leave_time: Optional[datetime]
|
|
121
|
+
|
|
122
|
+
_tz_enter_time = timezone_validator("enter_time")
|
|
123
|
+
_tz_leave_time = timezone_validator("leave_time")
|
|
118
124
|
|
|
119
125
|
def iterate_media(self) -> Iterable[Union[Medium, LPRMedium]]:
|
|
120
126
|
"""
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from datetime import datetime
|
|
2
3
|
from enum import Enum
|
|
3
4
|
from typing import Optional, Dict, Union
|
|
4
5
|
|
|
5
6
|
from pydantic import validator, root_validator
|
|
6
7
|
|
|
7
|
-
from openmodule.config import testing
|
|
8
8
|
from openmodule.core import core
|
|
9
9
|
from openmodule.models.base import OpenModuleModel, ZMQMessage, timezone_validator
|
|
10
10
|
|
|
11
|
+
log = logging.getLogger("Eventlog")
|
|
12
|
+
|
|
11
13
|
|
|
12
14
|
class AnonymizationType(str, Enum):
|
|
13
15
|
lpr = "lpr" # anonymize to "***AB"
|
|
@@ -119,4 +121,6 @@ class EventlogMessage(ZMQMessage):
|
|
|
119
121
|
|
|
120
122
|
|
|
121
123
|
def send_event(infos: EventInfo, message: str, **message_kwargs: MessageKwarg):
|
|
124
|
+
format_kwargs = {key: kwarg.value for key, kwarg in message_kwargs.items()}
|
|
125
|
+
log.info(f"Sending event: {message.format(**format_kwargs)}")
|
|
122
126
|
EventlogMessage.create(infos=infos, message=message, **message_kwargs).send()
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import hashlib
|
|
2
|
-
import json
|
|
3
2
|
import logging
|
|
4
3
|
import random
|
|
5
4
|
import threading
|
|
@@ -7,6 +6,7 @@ import time
|
|
|
7
6
|
import warnings
|
|
8
7
|
from typing import Type, Optional, List, Union, Dict
|
|
9
8
|
|
|
9
|
+
import orjson
|
|
10
10
|
from sqlalchemy import Column, String, Integer
|
|
11
11
|
from sqlalchemy.ext.declarative import declarative_base
|
|
12
12
|
|
|
@@ -41,12 +41,12 @@ class KVEntry:
|
|
|
41
41
|
"""
|
|
42
42
|
raise NotImplementedError
|
|
43
43
|
|
|
44
|
-
def comparison_value(self) -> str:
|
|
44
|
+
def comparison_value(self) -> str: # pragma: no cover
|
|
45
45
|
"""
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
Only used for KVStoreWithChangedNotification
|
|
47
|
+
return some hash or string. Changed messages are only sent if this value changed for a key
|
|
48
48
|
"""
|
|
49
|
-
|
|
49
|
+
raise NotImplementedError
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
class KVStore:
|
|
@@ -93,24 +93,24 @@ class KVStore:
|
|
|
93
93
|
"""RPCs for kv_sync are filtered by service name"""
|
|
94
94
|
return request.service == self.service_name
|
|
95
95
|
|
|
96
|
-
def _kvs_check_etag_mismatched(self, request: KVSetRequest, current_kvs:
|
|
96
|
+
def _kvs_check_etag_mismatched(self, request: KVSetRequest, current_kvs: Dict[str, int]):
|
|
97
97
|
for kv in request.kvs:
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
98
|
+
current_e_tag = current_kvs.get(kv.key)
|
|
99
|
+
if current_e_tag is None and kv.previous_e_tag is not None:
|
|
100
|
+
self.log.warning(f"Key does not exists but previous ETag given for key {kv.key}")
|
|
101
|
+
raise self.ETagMismatchException("Previous ETag given but not in database")
|
|
102
|
+
elif current_e_tag is not None and kv.previous_e_tag is None:
|
|
103
|
+
self.log.warning(f"Key exists but no previous ETag given for key {kv.key}")
|
|
104
|
+
raise self.ETagMismatchException("No previous ETag")
|
|
105
|
+
elif current_e_tag != kv.previous_e_tag:
|
|
106
|
+
self.log.warning(f"Previous ETag does not match for key {kv.key}")
|
|
107
|
+
raise self.ETagMismatchException("Previous ETag does not match")
|
|
108
108
|
|
|
109
109
|
def _kvs_set_parse_kvs(self, kvs: List[KVSetRequestKV]) -> List[KVEntry]:
|
|
110
110
|
to_set = []
|
|
111
111
|
deprecation_warning = False
|
|
112
112
|
for kv in kvs:
|
|
113
|
-
value =
|
|
113
|
+
value = orjson.loads(kv.value)
|
|
114
114
|
if value is not None:
|
|
115
115
|
instances = self.database_table.parse_value(value)
|
|
116
116
|
# fallback for old parse_value() implementations
|
|
@@ -134,14 +134,6 @@ class KVStore:
|
|
|
134
134
|
to_set += instances
|
|
135
135
|
return to_set
|
|
136
136
|
|
|
137
|
-
def _get_comparison_values(self, updated_kv_entries: List[KVEntry]):
|
|
138
|
-
# only used in KVStoreWithChangedNotification
|
|
139
|
-
pass
|
|
140
|
-
|
|
141
|
-
def _send_changed_notification(self, success_keys: List[str], old_values: Dict[str, str]):
|
|
142
|
-
# only used in KVStoreWithChangedNotification
|
|
143
|
-
pass
|
|
144
|
-
|
|
145
137
|
def kvs_set(self, request: KVSetRequest, _) -> KVSetResponse:
|
|
146
138
|
"""
|
|
147
139
|
This method is called by the server to set values in the database.
|
|
@@ -154,24 +146,23 @@ class KVStore:
|
|
|
154
146
|
if not request.kvs:
|
|
155
147
|
return KVSetResponse()
|
|
156
148
|
with self.db() as session:
|
|
157
|
-
current_kvs = \
|
|
158
|
-
|
|
159
|
-
|
|
149
|
+
current_kvs: Dict[str, int] = \
|
|
150
|
+
{kv[0]: kv[1]
|
|
151
|
+
for kv in session.query(self.database_table).
|
|
152
|
+
filter(self.database_table.key.in_([kv.key for kv in request.kvs])).
|
|
153
|
+
with_entities(self.database_table.key, self.database_table.e_tag).all()}
|
|
160
154
|
self._kvs_check_etag_mismatched(request, current_kvs) # raises Exception on mismatch
|
|
161
155
|
to_set = self._kvs_set_parse_kvs(request.kvs)
|
|
162
156
|
try:
|
|
163
157
|
if current_kvs:
|
|
164
158
|
delete_query(session, session.query(self.database_table).filter(
|
|
165
|
-
self.database_table.key.in_(
|
|
159
|
+
self.database_table.key.in_(list(current_kvs.keys()))))
|
|
166
160
|
if to_set:
|
|
167
161
|
session.add_all(to_set)
|
|
168
162
|
session.commit()
|
|
169
163
|
except Exception as e:
|
|
170
164
|
session.rollback()
|
|
171
165
|
raise e
|
|
172
|
-
|
|
173
|
-
if request.kvs:
|
|
174
|
-
self._send_changed_notification([s.key for s in request.kvs], comparison_values)
|
|
175
166
|
return KVSetResponse()
|
|
176
167
|
|
|
177
168
|
def kvs_sync(self, request: KVSyncRequest, _) -> KVSyncResponse:
|
|
@@ -183,15 +174,20 @@ class KVStore:
|
|
|
183
174
|
"""
|
|
184
175
|
self.log.debug("Syncing kvs for %s", request.service)
|
|
185
176
|
with self.db() as session:
|
|
186
|
-
current_kvs =
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
177
|
+
current_kvs: Dict[str, int] = \
|
|
178
|
+
{kv[0]: kv[1]
|
|
179
|
+
for kv in session.query(self.database_table).
|
|
180
|
+
with_entities(self.database_table.key, self.database_table.e_tag).all()}
|
|
181
|
+
changed_kvs = {}
|
|
182
|
+
missing_kvs = {}
|
|
183
|
+
for key, server_e_tag in request.kvs.items():
|
|
184
|
+
device_e_tag = current_kvs.pop(key, None)
|
|
185
|
+
if device_e_tag is None:
|
|
186
|
+
missing_kvs[key] = server_e_tag
|
|
187
|
+
elif device_e_tag != server_e_tag:
|
|
188
|
+
changed_kvs[key] = device_e_tag
|
|
189
|
+
additional_kvs = current_kvs
|
|
190
|
+
return KVSyncResponse(additions=additional_kvs, changes=changed_kvs, missing=missing_kvs)
|
|
195
191
|
|
|
196
192
|
def _log_sync_rpc_response(self, rpc_entry: RPCClient.RPCEntry) -> bool:
|
|
197
193
|
"""
|
|
@@ -329,25 +325,55 @@ class KVStoreHandler:
|
|
|
329
325
|
|
|
330
326
|
|
|
331
327
|
class KVStoreWithChangedNotification(KVStore):
|
|
332
|
-
def _get_comparison_values(self,
|
|
328
|
+
def _get_comparison_values(self, kv_entries: List[KVEntry]) -> Dict[str, str]:
|
|
333
329
|
"""
|
|
334
330
|
make sure your db model has a meaningful `comparison_value` method
|
|
335
331
|
"""
|
|
336
|
-
|
|
332
|
+
return {kv.key: kv.comparison_value() for kv in kv_entries}
|
|
337
333
|
|
|
338
|
-
def _find_changed_kvs(self,
|
|
334
|
+
def _find_changed_kvs(self, new_values: Dict[str, str], old_values: Dict[str, str]) -> List[str]:
|
|
339
335
|
changed = []
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
changed.append(key)
|
|
336
|
+
for key, new_value in new_values.items():
|
|
337
|
+
old_value = old_values.pop(key, None)
|
|
338
|
+
if old_value is None or old_value != new_value:
|
|
339
|
+
changed.append(key)
|
|
340
|
+
changed += list(old_values.keys())
|
|
346
341
|
return changed
|
|
347
342
|
|
|
348
|
-
def _send_changed_notification(self,
|
|
343
|
+
def _send_changed_notification(self, new_values: Dict[str, str], old_values: Dict[str, str]): # pragma: no cover
|
|
349
344
|
"""
|
|
350
345
|
consider using self._find_changed_kvs when implementing this
|
|
351
346
|
also make sure your db model has a meaningful `comparison_value` method
|
|
352
347
|
"""
|
|
353
348
|
raise NotImplementedError
|
|
349
|
+
|
|
350
|
+
def kvs_set(self, request: KVSetRequest, _) -> KVSetResponse:
|
|
351
|
+
"""
|
|
352
|
+
This method is called by the server to set values in the database.
|
|
353
|
+
It must never be called from the device.
|
|
354
|
+
Database entries must only be created/edited/deleted by this method.
|
|
355
|
+
Entries are never changed, only deleted and recreated with new values and e_tag.
|
|
356
|
+
The e_tag of the current value of a key needs to match previous_e_tag in the request, otherwise the key is
|
|
357
|
+
rejected.
|
|
358
|
+
"""
|
|
359
|
+
if not request.kvs:
|
|
360
|
+
return KVSetResponse()
|
|
361
|
+
with self.db() as session:
|
|
362
|
+
full_current_kvs: List[KVEntry] = session.query(self.database_table). \
|
|
363
|
+
filter(self.database_table.key.in_([kv.key for kv in request.kvs]))
|
|
364
|
+
comparison_values = self._get_comparison_values(full_current_kvs)
|
|
365
|
+
current_kvs = {kv.key: kv.e_tag for kv in full_current_kvs}
|
|
366
|
+
self._kvs_check_etag_mismatched(request, current_kvs) # raises Exception on mismatch
|
|
367
|
+
to_set = self._kvs_set_parse_kvs(request.kvs)
|
|
368
|
+
try:
|
|
369
|
+
if current_kvs:
|
|
370
|
+
delete_query(session, session.query(self.database_table).filter(
|
|
371
|
+
self.database_table.key.in_(list(current_kvs.keys()))))
|
|
372
|
+
if to_set:
|
|
373
|
+
session.add_all(to_set)
|
|
374
|
+
session.commit()
|
|
375
|
+
self._send_changed_notification(self._get_comparison_values(to_set), comparison_values)
|
|
376
|
+
except Exception as e:
|
|
377
|
+
session.rollback()
|
|
378
|
+
raise e
|
|
379
|
+
return KVSetResponse()
|