port-ocean 0.23.3__tar.gz → 0.23.5__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.23.3 → port_ocean-0.23.5}/PKG-INFO +1 -1
- {port_ocean-0.23.3 → port_ocean-0.23.5}/integrations/_infra/Dockerfile.Deb +6 -6
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/clients/port/mixins/integrations.py +3 -2
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/config/dynamic.py +1 -1
- port_ocean-0.23.5/port_ocean/context/metric_resource.py +63 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/resync_state_updater/updater.py +3 -3
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/integrations/mixins/sync_raw.py +108 -66
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/exceptions/core.py +4 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/helpers/metric/metric.py +35 -11
- port_ocean-0.23.5/port_ocean/helpers/metric/utils.py +52 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/core/handlers/mixins/test_sync_raw.py +29 -5
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/log/test_handlers.py +2 -1
- {port_ocean-0.23.3 → port_ocean-0.23.5}/pyproject.toml +1 -1
- port_ocean-0.23.3/port_ocean/helpers/metric/utils.py +0 -28
- {port_ocean-0.23.3 → port_ocean-0.23.5}/LICENSE.md +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/README.md +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/integrations/_infra/Dockerfile.alpine +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/integrations/_infra/Dockerfile.base.builder +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/integrations/_infra/Dockerfile.base.runner +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/integrations/_infra/Dockerfile.dockerignore +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/integrations/_infra/Dockerfile.local +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/integrations/_infra/Makefile +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/integrations/_infra/entry_local.sh +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/integrations/_infra/grpcio.sh +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/integrations/_infra/init.sh +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/bootstrap.py +0 -0
- {port_ocean-0.23.3/port_ocean/tests/helpers → port_ocean-0.23.5/port_ocean/cache}/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cache/base.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cache/disk.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cache/errors.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cache/memory.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cli.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/commands/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/commands/defaults/__init___.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/commands/defaults/clean.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/commands/defaults/dock.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/commands/defaults/group.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/commands/list_integrations.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/commands/main.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/commands/new.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/commands/pull.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/commands/sail.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/commands/version.py +0 -0
- {port_ocean-0.23.3/port_ocean/tests/clients/oauth → port_ocean-0.23.5/port_ocean/cli/cookiecutter}/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/cookiecutter.json +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/extensions.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/hooks/post_gen_project.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.env.example +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.gitignore +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/.gitignore +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/blueprints.json +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/port-app-config.yml +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CHANGELOG.md +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CONTRIBUTING.md +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/README.md +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/changelog/.gitignore +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/debug.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/main.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/poetry.toml +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/pyproject.toml +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/sonar-project.properties +0 -0
- {port_ocean-0.23.3/port_ocean/tests/clients → port_ocean-0.23.5/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests}/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/test_sample.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/cli/utils.py +0 -0
- {port_ocean-0.23.3/port_ocean/tests → port_ocean-0.23.5/port_ocean/clients}/__init__.py +0 -0
- {port_ocean-0.23.3/port_ocean/log → port_ocean-0.23.5/port_ocean/clients/auth}/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/clients/auth/auth_client.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/clients/auth/oauth_client.py +0 -0
- {port_ocean-0.23.3/port_ocean/helpers → port_ocean-0.23.5/port_ocean/clients/port}/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/clients/port/authentication.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/clients/port/client.py +0 -0
- {port_ocean-0.23.3/port_ocean/exceptions → port_ocean-0.23.5/port_ocean/clients/port/mixins}/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/clients/port/mixins/blueprints.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/clients/port/mixins/entities.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/clients/port/mixins/migrations.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/clients/port/mixins/organization.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/clients/port/retry_transport.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/clients/port/types.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/clients/port/utils.py +0 -0
- {port_ocean-0.23.3/port_ocean/core/integrations → port_ocean-0.23.5/port_ocean/config}/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/config/base.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/config/settings.py +0 -0
- {port_ocean-0.23.3/port_ocean/core/handlers/webhook → port_ocean-0.23.5/port_ocean/consumers}/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/consumers/kafka_consumer.py +0 -0
- {port_ocean-0.23.3/port_ocean/core/handlers/entities_state_applier/port → port_ocean-0.23.5/port_ocean/context}/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/context/event.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/context/ocean.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/context/resource.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/defaults/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/defaults/clean.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/defaults/common.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/defaults/initialize.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/event_listener/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/event_listener/base.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/event_listener/factory.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/event_listener/http.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/event_listener/kafka.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/event_listener/once.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/event_listener/polling.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/event_listener/webhooks_only.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/base.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/entities_state_applier/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/entities_state_applier/base.py +0 -0
- {port_ocean-0.23.3/port_ocean/context → port_ocean-0.23.5/port_ocean/core/handlers/entities_state_applier/port}/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/entities_state_applier/port/applier.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/entities_state_applier/port/get_related_entities.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/entity_processor/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/entity_processor/base.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/entity_processor/jq_entity_processor.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/port_app_config/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/port_app_config/api.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/port_app_config/base.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/port_app_config/models.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/queue/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/queue/abstract_queue.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/queue/local_queue.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/resync_state_updater/__init__.py +0 -0
- {port_ocean-0.23.3/port_ocean/consumers → port_ocean-0.23.5/port_ocean/core/handlers/webhook}/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/webhook/abstract_webhook_processor.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/webhook/processor_manager.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/webhook/webhook_event.py +0 -0
- {port_ocean-0.23.3/port_ocean/config → port_ocean-0.23.5/port_ocean/core/integrations}/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/integrations/base.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/integrations/mixins/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/integrations/mixins/events.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/integrations/mixins/handler.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/integrations/mixins/live_events.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/integrations/mixins/sync.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/integrations/mixins/utils.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/models.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/ocean_types.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/utils/entity_topological_sorter.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/utils/utils.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/debug_cli.py +0 -0
- {port_ocean-0.23.3/port_ocean/clients/port/mixins → port_ocean-0.23.5/port_ocean/exceptions}/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/exceptions/api.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/exceptions/base.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/exceptions/clients.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/exceptions/context.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/exceptions/port_defaults.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/exceptions/utils.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/exceptions/webhook_processor.py +0 -0
- {port_ocean-0.23.3/port_ocean/clients/port → port_ocean-0.23.5/port_ocean/helpers}/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/helpers/async_client.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/helpers/retry.py +0 -0
- {port_ocean-0.23.3/port_ocean/clients/auth → port_ocean-0.23.5/port_ocean/log}/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/log/handlers.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/log/logger_setup.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/log/sensetive.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/middlewares.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/ocean.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/py.typed +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/run.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/sonar-project.properties +0 -0
- {port_ocean-0.23.3/port_ocean/clients → port_ocean-0.23.5/port_ocean/tests}/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/cache/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/cache/test_disk_cache.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/cache/test_memory_cache.py +0 -0
- {port_ocean-0.23.3/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests → port_ocean-0.23.5/port_ocean/tests/clients}/__init__.py +0 -0
- {port_ocean-0.23.3/port_ocean/cli/cookiecutter → port_ocean-0.23.5/port_ocean/tests/clients/oauth}/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/clients/oauth/test_oauth_client.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/clients/port/mixins/test_entities.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/clients/port/mixins/test_organization_mixin.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/conftest.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/core/conftest.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/core/defaults/test_common.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/core/handlers/entities_state_applier/test_applier.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/core/handlers/mixins/test_live_events.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/core/handlers/port_app_config/test_api.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/core/handlers/port_app_config/test_base.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/core/handlers/queue/test_local_queue.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/core/handlers/webhook/test_abstract_webhook_processor.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/core/handlers/webhook/test_processor_manager.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/core/handlers/webhook/test_webhook_event.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/core/test_utils.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/core/utils/test_entity_topological_sorter.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/core/utils/test_resolve_entities_diff.py +0 -0
- {port_ocean-0.23.3/port_ocean/cache → port_ocean-0.23.5/port_ocean/tests/helpers}/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/helpers/fake_port_api.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/helpers/fixtures.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/helpers/integration.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/helpers/ocean_app.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/helpers/port_client.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/helpers/smoke_test.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/test_metric.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/test_ocean.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/test_smoke.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/utils/test_async_iterators.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/utils/test_cache.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/utils/__init__.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/utils/async_http.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/utils/async_iterators.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/utils/cache.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/utils/ipc.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/utils/misc.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/utils/queue_utils.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/utils/repeat.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/utils/signal.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/utils/time.py +0 -0
- {port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/version.py +0 -0
|
@@ -5,15 +5,11 @@ FROM ${BASE_BUILDER_PYTHON_IMAGE} AS base
|
|
|
5
5
|
|
|
6
6
|
ARG BUILD_CONTEXT
|
|
7
7
|
ARG BUILDPLATFORM
|
|
8
|
-
ARG PROMETHEUS_MULTIPROC_DIR=/tmp/ocean/prometheus/metrics
|
|
9
8
|
|
|
10
9
|
ENV LIBRDKAFKA_VERSION=2.8.2 \
|
|
11
10
|
PYTHONUNBUFFERED=1 \
|
|
12
11
|
POETRY_VIRTUALENVS_IN_PROJECT=1 \
|
|
13
|
-
PIP_ROOT_USER_ACTION=ignore
|
|
14
|
-
PROMETHEUS_MULTIPROC_DIR=${PROMETHEUS_MULTIPROC_DIR}
|
|
15
|
-
|
|
16
|
-
RUN mkdir -p ${PROMETHEUS_MULTIPROC_DIR}
|
|
12
|
+
PIP_ROOT_USER_ACTION=ignore
|
|
17
13
|
|
|
18
14
|
WORKDIR /app
|
|
19
15
|
|
|
@@ -25,8 +21,12 @@ FROM ${BASE_RUNNER_PYTHON_IMAGE} AS prod
|
|
|
25
21
|
|
|
26
22
|
ARG INTEGRATION_VERSION
|
|
27
23
|
ARG BUILD_CONTEXT
|
|
24
|
+
ARG PROMETHEUS_MULTIPROC_DIR=/tmp/ocean/prometheus/metrics
|
|
28
25
|
|
|
29
|
-
ENV LIBRDKAFKA_VERSION=2.8.2
|
|
26
|
+
ENV LIBRDKAFKA_VERSION=2.8.2 \
|
|
27
|
+
PROMETHEUS_MULTIPROC_DIR=${PROMETHEUS_MULTIPROC_DIR}
|
|
28
|
+
|
|
29
|
+
RUN mkdir -p ${PROMETHEUS_MULTIPROC_DIR}
|
|
30
30
|
|
|
31
31
|
RUN apt-get update \
|
|
32
32
|
&& apt-get install -y \
|
|
@@ -214,8 +214,9 @@ class IntegrationClientMixin:
|
|
|
214
214
|
logger.debug("starting POST metrics request", metrics=metrics)
|
|
215
215
|
metrics_attributes = await self.get_metrics_attributes()
|
|
216
216
|
headers = await self.auth.headers()
|
|
217
|
+
url = metrics_attributes["ingestUrl"] + "/syncMetrics"
|
|
217
218
|
response = await self.client.post(
|
|
218
|
-
|
|
219
|
+
url,
|
|
219
220
|
headers=headers,
|
|
220
221
|
json={
|
|
221
222
|
"syncKindsMetrics": metrics,
|
|
@@ -229,7 +230,7 @@ class IntegrationClientMixin:
|
|
|
229
230
|
metrics_attributes = await self.get_metrics_attributes()
|
|
230
231
|
url = (
|
|
231
232
|
metrics_attributes["ingestUrl"]
|
|
232
|
-
+ f"/resync/{kind_metrics['eventId']}/kind/{kind_metrics['kindIdentifier']}"
|
|
233
|
+
+ f"/syncMetrics/resync/{kind_metrics['eventId']}/kind/{kind_metrics['kindIdentifier']}"
|
|
233
234
|
)
|
|
234
235
|
headers = await self.auth.headers()
|
|
235
236
|
response = await self.client.put(
|
|
@@ -51,7 +51,7 @@ def default_config_factory(configurations: Any) -> Type[BaseModel]:
|
|
|
51
51
|
case _:
|
|
52
52
|
raise ValueError(f"Unknown type: {config.type}")
|
|
53
53
|
|
|
54
|
-
default = ... if config.required else None
|
|
54
|
+
default: Any = ... if config.required else None
|
|
55
55
|
if config.default is not None:
|
|
56
56
|
default = parse_obj_as(field_type, config.default)
|
|
57
57
|
fields[decamelize(config.name)] = (
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from contextlib import asynccontextmanager
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import AsyncIterator, TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from loguru import logger
|
|
6
|
+
from werkzeug.local import LocalStack, LocalProxy
|
|
7
|
+
|
|
8
|
+
from port_ocean.exceptions.context import (
|
|
9
|
+
ResourceContextNotFoundError,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class MetricResourceContext:
|
|
18
|
+
"""
|
|
19
|
+
The metric resource context is a context manager that allows you to access the current metric resource if there is one.
|
|
20
|
+
This is useful for getting the metric resource kind
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
metric_resource_kind: str
|
|
24
|
+
index: int
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def kind(self) -> str:
|
|
28
|
+
return self.metric_resource_kind
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
_resource_context_stack: LocalStack[MetricResourceContext] = LocalStack()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _get_metric_resource_context() -> MetricResourceContext:
|
|
35
|
+
"""
|
|
36
|
+
Get the context from the current thread.
|
|
37
|
+
"""
|
|
38
|
+
top_resource_context = _resource_context_stack.top
|
|
39
|
+
if top_resource_context is None:
|
|
40
|
+
raise ResourceContextNotFoundError(
|
|
41
|
+
"You must be inside an metric resource context in order to use it"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
return top_resource_context
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
metric_resource: MetricResourceContext = LocalProxy(lambda: _get_metric_resource_context()) # type: ignore
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@asynccontextmanager
|
|
51
|
+
async def metric_resource_context(
|
|
52
|
+
metric_resource_kind: str, index: int = 0
|
|
53
|
+
) -> AsyncIterator[MetricResourceContext]:
|
|
54
|
+
_resource_context_stack.push(
|
|
55
|
+
MetricResourceContext(metric_resource_kind=metric_resource_kind, index=index)
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
with logger.contextualize(
|
|
59
|
+
metric_resource_kind=metric_resource.metric_resource_kind
|
|
60
|
+
):
|
|
61
|
+
yield metric_resource
|
|
62
|
+
|
|
63
|
+
_resource_context_stack.pop()
|
{port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/core/handlers/resync_state_updater/updater.py
RENAMED
|
@@ -94,6 +94,6 @@ class ResyncStateUpdater:
|
|
|
94
94
|
await ocean.metrics.send_metrics_to_webhook(
|
|
95
95
|
kind=ocean.metrics.current_resource_kind()
|
|
96
96
|
)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
await ocean.metrics.report_sync_metrics(
|
|
98
|
+
kinds=[ocean.metrics.current_resource_kind()]
|
|
99
|
+
)
|
|
@@ -9,6 +9,7 @@ import httpx
|
|
|
9
9
|
from loguru import logger
|
|
10
10
|
from port_ocean.clients.port.types import UserAgentType
|
|
11
11
|
from port_ocean.context.event import TriggerType, event_context, EventType, event
|
|
12
|
+
from port_ocean.context.metric_resource import metric_resource_context
|
|
12
13
|
from port_ocean.context.ocean import ocean
|
|
13
14
|
from port_ocean.context.resource import resource_context
|
|
14
15
|
from port_ocean.context import resource
|
|
@@ -32,9 +33,9 @@ from port_ocean.core.ocean_types import (
|
|
|
32
33
|
CalculationResult,
|
|
33
34
|
)
|
|
34
35
|
from port_ocean.core.utils.utils import resolve_entities_diff, zip_and_sum, gather_and_split_errors_from_results
|
|
35
|
-
from port_ocean.exceptions.core import OceanAbortException
|
|
36
|
-
from port_ocean.helpers.metric.metric import SyncState, MetricType, MetricPhase
|
|
37
|
-
from port_ocean.helpers.metric.utils import TimeMetric
|
|
36
|
+
from port_ocean.exceptions.core import IntegrationSubProcessFailedException, OceanAbortException
|
|
37
|
+
from port_ocean.helpers.metric.metric import MetricResourceKind, SyncState, MetricType, MetricPhase
|
|
38
|
+
from port_ocean.helpers.metric.utils import TimeMetric, TimeMetricWithResourceKind
|
|
38
39
|
from port_ocean.utils.ipc import FileIPC
|
|
39
40
|
|
|
40
41
|
SEND_RAW_DATA_EXAMPLES_AMOUNT = 5
|
|
@@ -249,9 +250,16 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
249
250
|
labels=[ocean.metrics.current_resource_kind(), MetricPhase.LOAD, MetricPhase.LoadResult.SKIPPED],
|
|
250
251
|
value=len(objects_diff[0].entity_selector_diff.passed) - len(changed_entities)
|
|
251
252
|
)
|
|
252
|
-
await self.entities_state_applier.upsert(
|
|
253
|
+
upserted_entities = await self.entities_state_applier.upsert(
|
|
253
254
|
changed_entities, user_agent_type
|
|
254
255
|
)
|
|
256
|
+
|
|
257
|
+
ocean.metrics.set_metric(
|
|
258
|
+
name=MetricType.OBJECT_COUNT_NAME,
|
|
259
|
+
labels=[ocean.metrics.current_resource_kind(), MetricPhase.LOAD, MetricPhase.LoadResult.LOADED],
|
|
260
|
+
value=len(upserted_entities)
|
|
261
|
+
)
|
|
262
|
+
|
|
255
263
|
else:
|
|
256
264
|
logger.info("Entities in batch didn't changed since last sync, skipping", total_entities=len(objects_diff[0].entity_selector_diff.passed))
|
|
257
265
|
ocean.metrics.inc_metric(
|
|
@@ -265,6 +273,11 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
265
273
|
modified_objects = await self.entities_state_applier.upsert(
|
|
266
274
|
objects_diff[0].entity_selector_diff.passed, user_agent_type
|
|
267
275
|
)
|
|
276
|
+
ocean.metrics.set_metric(
|
|
277
|
+
name=MetricType.OBJECT_COUNT_NAME,
|
|
278
|
+
labels=[ocean.metrics.current_resource_kind(), MetricPhase.LOAD, MetricPhase.LoadResult.LOADED],
|
|
279
|
+
value=len(upserted_entities)
|
|
280
|
+
)
|
|
268
281
|
else:
|
|
269
282
|
modified_objects = await self.entities_state_applier.upsert(
|
|
270
283
|
objects_diff[0].entity_selector_diff.passed, user_agent_type
|
|
@@ -613,14 +626,13 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
613
626
|
id = uuid.uuid4()
|
|
614
627
|
logger.info(f"Starting subprocess with id {id}")
|
|
615
628
|
file_ipc_map = {
|
|
616
|
-
"process_resource": FileIPC(id, "process_resource",([],[])),
|
|
629
|
+
"process_resource": FileIPC(id, "process_resource",([],[IntegrationSubProcessFailedException(f"Subprocess failed for {resource.kind} with index {index}")])),
|
|
617
630
|
"topological_entities": FileIPC(id, "topological_entities",[]),
|
|
618
631
|
}
|
|
619
632
|
process = ProcessWrapper(target=self.process_resource_in_subprocess, args=(file_ipc_map,resource,index,user_agent_type))
|
|
620
633
|
process.start()
|
|
621
634
|
await process.join_async()
|
|
622
635
|
|
|
623
|
-
|
|
624
636
|
event.entity_topological_sorter.entities.extend(file_ipc_map["topological_entities"].load())
|
|
625
637
|
return file_ipc_map["process_resource"].load()
|
|
626
638
|
|
|
@@ -633,16 +645,12 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
633
645
|
async with resource_context(resource,index):
|
|
634
646
|
resource_kind_id = f"{resource.kind}-{index}"
|
|
635
647
|
ocean.metrics.sync_state = SyncState.SYNCING
|
|
648
|
+
|
|
636
649
|
task = asyncio.create_task(
|
|
637
650
|
self._register_in_batches(resource, user_agent_type)
|
|
638
651
|
)
|
|
639
652
|
event.on_abort(lambda: task.cancel())
|
|
640
653
|
kind_results: tuple[list[Entity], list[Exception]] = await task
|
|
641
|
-
ocean.metrics.set_metric(
|
|
642
|
-
name=MetricType.OBJECT_COUNT_NAME,
|
|
643
|
-
labels=[ocean.metrics.current_resource_kind(), MetricPhase.LOAD, MetricPhase.LoadResult.LOADED],
|
|
644
|
-
value=len(kind_results[0])
|
|
645
|
-
)
|
|
646
654
|
|
|
647
655
|
if ocean.metrics.sync_state != SyncState.FAILED:
|
|
648
656
|
ocean.metrics.sync_state = SyncState.COMPLETED
|
|
@@ -650,10 +658,88 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
650
658
|
await ocean.metrics.send_metrics_to_webhook(
|
|
651
659
|
kind=resource_kind_id
|
|
652
660
|
)
|
|
653
|
-
|
|
661
|
+
await ocean.metrics.report_kind_sync_metrics(kind=resource_kind_id, blueprint=resource.port.entity.mappings.blueprint)
|
|
654
662
|
|
|
655
663
|
return kind_results
|
|
656
664
|
|
|
665
|
+
@TimeMetricWithResourceKind(MetricPhase.RESYNC)
|
|
666
|
+
async def resync_reconciliation(
|
|
667
|
+
self,
|
|
668
|
+
creation_results: list[tuple[list[Entity], list[Exception]]],
|
|
669
|
+
did_fetched_current_state: bool,
|
|
670
|
+
user_agent_type: UserAgentType,
|
|
671
|
+
app_config: Any,
|
|
672
|
+
silent: bool = True,
|
|
673
|
+
) -> None:
|
|
674
|
+
"""Handle the reconciliation phase of the resync process.
|
|
675
|
+
|
|
676
|
+
This method handles:
|
|
677
|
+
1. Sorting and upserting failed entities
|
|
678
|
+
2. Checking if current state was fetched
|
|
679
|
+
3. Calculating resync diff
|
|
680
|
+
4. Handling errors
|
|
681
|
+
5. Deleting entities that are no longer needed
|
|
682
|
+
6. Executing resync complete hooks
|
|
683
|
+
|
|
684
|
+
Args:
|
|
685
|
+
creation_results (list[tuple[list[Entity], list[Exception]]]): Results from entity creation
|
|
686
|
+
did_fetched_current_state (bool): Whether the current state was successfully fetched
|
|
687
|
+
user_agent_type (UserAgentType): The type of user agent
|
|
688
|
+
app_config (Any): The application configuration
|
|
689
|
+
silent (bool): Whether to raise exceptions or handle them silently
|
|
690
|
+
|
|
691
|
+
"""
|
|
692
|
+
await self.sort_and_upsert_failed_entities(user_agent_type)
|
|
693
|
+
|
|
694
|
+
if not did_fetched_current_state:
|
|
695
|
+
logger.warning(
|
|
696
|
+
"Due to an error before the resync, the previous state of entities at Port is unknown."
|
|
697
|
+
" Skipping delete phase due to unknown initial state."
|
|
698
|
+
)
|
|
699
|
+
return False
|
|
700
|
+
|
|
701
|
+
logger.info("Starting resync diff calculation")
|
|
702
|
+
generated_entities, errors = zip_and_sum(creation_results) or [
|
|
703
|
+
[],
|
|
704
|
+
[],
|
|
705
|
+
]
|
|
706
|
+
|
|
707
|
+
if errors:
|
|
708
|
+
message = f"Resync failed with {len(errors)} errors, skipping delete phase due to incomplete state"
|
|
709
|
+
error_group = ExceptionGroup(
|
|
710
|
+
message,
|
|
711
|
+
errors,
|
|
712
|
+
)
|
|
713
|
+
if not silent:
|
|
714
|
+
raise error_group
|
|
715
|
+
|
|
716
|
+
logger.error(message, exc_info=error_group)
|
|
717
|
+
return False
|
|
718
|
+
|
|
719
|
+
logger.info(
|
|
720
|
+
f"Running resync diff calculation, number of entities created during sync: {len(generated_entities)}"
|
|
721
|
+
)
|
|
722
|
+
entities_at_port = await ocean.port_client.search_entities(
|
|
723
|
+
user_agent_type
|
|
724
|
+
)
|
|
725
|
+
|
|
726
|
+
await self.entities_state_applier.delete_diff(
|
|
727
|
+
{"before": entities_at_port, "after": generated_entities},
|
|
728
|
+
user_agent_type, app_config.get_entity_deletion_threshold()
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
logger.info("Resync finished successfully")
|
|
732
|
+
|
|
733
|
+
# Execute resync_complete hooks
|
|
734
|
+
if "resync_complete" in self.event_strategy:
|
|
735
|
+
logger.info("Executing resync_complete hooks")
|
|
736
|
+
|
|
737
|
+
for resync_complete_fn in self.event_strategy["resync_complete"]:
|
|
738
|
+
await resync_complete_fn()
|
|
739
|
+
|
|
740
|
+
logger.info("Finished executing resync_complete hooks")
|
|
741
|
+
|
|
742
|
+
|
|
657
743
|
@TimeMetric(MetricPhase.RESYNC)
|
|
658
744
|
async def sync_raw_all(
|
|
659
745
|
self,
|
|
@@ -689,8 +775,9 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
689
775
|
logger.info(f"Resync will use the following mappings: {app_config.dict()}")
|
|
690
776
|
|
|
691
777
|
kinds = [f"{resource.kind}-{index}" for index, resource in enumerate(app_config.resources)]
|
|
778
|
+
blueprints = [resource.port.entity.mappings.blueprint for resource in app_config.resources]
|
|
692
779
|
ocean.metrics.initialize_metrics(kinds)
|
|
693
|
-
|
|
780
|
+
await ocean.metrics.report_sync_metrics(kinds=kinds, blueprints=blueprints)
|
|
694
781
|
|
|
695
782
|
# Clear cache
|
|
696
783
|
await ocean.app.cache_provider.clear()
|
|
@@ -716,65 +803,20 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
716
803
|
multiprocessing.set_start_method('fork', True)
|
|
717
804
|
try:
|
|
718
805
|
for index,resource in enumerate(app_config.resources):
|
|
719
|
-
|
|
720
806
|
logger.info(f"Starting processing resource {resource.kind} with index {index}")
|
|
721
|
-
|
|
722
807
|
creation_results.append(await self.process_resource(resource,index,user_agent_type))
|
|
723
|
-
|
|
724
|
-
await self.sort_and_upsert_failed_entities(user_agent_type)
|
|
725
|
-
|
|
726
808
|
except asyncio.CancelledError as e:
|
|
727
809
|
logger.warning("Resync aborted successfully, skipping delete phase. This leads to an incomplete state")
|
|
728
810
|
raise
|
|
729
811
|
else:
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
generated_entities, errors = zip_and_sum(creation_results) or [
|
|
739
|
-
[],
|
|
740
|
-
[],
|
|
741
|
-
]
|
|
742
|
-
|
|
743
|
-
if errors:
|
|
744
|
-
message = f"Resync failed with {len(errors)} errors, skipping delete phase due to incomplete state"
|
|
745
|
-
error_group = ExceptionGroup(
|
|
746
|
-
message,
|
|
747
|
-
errors,
|
|
748
|
-
)
|
|
749
|
-
if not silent:
|
|
750
|
-
raise error_group
|
|
751
|
-
|
|
752
|
-
logger.error(message, exc_info=error_group)
|
|
753
|
-
return False
|
|
754
|
-
else:
|
|
755
|
-
logger.info(
|
|
756
|
-
f"Running resync diff calculation, number of entities created during sync: {len(generated_entities)}"
|
|
757
|
-
)
|
|
758
|
-
entities_at_port = await ocean.port_client.search_entities(
|
|
759
|
-
user_agent_type
|
|
760
|
-
)
|
|
761
|
-
await self.entities_state_applier.delete_diff(
|
|
762
|
-
{"before": entities_at_port, "after": generated_entities},
|
|
763
|
-
user_agent_type, app_config.get_entity_deletion_threshold()
|
|
764
|
-
)
|
|
765
|
-
|
|
766
|
-
logger.info("Resync finished successfully")
|
|
767
|
-
|
|
768
|
-
# Execute resync_complete hooks
|
|
769
|
-
if "resync_complete" in self.event_strategy:
|
|
770
|
-
logger.info("Executing resync_complete hooks")
|
|
771
|
-
|
|
772
|
-
for resync_complete_fn in self.event_strategy["resync_complete"]:
|
|
773
|
-
await resync_complete_fn()
|
|
774
|
-
|
|
775
|
-
logger.info("Finished executing resync_complete hooks")
|
|
776
|
-
|
|
777
|
-
return True
|
|
812
|
+
await self.resync_reconciliation(
|
|
813
|
+
creation_results,
|
|
814
|
+
did_fetched_current_state,
|
|
815
|
+
user_agent_type,
|
|
816
|
+
app_config,
|
|
817
|
+
silent
|
|
818
|
+
)
|
|
819
|
+
await ocean.metrics.report_sync_metrics(kinds=[MetricResourceKind.RECONCILIATION])
|
|
778
820
|
finally:
|
|
779
821
|
await ocean.app.cache_provider.clear()
|
|
780
822
|
if ocean.app.process_execution_mode == ProcessExecutionMode.multi_process:
|
|
@@ -6,7 +6,7 @@ import prometheus_client
|
|
|
6
6
|
from httpx import AsyncClient
|
|
7
7
|
from fastapi.responses import PlainTextResponse
|
|
8
8
|
from loguru import logger
|
|
9
|
-
from port_ocean.context import resource
|
|
9
|
+
from port_ocean.context import metric_resource, resource
|
|
10
10
|
from prometheus_client import Gauge
|
|
11
11
|
import prometheus_client.openmetrics
|
|
12
12
|
import prometheus_client.openmetrics.exposition
|
|
@@ -57,6 +57,11 @@ class SyncState:
|
|
|
57
57
|
FAILED = "failed"
|
|
58
58
|
|
|
59
59
|
|
|
60
|
+
class MetricResourceKind:
|
|
61
|
+
RECONCILIATION = "__reconciliation__"
|
|
62
|
+
RESYNC = "__resync__"
|
|
63
|
+
|
|
64
|
+
|
|
60
65
|
# Registry for core and custom metrics
|
|
61
66
|
_metrics_registry: Dict[str, Tuple[str, str, List[str]]] = {
|
|
62
67
|
MetricType.DURATION_NAME: (
|
|
@@ -118,6 +123,7 @@ class Metrics:
|
|
|
118
123
|
self.registry = prometheus_client.CollectorRegistry()
|
|
119
124
|
if multiprocessing_enabled:
|
|
120
125
|
multiprocess.MultiProcessCollector(self.registry)
|
|
126
|
+
self.multiprocessing_enabled = multiprocessing_enabled
|
|
121
127
|
self.metrics: dict[str, Gauge] = {}
|
|
122
128
|
self.load_metrics()
|
|
123
129
|
self._integration_version: Optional[str] = None
|
|
@@ -208,7 +214,8 @@ class Metrics:
|
|
|
208
214
|
logger.error(f"Failed to cleanup prometheus metrics: {e}")
|
|
209
215
|
|
|
210
216
|
def initialize_metrics(self, kind_blockes: list[str]) -> None:
|
|
211
|
-
self.
|
|
217
|
+
if self.multiprocessing_enabled:
|
|
218
|
+
self.cleanup_prometheus_metrics()
|
|
212
219
|
for kind in kind_blockes:
|
|
213
220
|
self.set_metric(MetricType.SUCCESS_NAME, [kind, MetricPhase.RESYNC], 0)
|
|
214
221
|
self.set_metric(MetricType.DURATION_NAME, [kind, MetricPhase.RESYNC], 0)
|
|
@@ -252,8 +259,6 @@ class Metrics:
|
|
|
252
259
|
)
|
|
253
260
|
|
|
254
261
|
def create_mertic_router(self) -> APIRouter:
|
|
255
|
-
if not self.enabled:
|
|
256
|
-
return APIRouter()
|
|
257
262
|
router = APIRouter()
|
|
258
263
|
|
|
259
264
|
@router.get("/", response_class=PlainTextResponse)
|
|
@@ -265,6 +270,12 @@ class Metrics:
|
|
|
265
270
|
def current_resource_kind(self) -> str:
|
|
266
271
|
try:
|
|
267
272
|
return f"{resource.resource.kind}-{resource.resource.index}"
|
|
273
|
+
except ResourceContextNotFoundError:
|
|
274
|
+
return self.current_metric_resource_kind()
|
|
275
|
+
|
|
276
|
+
def current_metric_resource_kind(self) -> str:
|
|
277
|
+
try:
|
|
278
|
+
return metric_resource.metric_resource.metric_resource_kind
|
|
268
279
|
except ResourceContextNotFoundError:
|
|
269
280
|
return "__runtime__"
|
|
270
281
|
|
|
@@ -274,15 +285,21 @@ class Metrics:
|
|
|
274
285
|
).decode()
|
|
275
286
|
|
|
276
287
|
async def report_sync_metrics(
|
|
277
|
-
self,
|
|
288
|
+
self,
|
|
289
|
+
metric_name: Optional[str] = None,
|
|
290
|
+
kinds: Optional[list[str]] = None,
|
|
291
|
+
blueprints: Optional[list[Optional[str]]] = None,
|
|
278
292
|
) -> None:
|
|
279
293
|
if kinds is None:
|
|
280
294
|
return None
|
|
281
295
|
|
|
282
296
|
metrics = []
|
|
283
297
|
|
|
284
|
-
|
|
285
|
-
|
|
298
|
+
if blueprints is None:
|
|
299
|
+
blueprints = [None] * len(kinds)
|
|
300
|
+
|
|
301
|
+
for kind, blueprint in zip(kinds, blueprints):
|
|
302
|
+
metric = self.generate_metrics(metric_name, kind, blueprint)
|
|
286
303
|
metrics.extend(metric)
|
|
287
304
|
|
|
288
305
|
try:
|
|
@@ -291,9 +308,12 @@ class Metrics:
|
|
|
291
308
|
logger.error(f"Error posting metrics: {e}", metrics=metrics)
|
|
292
309
|
|
|
293
310
|
async def report_kind_sync_metrics(
|
|
294
|
-
self,
|
|
311
|
+
self,
|
|
312
|
+
metric_name: Optional[str] = None,
|
|
313
|
+
kind: Optional[str] = None,
|
|
314
|
+
blueprint: Optional[str] = None,
|
|
295
315
|
) -> None:
|
|
296
|
-
metrics = self.generate_metrics(metric_name, kind)
|
|
316
|
+
metrics = self.generate_metrics(metric_name, kind, blueprint)
|
|
297
317
|
if not metrics:
|
|
298
318
|
return None
|
|
299
319
|
|
|
@@ -304,7 +324,10 @@ class Metrics:
|
|
|
304
324
|
logger.error(f"Error putting metrics: {e}", metrics=metrics)
|
|
305
325
|
|
|
306
326
|
def generate_metrics(
|
|
307
|
-
self,
|
|
327
|
+
self,
|
|
328
|
+
metric_name: Optional[str] = None,
|
|
329
|
+
kind: Optional[str] = None,
|
|
330
|
+
blueprint: Optional[str] = None,
|
|
308
331
|
) -> list[dict[str, Any]]:
|
|
309
332
|
try:
|
|
310
333
|
latest_raw = self.generate_latest()
|
|
@@ -363,9 +386,10 @@ class Metrics:
|
|
|
363
386
|
if "-" in kind_key
|
|
364
387
|
else kind_key
|
|
365
388
|
),
|
|
366
|
-
"kindIndex":
|
|
389
|
+
"kindIndex": int(kind_key[-1]) if kind_key[-1].isdigit() else 0,
|
|
367
390
|
"eventId": self.event_id,
|
|
368
391
|
"syncState": self.sync_state,
|
|
392
|
+
"blueprint": blueprint if blueprint else "",
|
|
369
393
|
"metrics": metrics,
|
|
370
394
|
}
|
|
371
395
|
events.append(event)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
import time
|
|
3
|
+
from typing import Any, Callable
|
|
4
|
+
|
|
5
|
+
from port_ocean.context.metric_resource import metric_resource_context
|
|
6
|
+
from port_ocean.context.ocean import ocean
|
|
7
|
+
from port_ocean.helpers.metric.metric import MetricResourceKind, MetricType
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def TimeMetric(phase: str) -> Any:
|
|
11
|
+
def decorator(func: Callable[..., Any]) -> Any:
|
|
12
|
+
|
|
13
|
+
@wraps(func)
|
|
14
|
+
async def wrapper(*args: Any, **kwargs: dict[Any, Any]) -> Any:
|
|
15
|
+
start = time.monotonic()
|
|
16
|
+
res = await func(*args, **kwargs)
|
|
17
|
+
end = time.monotonic()
|
|
18
|
+
duration = end - start
|
|
19
|
+
ocean.metrics.inc_metric(
|
|
20
|
+
name=MetricType.DURATION_NAME,
|
|
21
|
+
labels=[ocean.metrics.current_resource_kind(), phase],
|
|
22
|
+
value=duration,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
return res
|
|
26
|
+
|
|
27
|
+
return wrapper
|
|
28
|
+
|
|
29
|
+
return decorator
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def TimeMetricWithResourceKind(phase: str) -> Any:
|
|
33
|
+
def decorator(func: Callable[..., Any]) -> Any:
|
|
34
|
+
|
|
35
|
+
@wraps(func)
|
|
36
|
+
async def wrapper(*args: Any, **kwargs: dict[Any, Any]) -> Any:
|
|
37
|
+
async with metric_resource_context(MetricResourceKind.RECONCILIATION):
|
|
38
|
+
start = time.monotonic()
|
|
39
|
+
res = await func(*args, **kwargs)
|
|
40
|
+
end = time.monotonic()
|
|
41
|
+
duration = end - start
|
|
42
|
+
ocean.metrics.inc_metric(
|
|
43
|
+
name=MetricType.DURATION_NAME,
|
|
44
|
+
labels=[ocean.metrics.current_resource_kind(), phase],
|
|
45
|
+
value=duration,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
return res
|
|
49
|
+
|
|
50
|
+
return wrapper
|
|
51
|
+
|
|
52
|
+
return decorator
|
{port_ocean-0.23.3 → port_ocean-0.23.5}/port_ocean/tests/core/handlers/mixins/test_sync_raw.py
RENAMED
|
@@ -154,7 +154,7 @@ async def test_sync_raw_mixin_self_dependency(
|
|
|
154
154
|
|
|
155
155
|
# Add assertions for actual metrics
|
|
156
156
|
metrics = mock_ocean.metrics.generate_metrics()
|
|
157
|
-
assert len(metrics) ==
|
|
157
|
+
assert len(metrics) == 3
|
|
158
158
|
|
|
159
159
|
# Verify object counts
|
|
160
160
|
for metric in metrics:
|
|
@@ -187,7 +187,7 @@ async def test_sync_raw_mixin_self_dependency(
|
|
|
187
187
|
metric["metrics"]["phase"]["load"]["object_count_type"][
|
|
188
188
|
"loaded"
|
|
189
189
|
]["object_count"]
|
|
190
|
-
==
|
|
190
|
+
== 1
|
|
191
191
|
)
|
|
192
192
|
|
|
193
193
|
# Verify success
|
|
@@ -196,6 +196,14 @@ async def test_sync_raw_mixin_self_dependency(
|
|
|
196
196
|
# Verify sync state
|
|
197
197
|
assert metric["syncState"] == "completed"
|
|
198
198
|
|
|
199
|
+
if metric["kind"] == "reconciliation":
|
|
200
|
+
assert (
|
|
201
|
+
metric["metrics"]["phase"]["load"]["object_count_type"][
|
|
202
|
+
"failed"
|
|
203
|
+
]["object_count"]
|
|
204
|
+
== 1
|
|
205
|
+
)
|
|
206
|
+
|
|
199
207
|
|
|
200
208
|
@pytest.mark.asyncio
|
|
201
209
|
async def test_sync_raw_mixin_circular_dependency(
|
|
@@ -282,7 +290,7 @@ async def test_sync_raw_mixin_circular_dependency(
|
|
|
282
290
|
|
|
283
291
|
# Add assertions for actual metrics
|
|
284
292
|
metrics = mock_ocean.metrics.generate_metrics()
|
|
285
|
-
assert len(metrics) ==
|
|
293
|
+
assert len(metrics) == 3
|
|
286
294
|
|
|
287
295
|
# Verify object counts
|
|
288
296
|
for metric in metrics:
|
|
@@ -315,7 +323,7 @@ async def test_sync_raw_mixin_circular_dependency(
|
|
|
315
323
|
metric["metrics"]["phase"]["load"]["object_count_type"][
|
|
316
324
|
"loaded"
|
|
317
325
|
]["object_count"]
|
|
318
|
-
==
|
|
326
|
+
== 0
|
|
319
327
|
)
|
|
320
328
|
|
|
321
329
|
# Verify success
|
|
@@ -324,6 +332,14 @@ async def test_sync_raw_mixin_circular_dependency(
|
|
|
324
332
|
# Verify sync state
|
|
325
333
|
assert metric["syncState"] == "completed"
|
|
326
334
|
|
|
335
|
+
if metric["kind"] == "reconciliation":
|
|
336
|
+
assert (
|
|
337
|
+
metric["metrics"]["phase"]["load"]["object_count_type"][
|
|
338
|
+
"loaded"
|
|
339
|
+
]["object_count"]
|
|
340
|
+
== 2
|
|
341
|
+
)
|
|
342
|
+
|
|
327
343
|
|
|
328
344
|
@pytest.mark.asyncio
|
|
329
345
|
async def test_sync_raw_mixin_dependency(
|
|
@@ -422,7 +438,7 @@ async def test_sync_raw_mixin_dependency(
|
|
|
422
438
|
|
|
423
439
|
# Add assertions for actual metrics
|
|
424
440
|
metrics = mock_ocean.metrics.generate_metrics()
|
|
425
|
-
assert len(metrics) ==
|
|
441
|
+
assert len(metrics) == 3
|
|
426
442
|
|
|
427
443
|
# Verify object counts
|
|
428
444
|
for metric in metrics:
|
|
@@ -458,6 +474,14 @@ async def test_sync_raw_mixin_dependency(
|
|
|
458
474
|
# Verify sync state
|
|
459
475
|
assert metric["syncState"] == "completed"
|
|
460
476
|
|
|
477
|
+
if metric["kind"] == "reconciliation":
|
|
478
|
+
assert (
|
|
479
|
+
metric["metrics"]["phase"]["load"]["object_count_type"][
|
|
480
|
+
"loaded"
|
|
481
|
+
]["object_count"]
|
|
482
|
+
== 5
|
|
483
|
+
)
|
|
484
|
+
|
|
461
485
|
|
|
462
486
|
@pytest.mark.asyncio
|
|
463
487
|
async def test_register_raw(
|