port-ocean 0.27.10__tar.gz → 0.28.1__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.
- {port_ocean-0.27.10 → port_ocean-0.28.1}/PKG-INFO +1 -1
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/integrations/mixins/sync_raw.py +10 -10
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/helpers/async_client.py +7 -8
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/helpers/retry.py +162 -72
- port_ocean-0.28.1/port_ocean/tests/helpers/test_retry.py +309 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/pyproject.toml +1 -1
- {port_ocean-0.27.10 → port_ocean-0.28.1}/LICENSE.md +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/README.md +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/integrations/_infra/Dockerfile.Deb +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/integrations/_infra/Dockerfile.alpine +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/integrations/_infra/Dockerfile.base.builder +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/integrations/_infra/Dockerfile.base.runner +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/integrations/_infra/Dockerfile.dockerignore +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/integrations/_infra/Dockerfile.local +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/integrations/_infra/Makefile +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/integrations/_infra/README.md +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/integrations/_infra/entry_local.sh +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/integrations/_infra/grpcio.sh +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/integrations/_infra/init.sh +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/bootstrap.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cache/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cache/base.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cache/disk.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cache/errors.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cache/memory.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cli.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/commands/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/commands/defaults/__init___.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/commands/defaults/clean.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/commands/defaults/dock.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/commands/defaults/group.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/commands/list_integrations.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/commands/main.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/commands/new.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/commands/pull.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/commands/sail.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/commands/version.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/cookiecutter.json +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/extensions.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/hooks/post_gen_project.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.env.example +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.gitignore +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/.gitignore +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/blueprints.json +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/port-app-config.yml +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CHANGELOG.md +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CONTRIBUTING.md +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/README.md +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/changelog/.gitignore +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/debug.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/main.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/poetry.toml +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/pyproject.toml +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/sonar-project.properties +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/test_sample.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/cli/utils.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/clients/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/clients/auth/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/clients/auth/auth_client.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/clients/auth/oauth_client.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/clients/port/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/clients/port/authentication.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/clients/port/client.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/clients/port/mixins/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/clients/port/mixins/blueprints.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/clients/port/mixins/entities.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/clients/port/mixins/integrations.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/clients/port/mixins/migrations.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/clients/port/mixins/organization.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/clients/port/retry_transport.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/clients/port/types.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/clients/port/utils.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/config/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/config/base.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/config/dynamic.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/config/settings.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/consumers/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/consumers/kafka_consumer.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/context/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/context/event.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/context/metric_resource.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/context/ocean.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/context/resource.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/defaults/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/defaults/clean.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/defaults/common.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/defaults/initialize.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/event_listener/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/event_listener/base.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/event_listener/factory.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/event_listener/http.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/event_listener/kafka.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/event_listener/once.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/event_listener/polling.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/event_listener/webhooks_only.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/base.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/entities_state_applier/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/entities_state_applier/base.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/entities_state_applier/port/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/entities_state_applier/port/applier.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/entities_state_applier/port/get_related_entities.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/entity_processor/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/entity_processor/base.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/entity_processor/jq_entity_processor.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/port_app_config/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/port_app_config/api.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/port_app_config/base.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/port_app_config/models.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/queue/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/queue/abstract_queue.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/queue/group_queue.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/queue/local_queue.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/resync_state_updater/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/resync_state_updater/updater.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/webhook/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/webhook/abstract_webhook_processor.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/webhook/processor_manager.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/handlers/webhook/webhook_event.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/integrations/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/integrations/base.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/integrations/mixins/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/integrations/mixins/events.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/integrations/mixins/handler.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/integrations/mixins/live_events.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/integrations/mixins/sync.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/integrations/mixins/utils.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/models.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/ocean_types.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/utils/entity_topological_sorter.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/core/utils/utils.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/debug_cli.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/exceptions/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/exceptions/api.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/exceptions/base.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/exceptions/clients.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/exceptions/context.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/exceptions/core.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/exceptions/port_defaults.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/exceptions/utils.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/exceptions/webhook_processor.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/helpers/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/helpers/metric/metric.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/helpers/metric/utils.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/helpers/stream.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/log/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/log/handlers.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/log/logger_setup.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/log/sensetive.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/middlewares.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/ocean.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/py.typed +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/run.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/sonar-project.properties +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/cache/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/cache/test_disk_cache.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/cache/test_memory_cache.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/clients/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/clients/oauth/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/clients/oauth/test_oauth_client.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/clients/port/mixins/test_entities.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/clients/port/mixins/test_integrations.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/clients/port/mixins/test_organization_mixin.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/config/test_config.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/conftest.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/core/conftest.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/core/defaults/test_common.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/core/event_listener/test_kafka.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/core/handlers/entities_state_applier/test_applier.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/core/handlers/mixins/test_live_events.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/core/handlers/mixins/test_sync_raw.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/core/handlers/port_app_config/test_api.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/core/handlers/port_app_config/test_base.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/core/handlers/queue/test_group_queue.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/core/handlers/queue/test_local_queue.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/core/handlers/webhook/test_abstract_webhook_processor.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/core/handlers/webhook/test_processor_manager.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/core/handlers/webhook/test_webhook_event.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/core/test_utils.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/core/utils/test_entity_topological_sorter.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/core/utils/test_resolve_entities_diff.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/helpers/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/helpers/fake_port_api.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/helpers/fixtures.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/helpers/integration.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/helpers/ocean_app.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/helpers/port_client.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/helpers/smoke_test.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/log/test_handlers.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/test_metric.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/test_ocean.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/test_smoke.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/utils/test_async_iterators.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/tests/utils/test_cache.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/utils/__init__.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/utils/async_http.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/utils/async_iterators.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/utils/cache.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/utils/ipc.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/utils/misc.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/utils/queue_utils.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/utils/repeat.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/utils/signal.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/utils/time.py +0 -0
- {port_ocean-0.27.10 → port_ocean-0.28.1}/port_ocean/version.py +0 -0
@@ -455,16 +455,16 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
455
455
|
],
|
456
456
|
value=number_of_transformed_entities,
|
457
457
|
)
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
458
|
+
if number_of_raw_results > number_of_transformed_entities :
|
459
|
+
ocean.metrics.inc_metric(
|
460
|
+
name=MetricType.OBJECT_COUNT_NAME,
|
461
|
+
labels=[
|
462
|
+
ocean.metrics.current_resource_kind(),
|
463
|
+
MetricPhase.TRANSFORM,
|
464
|
+
MetricPhase.TransformResult.FILTERED_OUT,
|
465
|
+
],
|
466
|
+
value=number_of_raw_results - number_of_transformed_entities,
|
467
|
+
)
|
468
468
|
|
469
469
|
return passed_entities, errors
|
470
470
|
|
@@ -3,7 +3,7 @@ from typing import Any, Type
|
|
3
3
|
import httpx
|
4
4
|
from loguru import logger
|
5
5
|
|
6
|
-
from port_ocean.helpers.retry import RetryTransport
|
6
|
+
from port_ocean.helpers.retry import RetryTransport, RetryConfig
|
7
7
|
from port_ocean.helpers.stream import Stream
|
8
8
|
|
9
9
|
|
@@ -18,10 +18,12 @@ class OceanAsyncClient(httpx.AsyncClient):
|
|
18
18
|
self,
|
19
19
|
transport_class: Type[RetryTransport] = RetryTransport,
|
20
20
|
transport_kwargs: dict[str, Any] | None = None,
|
21
|
+
retry_config: RetryConfig | None = None,
|
21
22
|
**kwargs: Any,
|
22
23
|
):
|
23
24
|
self._transport_kwargs = transport_kwargs
|
24
25
|
self._transport_class = transport_class
|
26
|
+
self._retry_config = retry_config
|
25
27
|
super().__init__(**kwargs)
|
26
28
|
|
27
29
|
def _init_transport( # type: ignore[override]
|
@@ -33,9 +35,8 @@ class OceanAsyncClient(httpx.AsyncClient):
|
|
33
35
|
return super()._init_transport(transport=transport, **kwargs)
|
34
36
|
|
35
37
|
return self._transport_class(
|
36
|
-
wrapped_transport=httpx.AsyncHTTPTransport(
|
37
|
-
|
38
|
-
),
|
38
|
+
wrapped_transport=httpx.AsyncHTTPTransport(**kwargs),
|
39
|
+
retry_config=self._retry_config,
|
39
40
|
logger=logger,
|
40
41
|
**(self._transport_kwargs or {}),
|
41
42
|
)
|
@@ -44,10 +45,8 @@ class OceanAsyncClient(httpx.AsyncClient):
|
|
44
45
|
self, proxy: httpx.Proxy, **kwargs: Any
|
45
46
|
) -> httpx.AsyncBaseTransport:
|
46
47
|
return self._transport_class(
|
47
|
-
wrapped_transport=httpx.AsyncHTTPTransport(
|
48
|
-
|
49
|
-
**kwargs,
|
50
|
-
),
|
48
|
+
wrapped_transport=httpx.AsyncHTTPTransport(proxy=proxy, **kwargs),
|
49
|
+
retry_config=self._retry_config,
|
51
50
|
logger=logger,
|
52
51
|
**(self._transport_kwargs or {}),
|
53
52
|
)
|
@@ -4,12 +4,24 @@ import time
|
|
4
4
|
from datetime import datetime
|
5
5
|
from functools import partial
|
6
6
|
from http import HTTPStatus
|
7
|
-
from typing import
|
7
|
+
from typing import (
|
8
|
+
Any,
|
9
|
+
Callable,
|
10
|
+
Coroutine,
|
11
|
+
Iterable,
|
12
|
+
Mapping,
|
13
|
+
Union,
|
14
|
+
cast,
|
15
|
+
Optional,
|
16
|
+
List,
|
17
|
+
)
|
8
18
|
import httpx
|
9
19
|
from dateutil.parser import isoparse
|
10
20
|
import logging
|
11
21
|
|
22
|
+
MAX_BACKOFF_WAIT_IN_SECONDS = 60
|
12
23
|
_ON_RETRY_CALLBACK: Callable[[httpx.Request], httpx.Request] | None = None
|
24
|
+
_RETRY_CONFIG_CALLBACK: Callable[[], "RetryConfig"] | None = None
|
13
25
|
|
14
26
|
|
15
27
|
def register_on_retry_callback(
|
@@ -19,6 +31,92 @@ def register_on_retry_callback(
|
|
19
31
|
_ON_RETRY_CALLBACK = _on_retry_callback
|
20
32
|
|
21
33
|
|
34
|
+
def register_retry_config_callback(
|
35
|
+
retry_config_callback: Callable[[], "RetryConfig"]
|
36
|
+
) -> None:
|
37
|
+
"""Register a callback function that returns a RetryConfig instance.
|
38
|
+
|
39
|
+
The callback will be called when a RetryTransport needs to be created.
|
40
|
+
|
41
|
+
Args:
|
42
|
+
retry_config_callback: A function that returns a RetryConfig instance
|
43
|
+
"""
|
44
|
+
global _RETRY_CONFIG_CALLBACK
|
45
|
+
_RETRY_CONFIG_CALLBACK = retry_config_callback
|
46
|
+
|
47
|
+
|
48
|
+
class RetryConfig:
|
49
|
+
"""Configuration class for retry behavior that can be customized per integration."""
|
50
|
+
|
51
|
+
def __init__(
|
52
|
+
self,
|
53
|
+
max_attempts: int = 10,
|
54
|
+
max_backoff_wait: float = MAX_BACKOFF_WAIT_IN_SECONDS,
|
55
|
+
base_delay: float = 0.1,
|
56
|
+
jitter_ratio: float = 0.1,
|
57
|
+
respect_retry_after_header: bool = True,
|
58
|
+
retryable_methods: Optional[Iterable[str]] = None,
|
59
|
+
retry_status_codes: Optional[Iterable[int]] = None,
|
60
|
+
retry_after_headers: Optional[List[str]] = None,
|
61
|
+
additional_retry_status_codes: Optional[Iterable[int]] = None,
|
62
|
+
):
|
63
|
+
"""
|
64
|
+
Initialize retry configuration.
|
65
|
+
|
66
|
+
Args:
|
67
|
+
max_attempts: Maximum number of retry attempts
|
68
|
+
max_backoff_wait: Maximum backoff wait time in seconds
|
69
|
+
base_delay: Base delay for exponential backoff
|
70
|
+
jitter_ratio: Jitter ratio for backoff (0-0.5)
|
71
|
+
respect_retry_after_header: Whether to respect Retry-After header
|
72
|
+
retryable_methods: HTTP methods that can be retried (overrides defaults if provided)
|
73
|
+
retry_status_codes: DEPRECATED - use additional_retry_status_codes instead
|
74
|
+
retry_after_headers: Custom headers to check for retry timing (e.g., ['X-RateLimit-Reset', 'Retry-After'])
|
75
|
+
additional_retry_status_codes: Additional status codes to retry (extends system defaults)
|
76
|
+
"""
|
77
|
+
self.max_attempts = max_attempts
|
78
|
+
self.max_backoff_wait = max_backoff_wait
|
79
|
+
self.base_delay = base_delay
|
80
|
+
self.jitter_ratio = jitter_ratio
|
81
|
+
self.respect_retry_after_header = respect_retry_after_header
|
82
|
+
|
83
|
+
# Default retryable methods - always include these unless explicitly overridden
|
84
|
+
default_methods = frozenset(
|
85
|
+
["HEAD", "GET", "PUT", "DELETE", "OPTIONS", "TRACE"]
|
86
|
+
)
|
87
|
+
self.retryable_methods = (
|
88
|
+
frozenset(retryable_methods) if retryable_methods else default_methods
|
89
|
+
)
|
90
|
+
|
91
|
+
# Default retry status codes - always include these for system reliability
|
92
|
+
default_status_codes = frozenset(
|
93
|
+
[
|
94
|
+
HTTPStatus.TOO_MANY_REQUESTS,
|
95
|
+
HTTPStatus.BAD_GATEWAY,
|
96
|
+
HTTPStatus.SERVICE_UNAVAILABLE,
|
97
|
+
HTTPStatus.GATEWAY_TIMEOUT,
|
98
|
+
HTTPStatus.UNAUTHORIZED,
|
99
|
+
HTTPStatus.BAD_REQUEST,
|
100
|
+
]
|
101
|
+
)
|
102
|
+
|
103
|
+
# Additional status codes to retry (extends defaults)
|
104
|
+
additional_codes = (
|
105
|
+
frozenset(additional_retry_status_codes)
|
106
|
+
if additional_retry_status_codes
|
107
|
+
else frozenset()
|
108
|
+
)
|
109
|
+
|
110
|
+
# Combine defaults with additional codes for extensibility
|
111
|
+
self.retry_status_codes = default_status_codes | additional_codes
|
112
|
+
self.retry_after_headers = retry_after_headers or ["Retry-After"]
|
113
|
+
|
114
|
+
if jitter_ratio < 0 or jitter_ratio > 0.5:
|
115
|
+
raise ValueError(
|
116
|
+
f"Jitter ratio should be between 0 and 0.5, actual {jitter_ratio}"
|
117
|
+
)
|
118
|
+
|
119
|
+
|
22
120
|
# Adapted from https://github.com/encode/httpx/issues/108#issuecomment-1434439481
|
23
121
|
class RetryTransport(httpx.AsyncBaseTransport, httpx.BaseTransport):
|
24
122
|
"""
|
@@ -41,32 +139,16 @@ class RetryTransport(httpx.AsyncBaseTransport, httpx.BaseTransport):
|
|
41
139
|
["HEAD", "GET", "PUT", "DELETE", "OPTIONS", "TRACE"].
|
42
140
|
retry_status_codes (Iterable[int], optional): The HTTP status codes that can be retried. Defaults to
|
43
141
|
[429, 502, 503, 504].
|
142
|
+
retry_config (RetryConfig, optional): Configuration for retry behavior. If not provided, uses defaults.
|
143
|
+
logger (Any, optional): The logger to use for logging retries.
|
44
144
|
|
45
145
|
Attributes:
|
46
146
|
_wrapped_transport (Union[httpx.BaseTransport, httpx.AsyncBaseTransport]): The underlying HTTP transport
|
47
147
|
being wrapped.
|
48
|
-
|
49
|
-
|
50
|
-
_respect_retry_after_header (bool): Whether to respect the Retry-After header in HTTP responses.
|
51
|
-
_retryable_methods (frozenset): The HTTP methods that can be retried.
|
52
|
-
_retry_status_codes (frozenset): The HTTP status codes that can be retried.
|
53
|
-
_jitter_ratio (float): The amount of jitter to add to the backoff time.
|
54
|
-
_max_backoff_wait (float): The maximum time to wait between retries in seconds.
|
148
|
+
_retry_config (RetryConfig): The retry configuration object.
|
149
|
+
_logger (Any): The logger to use for logging retries.
|
55
150
|
"""
|
56
151
|
|
57
|
-
RETRYABLE_METHODS = frozenset(["HEAD", "GET", "PUT", "DELETE", "OPTIONS", "TRACE"])
|
58
|
-
RETRYABLE_STATUS_CODES = frozenset(
|
59
|
-
[
|
60
|
-
HTTPStatus.TOO_MANY_REQUESTS,
|
61
|
-
HTTPStatus.BAD_GATEWAY,
|
62
|
-
HTTPStatus.SERVICE_UNAVAILABLE,
|
63
|
-
HTTPStatus.GATEWAY_TIMEOUT,
|
64
|
-
HTTPStatus.UNAUTHORIZED,
|
65
|
-
HTTPStatus.BAD_REQUEST,
|
66
|
-
]
|
67
|
-
)
|
68
|
-
MAX_BACKOFF_WAIT_IN_SECONDS = 60
|
69
|
-
|
70
152
|
def __init__(
|
71
153
|
self,
|
72
154
|
wrapped_transport: Union[httpx.BaseTransport, httpx.AsyncBaseTransport],
|
@@ -77,6 +159,7 @@ class RetryTransport(httpx.AsyncBaseTransport, httpx.BaseTransport):
|
|
77
159
|
respect_retry_after_header: bool = True,
|
78
160
|
retryable_methods: Iterable[str] | None = None,
|
79
161
|
retry_status_codes: Iterable[int] | None = None,
|
162
|
+
retry_config: Optional[RetryConfig] = None,
|
80
163
|
logger: Any | None = None,
|
81
164
|
) -> None:
|
82
165
|
"""
|
@@ -106,29 +189,27 @@ class RetryTransport(httpx.AsyncBaseTransport, httpx.BaseTransport):
|
|
106
189
|
retry_status_codes (Iterable[int], optional):
|
107
190
|
The HTTP status codes that can be retried.
|
108
191
|
Defaults to [429, 502, 503, 504].
|
192
|
+
retry_config (RetryConfig, optional):
|
193
|
+
Configuration for retry behavior. If not provided, uses default configuration.
|
109
194
|
logger (Any): The logger to use for logging retries.
|
110
195
|
"""
|
111
196
|
self._wrapped_transport = wrapped_transport
|
112
|
-
|
113
|
-
|
114
|
-
|
197
|
+
|
198
|
+
if retry_config is not None:
|
199
|
+
self._retry_config = retry_config
|
200
|
+
elif _RETRY_CONFIG_CALLBACK is not None:
|
201
|
+
self._retry_config = _RETRY_CONFIG_CALLBACK()
|
202
|
+
else:
|
203
|
+
self._retry_config = RetryConfig(
|
204
|
+
max_attempts=max_attempts,
|
205
|
+
max_backoff_wait=max_backoff_wait,
|
206
|
+
base_delay=base_delay,
|
207
|
+
jitter_ratio=jitter_ratio,
|
208
|
+
respect_retry_after_header=respect_retry_after_header,
|
209
|
+
retryable_methods=retryable_methods,
|
210
|
+
retry_status_codes=retry_status_codes,
|
115
211
|
)
|
116
212
|
|
117
|
-
self._max_attempts = max_attempts
|
118
|
-
self._base_delay = base_delay
|
119
|
-
self._respect_retry_after_header = respect_retry_after_header
|
120
|
-
self._retryable_methods = (
|
121
|
-
frozenset(retryable_methods)
|
122
|
-
if retryable_methods
|
123
|
-
else self.RETRYABLE_METHODS
|
124
|
-
)
|
125
|
-
self._retry_status_codes = (
|
126
|
-
frozenset(retry_status_codes)
|
127
|
-
if retry_status_codes
|
128
|
-
else self.RETRYABLE_STATUS_CODES
|
129
|
-
)
|
130
|
-
self._jitter_ratio = jitter_ratio
|
131
|
-
self._max_backoff_wait = max_backoff_wait
|
132
213
|
self._logger = logger
|
133
214
|
|
134
215
|
def handle_request(self, request: httpx.Request) -> httpx.Response:
|
@@ -206,12 +287,13 @@ class RetryTransport(httpx.AsyncBaseTransport, httpx.BaseTransport):
|
|
206
287
|
transport.close()
|
207
288
|
|
208
289
|
def _is_retryable_method(self, request: httpx.Request) -> bool:
|
209
|
-
return
|
210
|
-
|
290
|
+
return (
|
291
|
+
request.method in self._retry_config.retryable_methods
|
292
|
+
or request.extensions.get("retryable", False)
|
211
293
|
)
|
212
294
|
|
213
295
|
def _should_retry(self, response: httpx.Response) -> bool:
|
214
|
-
return response.status_code in self.
|
296
|
+
return response.status_code in self._retry_config.retry_status_codes
|
215
297
|
|
216
298
|
def _log_error(
|
217
299
|
self,
|
@@ -314,46 +396,54 @@ class RetryTransport(httpx.AsyncBaseTransport, httpx.BaseTransport):
|
|
314
396
|
)
|
315
397
|
|
316
398
|
async def _should_retry_async(self, response: httpx.Response) -> bool:
|
317
|
-
return response.status_code in self.
|
399
|
+
return response.status_code in self._retry_config.retry_status_codes
|
318
400
|
|
319
401
|
def _calculate_sleep(
|
320
402
|
self, attempts_made: int, headers: Union[httpx.Headers, Mapping[str, str]]
|
321
403
|
) -> float:
|
322
|
-
# Retry-After
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
#
|
331
|
-
|
332
|
-
|
333
|
-
if retry_after_header.isdigit():
|
334
|
-
return float(retry_after_header)
|
335
|
-
|
336
|
-
try:
|
337
|
-
parsed_date = isoparse(
|
338
|
-
retry_after_header
|
339
|
-
).astimezone() # converts to local time
|
340
|
-
diff = (parsed_date - datetime.now().astimezone()).total_seconds()
|
341
|
-
if diff > 0:
|
342
|
-
return min(diff, self._max_backoff_wait)
|
343
|
-
except ValueError:
|
344
|
-
pass
|
345
|
-
|
346
|
-
backoff = self._base_delay * (2 ** (attempts_made - 1))
|
347
|
-
jitter = (backoff * self._jitter_ratio) * random.choice([1, -1])
|
404
|
+
# Check custom retry headers first, then fall back to Retry-After
|
405
|
+
if self._retry_config.respect_retry_after_header:
|
406
|
+
for header_name in self._retry_config.retry_after_headers:
|
407
|
+
if header_value := (headers.get(header_name) or "").strip():
|
408
|
+
sleep_time = self._parse_retry_header(header_value)
|
409
|
+
if sleep_time is not None:
|
410
|
+
return min(sleep_time, self._retry_config.max_backoff_wait)
|
411
|
+
|
412
|
+
# Fall back to exponential backoff
|
413
|
+
backoff = self._retry_config.base_delay * (2 ** (attempts_made - 1))
|
414
|
+
jitter = (backoff * self._retry_config.jitter_ratio) * random.choice([1, -1])
|
348
415
|
total_backoff = backoff + jitter
|
349
|
-
return min(total_backoff, self.
|
416
|
+
return min(total_backoff, self._retry_config.max_backoff_wait)
|
417
|
+
|
418
|
+
def _parse_retry_header(self, header_value: str) -> Optional[float]:
|
419
|
+
"""Parse retry header value and return sleep time in seconds.
|
420
|
+
|
421
|
+
Args:
|
422
|
+
header_value: The header value to parse (e.g., "30", "2023-12-01T12:00:00Z")
|
423
|
+
|
424
|
+
Returns:
|
425
|
+
Sleep time in seconds if parsing succeeds, None if the header value cannot be parsed
|
426
|
+
"""
|
427
|
+
if header_value.isdigit():
|
428
|
+
return float(header_value)
|
429
|
+
|
430
|
+
try:
|
431
|
+
# Try to parse as ISO date (common for rate limit headers like X-RateLimit-Reset)
|
432
|
+
parsed_date = isoparse(header_value).astimezone()
|
433
|
+
diff = (parsed_date - datetime.now().astimezone()).total_seconds()
|
434
|
+
if diff > 0:
|
435
|
+
return diff
|
436
|
+
except ValueError:
|
437
|
+
pass
|
438
|
+
|
439
|
+
return None
|
350
440
|
|
351
441
|
async def _retry_operation_async(
|
352
442
|
self,
|
353
443
|
request: httpx.Request,
|
354
444
|
send_method: Callable[..., Coroutine[Any, Any, httpx.Response]],
|
355
445
|
) -> httpx.Response:
|
356
|
-
remaining_attempts = self.
|
446
|
+
remaining_attempts = self._retry_config.max_attempts
|
357
447
|
attempts_made = 0
|
358
448
|
response: httpx.Response | None = None
|
359
449
|
error: Exception | None = None
|
@@ -403,7 +493,7 @@ class RetryTransport(httpx.AsyncBaseTransport, httpx.BaseTransport):
|
|
403
493
|
request: httpx.Request,
|
404
494
|
send_method: Callable[..., httpx.Response],
|
405
495
|
) -> httpx.Response:
|
406
|
-
remaining_attempts = self.
|
496
|
+
remaining_attempts = self._retry_config.max_attempts
|
407
497
|
attempts_made = 0
|
408
498
|
response: httpx.Response | None = None
|
409
499
|
error: Exception | None = None
|