port-ocean 0.20.3__tar.gz → 0.21.0__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.
Potentially problematic release.
This version of port-ocean might be problematic. Click here for more details.
- {port_ocean-0.20.3 → port_ocean-0.21.0}/PKG-INFO +1 -1
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/hooks/post_gen_project.py +1 -1
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/context/event.py +7 -2
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/context/ocean.py +37 -1
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/webhook/abstract_webhook_processor.py +2 -2
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/webhook/processor_manager.py +16 -12
- port_ocean-0.21.0/port_ocean/core/integrations/mixins/events.py +71 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/integrations/mixins/sync_raw.py +14 -1
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/ocean_types.py +5 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/core/handlers/mixins/test_sync_raw.py +174 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/core/handlers/webhook/test_abstract_webhook_processor.py +2 -2
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/core/handlers/webhook/test_processor_manager.py +174 -36
- {port_ocean-0.20.3 → port_ocean-0.21.0}/pyproject.toml +1 -1
- port_ocean-0.20.3/port_ocean/core/integrations/mixins/events.py +0 -67
- {port_ocean-0.20.3 → port_ocean-0.21.0}/LICENSE.md +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/README.md +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/integrations/_infra/Dockerfile.Deb +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/integrations/_infra/Dockerfile.alpine +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/integrations/_infra/Dockerfile.base.builder +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/integrations/_infra/Dockerfile.base.runner +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/integrations/_infra/Dockerfile.dockerignore +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/integrations/_infra/Makefile +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/integrations/_infra/grpcio.sh +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/integrations/_infra/init.sh +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/bootstrap.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cli.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/commands/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/commands/defaults/__init___.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/commands/defaults/clean.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/commands/defaults/dock.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/commands/defaults/group.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/commands/list_integrations.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/commands/main.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/commands/new.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/commands/pull.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/commands/sail.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/commands/version.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/cookiecutter.json +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/extensions.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.env.example +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.gitignore +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/.gitignore +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/blueprints.json +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/port-app-config.yml +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CHANGELOG.md +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CONTRIBUTING.md +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/README.md +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/changelog/.gitignore +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/debug.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/main.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/poetry.toml +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/pyproject.toml +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/sonar-project.properties +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/test_sample.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/utils.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/clients/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/clients/auth/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/clients/auth/auth_client.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/clients/auth/oauth_client.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/clients/port/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/clients/port/authentication.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/clients/port/client.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/clients/port/mixins/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/clients/port/mixins/blueprints.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/clients/port/mixins/entities.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/clients/port/mixins/integrations.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/clients/port/mixins/migrations.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/clients/port/mixins/organization.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/clients/port/retry_transport.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/clients/port/types.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/clients/port/utils.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/config/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/config/base.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/config/dynamic.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/config/settings.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/consumers/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/consumers/kafka_consumer.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/context/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/context/resource.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/defaults/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/defaults/clean.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/defaults/common.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/defaults/initialize.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/event_listener/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/event_listener/base.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/event_listener/factory.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/event_listener/http.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/event_listener/kafka.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/event_listener/once.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/event_listener/polling.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/event_listener/webhooks_only.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/base.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/entities_state_applier/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/entities_state_applier/base.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/entities_state_applier/port/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/entities_state_applier/port/applier.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/entities_state_applier/port/get_related_entities.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/entity_processor/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/entity_processor/base.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/entity_processor/jq_entity_processor.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/port_app_config/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/port_app_config/api.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/port_app_config/base.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/port_app_config/models.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/queue/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/queue/abstract_queue.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/queue/local_queue.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/resync_state_updater/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/resync_state_updater/updater.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/webhook/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/webhook/webhook_event.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/integrations/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/integrations/base.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/integrations/mixins/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/integrations/mixins/handler.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/integrations/mixins/live_events.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/integrations/mixins/sync.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/integrations/mixins/utils.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/models.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/utils/entity_topological_sorter.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/utils/utils.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/debug_cli.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/exceptions/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/exceptions/api.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/exceptions/base.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/exceptions/clients.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/exceptions/context.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/exceptions/core.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/exceptions/port_defaults.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/exceptions/utils.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/exceptions/webhook_processor.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/helpers/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/helpers/async_client.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/helpers/retry.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/log/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/log/handlers.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/log/logger_setup.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/log/sensetive.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/middlewares.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/ocean.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/py.typed +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/run.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/sonar-project.properties +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/clients/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/clients/oauth/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/clients/oauth/test_oauth_client.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/clients/port/mixins/test_entities.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/clients/port/mixins/test_organization_mixin.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/conftest.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/core/defaults/test_common.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/core/handlers/entities_state_applier/test_applier.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/core/handlers/mixins/test_live_events.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/core/handlers/port_app_config/test_api.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/core/handlers/port_app_config/test_base.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/core/handlers/queue/test_local_queue.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/core/handlers/webhook/test_webhook_event.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/core/test_utils.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/core/utils/test_entity_topological_sorter.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/core/utils/test_resolve_entities_diff.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/helpers/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/helpers/fake_port_api.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/helpers/fixtures.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/helpers/integration.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/helpers/ocean_app.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/helpers/port_client.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/helpers/smoke_test.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/log/test_handlers.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/test_ocean.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/test_smoke.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/utils/test_async_iterators.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/utils/test_cache.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/utils/__init__.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/utils/async_http.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/utils/async_iterators.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/utils/cache.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/utils/misc.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/utils/queue_utils.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/utils/repeat.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/utils/signal.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/utils/time.py +0 -0
- {port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/version.py +0 -0
{port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/cli/cookiecutter/hooks/post_gen_project.py
RENAMED
|
@@ -8,7 +8,7 @@ def handle_private_integration_flags():
|
|
|
8
8
|
)
|
|
9
9
|
root_dir = os.path.join("{{ cookiecutter._repo_dir }}", "../../../")
|
|
10
10
|
infra_make_file = os.path.join(root_dir, "integrations/_infra/Makefile")
|
|
11
|
-
infra_dockerfile = os.path.join(root_dir, "integrations/_infra/Dockerfile.
|
|
11
|
+
infra_dockerfile = os.path.join(root_dir, "integrations/_infra/Dockerfile.Deb")
|
|
12
12
|
infra_dockerignore = os.path.join(
|
|
13
13
|
root_dir, "integrations/_infra/Dockerfile.dockerignore"
|
|
14
14
|
)
|
|
@@ -182,9 +182,14 @@ async def event_context(
|
|
|
182
182
|
f"Skipping resync due to empty mapping: {str(e)}", exc_info=True
|
|
183
183
|
)
|
|
184
184
|
raise
|
|
185
|
-
except
|
|
185
|
+
except BaseException as e:
|
|
186
186
|
success = False
|
|
187
|
-
|
|
187
|
+
if isinstance(e, KeyboardInterrupt):
|
|
188
|
+
logger.warning("Operation interrupted by user", exc_info=True)
|
|
189
|
+
elif isinstance(e, asyncio.CancelledError):
|
|
190
|
+
logger.warning("Operation was cancelled", exc_info=True)
|
|
191
|
+
else:
|
|
192
|
+
logger.error(f"Event failed with error: {repr(e)}", exc_info=True)
|
|
188
193
|
raise
|
|
189
194
|
else:
|
|
190
195
|
success = True
|
|
@@ -12,6 +12,8 @@ from port_ocean.core.ocean_types import (
|
|
|
12
12
|
START_EVENT_LISTENER,
|
|
13
13
|
RawEntityDiff,
|
|
14
14
|
EntityDiff,
|
|
15
|
+
BEFORE_RESYNC_EVENT_LISTENER,
|
|
16
|
+
AFTER_RESYNC_EVENT_LISTENER,
|
|
15
17
|
)
|
|
16
18
|
from port_ocean.exceptions.context import (
|
|
17
19
|
PortOceanContextNotFoundError,
|
|
@@ -80,7 +82,7 @@ class PortOceanContext:
|
|
|
80
82
|
) -> RESYNC_EVENT_LISTENER | None:
|
|
81
83
|
if not self.app.config.event_listener.should_resync:
|
|
82
84
|
logger.debug(
|
|
83
|
-
"
|
|
85
|
+
f"Using event listener {self.app.config.event_listener.type}, which shouldn't perform any resyncs. Skipping resyncs setup..."
|
|
84
86
|
)
|
|
85
87
|
return None
|
|
86
88
|
return self.integration.on_resync(function, kind)
|
|
@@ -93,6 +95,40 @@ class PortOceanContext:
|
|
|
93
95
|
|
|
94
96
|
return wrapper
|
|
95
97
|
|
|
98
|
+
def on_resync_start(
|
|
99
|
+
self,
|
|
100
|
+
) -> Callable[
|
|
101
|
+
[BEFORE_RESYNC_EVENT_LISTENER | None], BEFORE_RESYNC_EVENT_LISTENER | None
|
|
102
|
+
]:
|
|
103
|
+
def wrapper(
|
|
104
|
+
function: BEFORE_RESYNC_EVENT_LISTENER | None,
|
|
105
|
+
) -> BEFORE_RESYNC_EVENT_LISTENER | None:
|
|
106
|
+
if not self.app.config.event_listener.should_resync:
|
|
107
|
+
logger.debug(
|
|
108
|
+
f"Using event listener {self.app.config.event_listener.type}, which shouldn't perform any resyncs. Skipping resyncs setup..."
|
|
109
|
+
)
|
|
110
|
+
return None
|
|
111
|
+
return self.integration.on_resync_start(function)
|
|
112
|
+
|
|
113
|
+
return wrapper
|
|
114
|
+
|
|
115
|
+
def on_resync_complete(
|
|
116
|
+
self,
|
|
117
|
+
) -> Callable[
|
|
118
|
+
[AFTER_RESYNC_EVENT_LISTENER | None], AFTER_RESYNC_EVENT_LISTENER | None
|
|
119
|
+
]:
|
|
120
|
+
def wrapper(
|
|
121
|
+
function: AFTER_RESYNC_EVENT_LISTENER | None,
|
|
122
|
+
) -> AFTER_RESYNC_EVENT_LISTENER | None:
|
|
123
|
+
if not self.app.config.event_listener.should_resync:
|
|
124
|
+
logger.debug(
|
|
125
|
+
f"Using event listener {self.app.config.event_listener.type}, which shouldn't perform any resyncs. Skipping resyncs setup..."
|
|
126
|
+
)
|
|
127
|
+
return None
|
|
128
|
+
return self.integration.on_resync_complete(function)
|
|
129
|
+
|
|
130
|
+
return wrapper
|
|
131
|
+
|
|
96
132
|
async def update_raw_diff(
|
|
97
133
|
self,
|
|
98
134
|
kind: str,
|
|
@@ -109,9 +109,9 @@ class AbstractWebhookProcessor(ABC):
|
|
|
109
109
|
pass
|
|
110
110
|
|
|
111
111
|
@abstractmethod
|
|
112
|
-
def should_process_event(self, event: WebhookEvent) -> bool:
|
|
112
|
+
async def should_process_event(self, event: WebhookEvent) -> bool:
|
|
113
113
|
pass
|
|
114
114
|
|
|
115
115
|
@abstractmethod
|
|
116
|
-
def get_matching_kinds(self, event: WebhookEvent) -> list[str]:
|
|
116
|
+
async def get_matching_kinds(self, event: WebhookEvent) -> list[str]:
|
|
117
117
|
pass
|
{port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/core/handlers/webhook/processor_manager.py
RENAMED
|
@@ -47,7 +47,7 @@ class LiveEventsProcessorManager(LiveEventsMixin, EventsMixin):
|
|
|
47
47
|
except Exception as e:
|
|
48
48
|
logger.exception(f"Error starting queue processor for {path}: {str(e)}")
|
|
49
49
|
|
|
50
|
-
def _extract_matching_processors(
|
|
50
|
+
async def _extract_matching_processors(
|
|
51
51
|
self, webhook_event: WebhookEvent, path: str
|
|
52
52
|
) -> list[tuple[ResourceConfig, AbstractWebhookProcessor]]:
|
|
53
53
|
"""Find and extract the matching processor for an event"""
|
|
@@ -56,8 +56,8 @@ class LiveEventsProcessorManager(LiveEventsMixin, EventsMixin):
|
|
|
56
56
|
|
|
57
57
|
for processor_class in self._processors_classes[path]:
|
|
58
58
|
processor = processor_class(webhook_event.clone())
|
|
59
|
-
if processor.should_process_event(webhook_event):
|
|
60
|
-
kinds = processor.get_matching_kinds(webhook_event)
|
|
59
|
+
if await processor.should_process_event(webhook_event):
|
|
60
|
+
kinds = await processor.get_matching_kinds(webhook_event)
|
|
61
61
|
for kind in kinds:
|
|
62
62
|
for resource in event.port_app_config.resources:
|
|
63
63
|
if resource.kind == kind:
|
|
@@ -92,26 +92,30 @@ class LiveEventsProcessorManager(LiveEventsMixin, EventsMixin):
|
|
|
92
92
|
parent_override=webhook_event.event_context,
|
|
93
93
|
):
|
|
94
94
|
matching_processors_with_resource = (
|
|
95
|
-
self._extract_matching_processors(webhook_event, path)
|
|
95
|
+
await self._extract_matching_processors(webhook_event, path)
|
|
96
96
|
)
|
|
97
97
|
webhook_event_raw_results_for_all_resources = await asyncio.gather(
|
|
98
98
|
*(
|
|
99
99
|
self._process_single_event(processor, path, resource)
|
|
100
100
|
for resource, processor in matching_processors_with_resource
|
|
101
|
-
)
|
|
101
|
+
),
|
|
102
|
+
return_exceptions=True,
|
|
102
103
|
)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
|
|
105
|
+
successful_raw_results: list[WebhookEventRawResults] = [
|
|
106
|
+
result
|
|
107
|
+
for result in webhook_event_raw_results_for_all_resources
|
|
108
|
+
if isinstance(result, WebhookEventRawResults)
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
if successful_raw_results:
|
|
106
112
|
logger.info(
|
|
107
113
|
"Exporting raw event results to entities",
|
|
108
114
|
webhook_event_raw_results_for_all_resources_length=len(
|
|
109
|
-
|
|
115
|
+
successful_raw_results
|
|
110
116
|
),
|
|
111
117
|
)
|
|
112
|
-
await self.sync_raw_results(
|
|
113
|
-
webhook_event_raw_results_for_all_resources
|
|
114
|
-
)
|
|
118
|
+
await self.sync_raw_results(successful_raw_results)
|
|
115
119
|
except asyncio.CancelledError:
|
|
116
120
|
logger.info(f"Queue processor for {path} is shutting down")
|
|
117
121
|
for _, processor in matching_processors_with_resource:
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from loguru import logger
|
|
5
|
+
|
|
6
|
+
from port_ocean.core.ocean_types import (
|
|
7
|
+
IntegrationEventsCallbacks,
|
|
8
|
+
START_EVENT_LISTENER,
|
|
9
|
+
RESYNC_EVENT_LISTENER,
|
|
10
|
+
BEFORE_RESYNC_EVENT_LISTENER,
|
|
11
|
+
AFTER_RESYNC_EVENT_LISTENER,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EventsMixin:
|
|
16
|
+
"""A mixin class that provides event handling capabilities for the integration class.
|
|
17
|
+
|
|
18
|
+
This mixin allows classes to register event listeners and manage event callbacks.
|
|
19
|
+
It provides methods for attaching listeners to various lifecycle events.
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
event_strategy: A dictionary storing event callbacks for different event types.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self) -> None:
|
|
26
|
+
self.event_strategy: IntegrationEventsCallbacks = {
|
|
27
|
+
"start": [],
|
|
28
|
+
"resync": defaultdict(list),
|
|
29
|
+
"resync_start": [],
|
|
30
|
+
"resync_complete": [],
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def available_resync_kinds(self) -> list[str]:
|
|
35
|
+
return list(self.event_strategy["resync"].keys())
|
|
36
|
+
|
|
37
|
+
def on_start(self, function: START_EVENT_LISTENER) -> START_EVENT_LISTENER:
|
|
38
|
+
"""Register a function as a listener for the "start" event."""
|
|
39
|
+
logger.debug(f"Registering {function} as a start event listener")
|
|
40
|
+
self.event_strategy["start"].append(function)
|
|
41
|
+
return function
|
|
42
|
+
|
|
43
|
+
def on_resync(
|
|
44
|
+
self, function: RESYNC_EVENT_LISTENER | None, kind: str | None = None
|
|
45
|
+
) -> RESYNC_EVENT_LISTENER | None:
|
|
46
|
+
"""Register a function as a listener for a "resync" event."""
|
|
47
|
+
if function is not None:
|
|
48
|
+
if kind is None:
|
|
49
|
+
logger.debug("Registering resync event listener any kind")
|
|
50
|
+
else:
|
|
51
|
+
logger.info(f"Registering resync event listener for kind {kind}")
|
|
52
|
+
self.event_strategy["resync"][kind].append(function)
|
|
53
|
+
return function
|
|
54
|
+
|
|
55
|
+
def on_resync_start(
|
|
56
|
+
self, function: BEFORE_RESYNC_EVENT_LISTENER | None
|
|
57
|
+
) -> BEFORE_RESYNC_EVENT_LISTENER | None:
|
|
58
|
+
"""Register a function to be called when a resync operation starts."""
|
|
59
|
+
if function is not None:
|
|
60
|
+
logger.debug(f"Registering {function} as a resync_start event listener")
|
|
61
|
+
self.event_strategy["resync_start"].append(function)
|
|
62
|
+
return function
|
|
63
|
+
|
|
64
|
+
def on_resync_complete(
|
|
65
|
+
self, function: AFTER_RESYNC_EVENT_LISTENER | None
|
|
66
|
+
) -> AFTER_RESYNC_EVENT_LISTENER | None:
|
|
67
|
+
"""Register a function to be called when a resync operation completes."""
|
|
68
|
+
if function is not None:
|
|
69
|
+
logger.debug(f"Registering {function} as a resync_complete event listener")
|
|
70
|
+
self.event_strategy["resync_complete"].append(function)
|
|
71
|
+
return function
|
|
@@ -550,6 +550,10 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
550
550
|
)
|
|
551
551
|
logger.info(f"Resync will use the following mappings: {app_config.dict()}")
|
|
552
552
|
|
|
553
|
+
# Execute resync_start hooks
|
|
554
|
+
for resync_start_fn in self.event_strategy["resync_start"]:
|
|
555
|
+
await resync_start_fn()
|
|
556
|
+
|
|
553
557
|
try:
|
|
554
558
|
did_fetched_current_state = True
|
|
555
559
|
except httpx.HTTPError as e:
|
|
@@ -598,7 +602,7 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
598
602
|
if errors:
|
|
599
603
|
message = f"Resync failed with {len(errors)}. Skipping delete phase due to incomplete state"
|
|
600
604
|
error_group = ExceptionGroup(
|
|
601
|
-
|
|
605
|
+
message,
|
|
602
606
|
errors,
|
|
603
607
|
)
|
|
604
608
|
if not silent:
|
|
@@ -618,3 +622,12 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
618
622
|
)
|
|
619
623
|
|
|
620
624
|
logger.info("Resync finished successfully")
|
|
625
|
+
|
|
626
|
+
# Execute resync_complete hooks
|
|
627
|
+
if "resync_complete" in self.event_strategy:
|
|
628
|
+
logger.info("Executing resync_complete hooks")
|
|
629
|
+
|
|
630
|
+
for resync_complete_fn in self.event_strategy["resync_complete"]:
|
|
631
|
+
await resync_complete_fn()
|
|
632
|
+
|
|
633
|
+
logger.info("Finished executing resync_complete hooks")
|
|
@@ -19,6 +19,9 @@ LISTENER_RESULT = Awaitable[RAW_RESULT] | ASYNC_GENERATOR_RESYNC_TYPE
|
|
|
19
19
|
RESYNC_EVENT_LISTENER = Callable[[str], LISTENER_RESULT]
|
|
20
20
|
START_EVENT_LISTENER = Callable[[], Awaitable[None]]
|
|
21
21
|
|
|
22
|
+
BEFORE_RESYNC_EVENT_LISTENER = Callable[[], Awaitable[None]]
|
|
23
|
+
AFTER_RESYNC_EVENT_LISTENER = Callable[[], Awaitable[None]]
|
|
24
|
+
|
|
22
25
|
|
|
23
26
|
class RawEntityDiff(TypedDict):
|
|
24
27
|
before: list[RAW_ITEM]
|
|
@@ -44,3 +47,5 @@ class CalculationResult(NamedTuple):
|
|
|
44
47
|
class IntegrationEventsCallbacks(TypedDict):
|
|
45
48
|
start: list[START_EVENT_LISTENER]
|
|
46
49
|
resync: dict[str | None, list[RESYNC_EVENT_LISTENER]]
|
|
50
|
+
resync_start: list[BEFORE_RESYNC_EVENT_LISTENER]
|
|
51
|
+
resync_complete: list[AFTER_RESYNC_EVENT_LISTENER]
|
{port_ocean-0.20.3 → port_ocean-0.21.0}/port_ocean/tests/core/handlers/mixins/test_sync_raw.py
RENAMED
|
@@ -799,3 +799,177 @@ async def test_register_resource_raw_skip_event_type_http_request_upsert_called_
|
|
|
799
799
|
mock_sync_raw_mixin._calculate_raw.assert_called_once()
|
|
800
800
|
mock_sync_raw_mixin._map_entities_compared_with_port.assert_not_called()
|
|
801
801
|
mock_sync_raw_mixin.entities_state_applier.upsert.assert_called_once()
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
@pytest.mark.asyncio
|
|
805
|
+
async def test_on_resync_start_hooks_are_called(
|
|
806
|
+
mock_sync_raw_mixin: SyncRawMixin,
|
|
807
|
+
mock_port_app_config: PortAppConfig,
|
|
808
|
+
) -> None:
|
|
809
|
+
# Setup
|
|
810
|
+
resync_start_called = False
|
|
811
|
+
|
|
812
|
+
async def on_resync_start() -> None:
|
|
813
|
+
nonlocal resync_start_called
|
|
814
|
+
resync_start_called = True
|
|
815
|
+
|
|
816
|
+
mock_sync_raw_mixin.on_resync_start(on_resync_start)
|
|
817
|
+
|
|
818
|
+
# Execute
|
|
819
|
+
async with event_context(EventType.RESYNC, trigger_type="machine") as event:
|
|
820
|
+
event.port_app_config = mock_port_app_config
|
|
821
|
+
await mock_sync_raw_mixin.sync_raw_all(
|
|
822
|
+
trigger_type="machine",
|
|
823
|
+
user_agent_type=UserAgentType.exporter,
|
|
824
|
+
)
|
|
825
|
+
|
|
826
|
+
# Verify
|
|
827
|
+
assert resync_start_called, "on_resync_start hook was not called"
|
|
828
|
+
|
|
829
|
+
|
|
830
|
+
@pytest.mark.asyncio
|
|
831
|
+
async def test_on_resync_complete_hooks_are_called_on_success(
|
|
832
|
+
mock_sync_raw_mixin: SyncRawMixin,
|
|
833
|
+
mock_port_app_config: PortAppConfig,
|
|
834
|
+
mock_ocean: Ocean,
|
|
835
|
+
) -> None:
|
|
836
|
+
# Setup
|
|
837
|
+
resync_complete_called = False
|
|
838
|
+
|
|
839
|
+
async def on_resync_complete() -> None:
|
|
840
|
+
nonlocal resync_complete_called
|
|
841
|
+
resync_complete_called = True
|
|
842
|
+
|
|
843
|
+
mock_sync_raw_mixin.on_resync_complete(on_resync_complete)
|
|
844
|
+
mock_ocean.port_client.search_entities.return_value = [] # type: ignore
|
|
845
|
+
|
|
846
|
+
# Execute
|
|
847
|
+
async with event_context(EventType.RESYNC, trigger_type="machine") as event:
|
|
848
|
+
event.port_app_config = mock_port_app_config
|
|
849
|
+
await mock_sync_raw_mixin.sync_raw_all(
|
|
850
|
+
trigger_type="machine",
|
|
851
|
+
user_agent_type=UserAgentType.exporter,
|
|
852
|
+
)
|
|
853
|
+
|
|
854
|
+
# Verify
|
|
855
|
+
assert resync_complete_called, "on_resync_complete hook was not called"
|
|
856
|
+
|
|
857
|
+
|
|
858
|
+
@pytest.mark.asyncio
|
|
859
|
+
async def test_on_resync_complete_hooks_not_called_on_error(
|
|
860
|
+
mock_sync_raw_mixin: SyncRawMixin,
|
|
861
|
+
mock_port_app_config: PortAppConfig,
|
|
862
|
+
) -> None:
|
|
863
|
+
# Setup
|
|
864
|
+
resync_complete_called = False
|
|
865
|
+
|
|
866
|
+
async def on_resync_complete() -> None:
|
|
867
|
+
nonlocal resync_complete_called
|
|
868
|
+
resync_complete_called = True
|
|
869
|
+
|
|
870
|
+
mock_sync_raw_mixin.on_resync_complete(on_resync_complete)
|
|
871
|
+
mock_sync_raw_mixin._get_resource_raw_results.side_effect = Exception("Test error") # type: ignore
|
|
872
|
+
|
|
873
|
+
# Execute
|
|
874
|
+
async with event_context(EventType.RESYNC, trigger_type="machine") as event:
|
|
875
|
+
event.port_app_config = mock_port_app_config
|
|
876
|
+
with pytest.raises(Exception):
|
|
877
|
+
await mock_sync_raw_mixin.sync_raw_all(
|
|
878
|
+
trigger_type="machine",
|
|
879
|
+
user_agent_type=UserAgentType.exporter,
|
|
880
|
+
)
|
|
881
|
+
|
|
882
|
+
# Verify
|
|
883
|
+
assert (
|
|
884
|
+
not resync_complete_called
|
|
885
|
+
), "on_resync_complete hook should not have been called on error"
|
|
886
|
+
|
|
887
|
+
|
|
888
|
+
@pytest.mark.asyncio
|
|
889
|
+
async def test_multiple_on_resync_start_on_resync_complete_hooks_called_in_order(
|
|
890
|
+
mock_sync_raw_mixin: SyncRawMixin,
|
|
891
|
+
mock_port_app_config: PortAppConfig,
|
|
892
|
+
mock_ocean: Ocean,
|
|
893
|
+
) -> None:
|
|
894
|
+
# Setup
|
|
895
|
+
call_order: list[str] = []
|
|
896
|
+
|
|
897
|
+
async def on_resync_start1() -> None:
|
|
898
|
+
call_order.append("on_resync_start1")
|
|
899
|
+
|
|
900
|
+
async def on_resync_start2() -> None:
|
|
901
|
+
call_order.append("on_resync_start2")
|
|
902
|
+
|
|
903
|
+
async def on_resync_complete1() -> None:
|
|
904
|
+
call_order.append("on_resync_complete1")
|
|
905
|
+
|
|
906
|
+
async def on_resync_complete2() -> None:
|
|
907
|
+
call_order.append("on_resync_complete2")
|
|
908
|
+
|
|
909
|
+
mock_sync_raw_mixin.on_resync_start(on_resync_start1)
|
|
910
|
+
mock_sync_raw_mixin.on_resync_start(on_resync_start2)
|
|
911
|
+
mock_sync_raw_mixin.on_resync_complete(on_resync_complete1)
|
|
912
|
+
mock_sync_raw_mixin.on_resync_complete(on_resync_complete2)
|
|
913
|
+
mock_ocean.port_client.search_entities.return_value = [] # type: ignore
|
|
914
|
+
|
|
915
|
+
# Execute
|
|
916
|
+
async with event_context(EventType.RESYNC, trigger_type="machine") as event:
|
|
917
|
+
event.port_app_config = mock_port_app_config
|
|
918
|
+
await mock_sync_raw_mixin.sync_raw_all(
|
|
919
|
+
trigger_type="machine",
|
|
920
|
+
user_agent_type=UserAgentType.exporter,
|
|
921
|
+
)
|
|
922
|
+
|
|
923
|
+
# Verify
|
|
924
|
+
assert call_order == [
|
|
925
|
+
"on_resync_start1",
|
|
926
|
+
"on_resync_start2",
|
|
927
|
+
"on_resync_complete1",
|
|
928
|
+
"on_resync_complete2",
|
|
929
|
+
], "Hooks were not called in the correct order"
|
|
930
|
+
|
|
931
|
+
|
|
932
|
+
@pytest.mark.asyncio
|
|
933
|
+
async def test_on_resync_start_hook_error_prevents_resync(
|
|
934
|
+
mock_sync_raw_mixin: SyncRawMixin,
|
|
935
|
+
mock_port_app_config: PortAppConfig,
|
|
936
|
+
) -> None:
|
|
937
|
+
# Setup
|
|
938
|
+
resync_complete_called = False
|
|
939
|
+
resync_proceeded = False
|
|
940
|
+
|
|
941
|
+
async def on_resync_start() -> None:
|
|
942
|
+
raise Exception("Before resync error")
|
|
943
|
+
|
|
944
|
+
async def on_resync_complete() -> None:
|
|
945
|
+
nonlocal resync_complete_called
|
|
946
|
+
resync_complete_called = True
|
|
947
|
+
|
|
948
|
+
mock_sync_raw_mixin.on_resync_start(on_resync_start)
|
|
949
|
+
mock_sync_raw_mixin.on_resync_complete(on_resync_complete)
|
|
950
|
+
|
|
951
|
+
original_get_resource_raw_results = mock_sync_raw_mixin._get_resource_raw_results
|
|
952
|
+
|
|
953
|
+
async def track_resync(*args: Any, **kwargs: Any) -> Any:
|
|
954
|
+
nonlocal resync_proceeded
|
|
955
|
+
resync_proceeded = True
|
|
956
|
+
return await original_get_resource_raw_results(*args, **kwargs)
|
|
957
|
+
|
|
958
|
+
mock_sync_raw_mixin._get_resource_raw_results = track_resync # type: ignore
|
|
959
|
+
|
|
960
|
+
# Execute
|
|
961
|
+
async with event_context(EventType.RESYNC, trigger_type="machine") as event:
|
|
962
|
+
event.port_app_config = mock_port_app_config
|
|
963
|
+
with pytest.raises(Exception, match="Before resync error"):
|
|
964
|
+
await mock_sync_raw_mixin.sync_raw_all(
|
|
965
|
+
trigger_type="machine",
|
|
966
|
+
user_agent_type=UserAgentType.exporter,
|
|
967
|
+
)
|
|
968
|
+
|
|
969
|
+
# Verify
|
|
970
|
+
assert (
|
|
971
|
+
not resync_proceeded
|
|
972
|
+
), "Resync should not have proceeded after before_resync hook error"
|
|
973
|
+
assert (
|
|
974
|
+
not resync_complete_called
|
|
975
|
+
), "on_resync_complete hook should not have been called after error"
|
|
@@ -32,7 +32,7 @@ class ConcreteWebhookProcessor(AbstractWebhookProcessor):
|
|
|
32
32
|
) -> WebhookEventRawResults:
|
|
33
33
|
return WebhookEventRawResults(updated_raw_results=[{}], deleted_raw_results=[])
|
|
34
34
|
|
|
35
|
-
def should_process_event(self, webhook_event: WebhookEvent) -> bool:
|
|
35
|
+
async def should_process_event(self, webhook_event: WebhookEvent) -> bool:
|
|
36
36
|
return True
|
|
37
37
|
|
|
38
38
|
async def before_processing(self) -> None:
|
|
@@ -47,7 +47,7 @@ class ConcreteWebhookProcessor(AbstractWebhookProcessor):
|
|
|
47
47
|
await super().cancel()
|
|
48
48
|
self.cancel_called = True
|
|
49
49
|
|
|
50
|
-
def get_matching_kinds(self, event: WebhookEvent) -> list[str]:
|
|
50
|
+
async def get_matching_kinds(self, event: WebhookEvent) -> list[str]:
|
|
51
51
|
return ["test"]
|
|
52
52
|
|
|
53
53
|
|