jf-ingest 0.0.151.dev0__tar.gz → 0.0.152.dev0__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.
Files changed (151) hide show
  1. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/PKG-INFO +1 -1
  2. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/adaptive_throttler.py +15 -6
  3. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/jf_git/clients/github.py +7 -5
  4. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/telemetry/metrics.py +15 -7
  5. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest.egg-info/PKG-INFO +1 -1
  6. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/github/test_github_gql_client.py +56 -16
  7. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.github/CODEOWNERS +0 -0
  8. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.github/workflows/semgrep.yml +0 -0
  9. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.gitignore +0 -0
  10. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.pre-commit-config.yaml +0 -0
  11. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.semgrep_rules/registry/README.md +0 -0
  12. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.semgrep_rules/registry/jellyfish.python.django.security.audit.xss.var-in-script-tag.var-in-script-tag.yml +0 -0
  13. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.semgrep_rules/registry/jellyfish.python.django.security.injection.open-redirect.open-redirect.yml +0 -0
  14. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.semgrep_rules/registry/jellyfish.python.django.security.nan-injection.nan-injection.yml +0 -0
  15. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.semgrep_rules/registry/jellyfish.python.django.security.passwords.use-none-for-password-default.use-none-for-password-default.yml +0 -0
  16. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.semgrep_rules/registry/python.django.security.audit.avoid-insecure-deserialization.avoid-insecure-deserialization.yml +0 -0
  17. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.semgrep_rules/registry/python.django.security.globals-as-template-context.globals-as-template-context.yml +0 -0
  18. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.semgrep_rules/registry/python.django.security.injection.command.command-injection-os-system.command-injection-os-system.yml +0 -0
  19. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.semgrep_rules/registry/python.django.security.injection.command.subprocess-injection.subprocess-injection.yml +0 -0
  20. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.semgrep_rules/registry/python.django.security.injection.csv-writer-injection.csv-writer-injection.yml +0 -0
  21. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.semgrep_rules/registry/python.django.security.injection.ssrf.ssrf-injection-requests.ssrf-injection-requests.yml +0 -0
  22. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.semgrep_rules/registry/python.django.security.injection.ssrf.ssrf-injection-urllib.ssrf-injection-urllib.yml +0 -0
  23. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.semgrep_rules/registry/python.django.security.injection.tainted-sql-string.tainted-sql-string.yml +0 -0
  24. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.semgrep_rules/registry/python.django.security.locals-as-template-context.locals-as-template-context.yml +0 -0
  25. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.semgrep_rules/registry/python.django.security.passwords.password-empty-string.password-empty-string.yml +0 -0
  26. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/.semgrep_rules/registry/python.lang.security.audit.eval-detected.eval-detected.yml +0 -0
  27. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/LICENSE +0 -0
  28. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/MANIFEST.in +0 -0
  29. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/README.md +0 -0
  30. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/example.yml +0 -0
  31. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/__init__.py +0 -0
  32. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/config.py +0 -0
  33. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/constants.py +0 -0
  34. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/data_manifests/__init__.py +0 -0
  35. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/data_manifests/jira/__init__.py +0 -0
  36. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/data_manifests/jira/adapters/__init__.py +0 -0
  37. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/data_manifests/jira/adapters/jira_cloud.py +0 -0
  38. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/data_manifests/jira/adapters/manifest_adapter.py +0 -0
  39. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/data_manifests/jira/generator.py +0 -0
  40. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/data_manifests/jira/manifest_base.py +0 -0
  41. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/data_manifests/manifest_base.py +0 -0
  42. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/diagnostics.py +0 -0
  43. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/events/models.py +0 -0
  44. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/events/utils.py +0 -0
  45. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/file_operations.py +0 -0
  46. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/graphql_utils.py +0 -0
  47. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/harness.py +0 -0
  48. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/jf_git/__init__.py +0 -0
  49. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/jf_git/adapters/__init__.py +0 -0
  50. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/jf_git/adapters/azure_devops.py +0 -0
  51. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/jf_git/adapters/github.py +0 -0
  52. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/jf_git/adapters/gitlab.py +0 -0
  53. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/jf_git/clients/__init__.py +0 -0
  54. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/jf_git/clients/azure_devops.py +0 -0
  55. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/jf_git/clients/gitlab.py +0 -0
  56. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/jf_git/exceptions.py +0 -0
  57. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/jf_git/standardized_models.py +0 -0
  58. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/jf_jira/__init__.py +0 -0
  59. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/jf_jira/auth.py +0 -0
  60. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/jf_jira/custom_fields.py +0 -0
  61. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/jf_jira/downloaders.py +0 -0
  62. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/jf_jira/exceptions.py +0 -0
  63. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/jf_jira/utils.py +0 -0
  64. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/logging_helper.py +0 -0
  65. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/name_redactor.py +0 -0
  66. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/regression_detector.py +0 -0
  67. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/telemetry/__init__.py +0 -0
  68. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/telemetry/tracing.py +0 -0
  69. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/utils.py +0 -0
  70. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest/validation.py +0 -0
  71. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest.egg-info/SOURCES.txt +0 -0
  72. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest.egg-info/dependency_links.txt +0 -0
  73. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest.egg-info/requires.txt +0 -0
  74. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/jf_ingest.egg-info/top_level.txt +0 -0
  75. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/mypy.ini +0 -0
  76. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/pdm.lock +0 -0
  77. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/pyproject.toml +0 -0
  78. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/run-black +0 -0
  79. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/run-isort +0 -0
  80. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/run-mypy +0 -0
  81. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/setup.cfg +0 -0
  82. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/__init__.py +0 -0
  83. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/data_manifests/__init__.py +0 -0
  84. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/data_manifests/jira/__init__.py +0 -0
  85. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/data_manifests/jira/test_generator.py +0 -0
  86. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/ado/fixtures/raw_branches.json +0 -0
  87. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/ado/fixtures/raw_commits.json +0 -0
  88. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/ado/fixtures/raw_diff.json +0 -0
  89. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/ado/fixtures/raw_graph_teams.json +0 -0
  90. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/ado/fixtures/raw_graph_users.json +0 -0
  91. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/ado/fixtures/raw_iterations.json +0 -0
  92. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/ado/fixtures/raw_prs.json +0 -0
  93. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/ado/fixtures/raw_repos.json +0 -0
  94. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/ado/fixtures/raw_threads.json +0 -0
  95. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/ado/test_ado_adapter.py +0 -0
  96. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/ado/test_ado_client.py +0 -0
  97. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/ado/test_smoke_test_ado_adapter.py +0 -0
  98. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/ado/utils.py +0 -0
  99. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/gitlab/fixtures/raw_organization_from_rest_api.json +0 -0
  100. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/gitlab/fixtures/raw_organizations_page_1.json +0 -0
  101. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/gitlab/fixtures/raw_organizations_page_2.json +0 -0
  102. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/gitlab/test_gitlab_adapter.py +0 -0
  103. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/gitlab/test_gitlab_client.py +0 -0
  104. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/gitlab/utils.py +0 -0
  105. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_git/test_git_adapter.py +0 -0
  106. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/__init__.py +0 -0
  107. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/boards.json +0 -0
  108. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/boards_empty_agile_payload.json +0 -0
  109. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/boards_sprints_for_board_1.json +0 -0
  110. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/boards_sprints_for_board_1_links.json +0 -0
  111. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/boards_sprints_not_supported.json +0 -0
  112. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/fields.json +0 -0
  113. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/issueLinkType.json +0 -0
  114. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/issues.json +0 -0
  115. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/issues_for_custom_fields.json +0 -0
  116. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/issuetype.json +0 -0
  117. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/priority.json +0 -0
  118. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/project.json +0 -0
  119. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/project_components_OJ.json +0 -0
  120. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/project_versions_JFR.json +0 -0
  121. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/project_versions_OJ.json +0 -0
  122. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/resolution.json +0 -0
  123. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/status.json +0 -0
  124. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/worklog_deleted.json +0 -0
  125. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/worklog_list.json +0 -0
  126. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/api_responses/worklog_updated.json +0 -0
  127. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/jellyfish_custom_fields.json +0 -0
  128. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/jellyfish_custom_fields_empty_response.json +0 -0
  129. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/jellyfish_issue_metadata.json +0 -0
  130. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/fixtures/results/project_version_component.json +0 -0
  131. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/test_custom_fields_comparison.py +0 -0
  132. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/test_custom_fields_jellyfish_retrieval.py +0 -0
  133. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/test_jira_auth.py +0 -0
  134. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/test_jira_download_boards_and_sprints.py +0 -0
  135. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/test_jira_download_fields.py +0 -0
  136. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/test_jira_download_issuelinktype.py +0 -0
  137. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/test_jira_download_issuetype.py +0 -0
  138. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/test_jira_download_priority.py +0 -0
  139. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/test_jira_download_projects_and_versions.py +0 -0
  140. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/test_jira_download_resolutions.py +0 -0
  141. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/test_jira_download_status.py +0 -0
  142. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/test_jira_download_users.py +0 -0
  143. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/test_jira_issues.py +0 -0
  144. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/test_jira_worklogs.py +0 -0
  145. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/jf_jira/utils.py +0 -0
  146. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/test_adaptive_throttler.py +0 -0
  147. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/test_file_operations.py +0 -0
  148. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/utils/__init__.py +0 -0
  149. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/utils/test_diagnostics.py +0 -0
  150. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/utils/test_logging_helper.py +0 -0
  151. {jf_ingest-0.0.151.dev0 → jf_ingest-0.0.152.dev0}/tests/utils/test_util_functions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jf-ingest
3
- Version: 0.0.151.dev0
3
+ Version: 0.0.152.dev0
4
4
  Summary: library used for ingesting jira data
5
5
  Author-email: jellyfish-oss <oss@jellyfish.co>
6
6
  License: MIT
@@ -219,7 +219,8 @@ class AdaptiveThrottler:
219
219
 
220
220
  with self._lock:
221
221
  self._response_time_histogram.measure(
222
- value=response_time, attributes=self.metrics_attrs
222
+ value=response_time,
223
+ attributes=self.metrics_attrs,
223
224
  )
224
225
  self._total_requests_counter.add(amount=1, attributes=self.metrics_attrs)
225
226
 
@@ -248,10 +249,14 @@ class AdaptiveThrottler:
248
249
  )
249
250
 
250
251
  self._request_rate_gauge.measure(
251
- value=self._request_rate, attributes=self.metrics_attrs
252
+ value=self._request_rate,
253
+ attributes=self.metrics_attrs,
254
+ record_immediate=True,
252
255
  )
253
- self._backoff_counter.add(amount=1, attributes=self.metrics_attrs)
254
- self._backoff_time_histogram.measure(value=backoff_time)
256
+ self._backoff_counter.add(
257
+ amount=1, attributes=self.metrics_attrs, record_immediate=True
258
+ )
259
+ self._backoff_time_histogram.measure(value=backoff_time, record_immediate=True)
255
260
 
256
261
  time.sleep(backoff_time)
257
262
  elif self._request_rate < self._initial_request_rate:
@@ -272,9 +277,13 @@ class AdaptiveThrottler:
272
277
  self._response_within_threshold_counter = 0
273
278
 
274
279
  self._request_rate_gauge.measure(
275
- value=self._request_rate, attributes=self.metrics_attrs
280
+ value=self._request_rate,
281
+ attributes=self.metrics_attrs,
282
+ record_immediate=True,
283
+ )
284
+ self._reverse_backoff_counter.add(
285
+ amount=1, attributes=self.metrics_attrs, record_immediate=True
276
286
  )
277
- self._reverse_backoff_counter.add(amount=1, attributes=self.metrics_attrs)
278
287
 
279
288
  send_to_agent_log_file(
280
289
  f"Request rate increased to {self._request_rate:.4f} rps",
@@ -34,6 +34,8 @@ GIT_DEFAULT_API_ENDPOINT = 'https://api.github.com'
34
34
 
35
35
  GQL_RATE_LIMIT_QUERY_BLOCK = "rateLimit {remaining, resetAt}"
36
36
 
37
+ GITHUB_STATUSES_TO_RETRY_ON = tuple(list(DEFAULT_HTTP_CODES_TO_RETRY_ON) + [403])
38
+
37
39
 
38
40
  def parse_date(dt):
39
41
  if dt is None:
@@ -237,10 +239,10 @@ class GithubClient:
237
239
  if e.response.status_code == 401:
238
240
  self._update_token()
239
241
  continue
240
- # We can get transient 403 level errors that have to do with rate limiting,
242
+ # We can get transient errors that have to do with rate limiting,
241
243
  # but aren't directly related to the above GqlRateLimitedExceptionInner logic.
242
244
  # Do a simple retry loop here
243
- elif e.response.status_code == 403:
245
+ elif e.response.status_code in GITHUB_STATUSES_TO_RETRY_ON:
244
246
  pass
245
247
  else:
246
248
  raise
@@ -452,7 +454,7 @@ class GithubClient:
452
454
  # HACK: A 403 appears to happen after we have been
453
455
  # rate-limited when hitting certain URLs. Add 403s
454
456
  # to HTTP Codes to retry
455
- statuses_to_retry = list(DEFAULT_HTTP_CODES_TO_RETRY_ON) + [403]
457
+ statuses_to_retry = GITHUB_STATUSES_TO_RETRY_ON
456
458
  result = retry_for_status(self.session.get, url, statuses_to_retry=statuses_to_retry)
457
459
  result.raise_for_status()
458
460
  return result.json()
@@ -471,7 +473,7 @@ class GithubClient:
471
473
  # HACK: A 403 appears to happen after we have been
472
474
  # rate-limited when hitting certain URLs. Add 403s
473
475
  # to HTTP Codes to retry
474
- statuses_to_retry = list(DEFAULT_HTTP_CODES_TO_RETRY_ON) + [403]
476
+ statuses_to_retry = GITHUB_STATUSES_TO_RETRY_ON
475
477
  result: requests.Response = retry_for_status(
476
478
  self.session.get, url, statuses_to_retry=statuses_to_retry
477
479
  )
@@ -495,7 +497,7 @@ class GithubClient:
495
497
  # HACK: A 403 appears to happen after we have been
496
498
  # rate-limited when hitting certain URLs. Add 403s
497
499
  # to HTTP Codes to retry
498
- statuses_to_retry = list(DEFAULT_HTTP_CODES_TO_RETRY_ON) + [403]
500
+ statuses_to_retry = GITHUB_STATUSES_TO_RETRY_ON
499
501
  while True:
500
502
  url = f'{self.rest_api_url}/repos/{org_login}/{repo_name}/labels?per_page={page_size}&page={page_number}'
501
503
  result = retry_for_status(self.session.get, url, statuses_to_retry=statuses_to_retry)
@@ -42,19 +42,28 @@ class JellyMetricBase:
42
42
  if not _METRIC_EXPORTER:
43
43
  logger.info("No exporter configured. Using ConsoleMetricExporter for NoOp metrics.")
44
44
  self._reader = PeriodicExportingMetricReader(ConsoleMetricExporter())
45
- self._provider: Union[MeterProvider, NoOpMeterProvider] = NoOpMeterProvider()
45
+ self._provider = NoOpMeterProvider()
46
46
  else:
47
47
  self._reader = PeriodicExportingMetricReader(_METRIC_EXPORTER)
48
48
  self._provider = MeterProvider(metric_readers=[self._reader])
49
49
 
50
- atexit.register(self.shutdown)
50
+ atexit.register(self.shutdown_reader)
51
51
 
52
52
  def _get_meter(self):
53
53
  return self._provider.get_meter(__name__)
54
54
 
55
- def shutdown(self):
56
- logger.info("Shutting down metrics reader and exporting all pending metrics.")
57
- self._reader.shutdown()
55
+ def shutdown_reader(self):
56
+ """Ensures the reader is flushed and shutdown at exit."""
57
+ current_log_level = logger.level
58
+
59
+ # The OTel library will log a warning message if .shutdown is called on a shutdown reader, but there isn't any
60
+ # way to check if the reader is already shutdown. This is a workaround to suppress the warning message.
61
+ try:
62
+ logger.setLevel(logging.ERROR)
63
+ self._reader.force_flush()
64
+ self._reader.shutdown()
65
+ finally:
66
+ logger.setLevel(current_log_level)
58
67
 
59
68
 
60
69
  class JellyGauge(JellyMetricBase):
@@ -98,7 +107,6 @@ class JellyGauge(JellyMetricBase):
98
107
  Set record_immediate to be true if you want to immediately flush the reader after this metric is collected -- should only
99
108
  be used in short-lived process situations.
100
109
  """
101
-
102
110
  logger.debug(
103
111
  f"Received measure call for {self._name}, reporting measurement {value}, {attributes}"
104
112
  )
@@ -108,6 +116,7 @@ class JellyGauge(JellyMetricBase):
108
116
  self._internal_gauge = self._meter.create_observable_gauge(
109
117
  name=self._name, callbacks=[self._callback]
110
118
  )
119
+
111
120
  if record_immediate:
112
121
  self._reader.collect()
113
122
 
@@ -115,7 +124,6 @@ class JellyGauge(JellyMetricBase):
115
124
  """
116
125
  Callback function for async gauge. Clears the observations as they're reported.
117
126
  """
118
-
119
127
  logger.debug(
120
128
  f"Received callback function call for {self._name}, reporting observations {self._observations}"
121
129
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jf-ingest
3
- Version: 0.0.151.dev0
3
+ Version: 0.0.152.dev0
4
4
  Summary: library used for ingesting jira data
5
5
  Author-email: jellyfish-oss <oss@jellyfish.co>
6
6
  License: MIT
@@ -21,6 +21,13 @@ TEST_BASE_URL = 'https://gav.com/gql'
21
21
  GQL_DATETIME_STR = "2024-12-29T12:00:58Z"
22
22
 
23
23
 
24
+ @pytest.fixture()
25
+ def client() -> GithubClient:
26
+ # Mock out a Github GQL Client
27
+ github_auth_config = GithubAuthConfig(company_slug='a company', base_url=TEST_BASE_URL, token='test')
28
+ return GithubClient(github_auth_config)
29
+
30
+
24
31
  def test_gql_page_results_block():
25
32
  """This is a sanity check that the GQL_PAGE_INFO_BLOCK and GQL_RATE_LIMIT_QUERY_BLOCK
26
33
  are valid. This are pretty vague smoke tests... it's unlikely anybody would mess with
@@ -53,12 +60,9 @@ def test_git_auth_config_base_url():
53
60
  assert client.rest_api_url == github_api_url
54
61
  assert client.gql_base_url == f'{github_api_url}/graphql'
55
62
 
56
- def test_get_rate_limit(requests_mock: requests_mock.Mocker):
63
+ def test_get_rate_limit(requests_mock: requests_mock.Mocker, client: GithubClient):
57
64
  # Mock classes/data
58
65
  session = requests.Session()
59
- # Mock out a Github GQL Client
60
- github_auth_config = GithubAuthConfig(company_slug='a company', base_url=TEST_BASE_URL, session=session, token='test')
61
- client = GithubClient(github_auth_config)
62
66
  client.session = session
63
67
 
64
68
  def _test_get_rate_limit_inner(remaining_rate_limit: int, reset_at_rate_limit_str: str):
@@ -89,7 +93,7 @@ def test_get_rate_limit(requests_mock: requests_mock.Mocker):
89
93
  _test_get_rate_limit_inner('cat', GQL_DATETIME_STR)
90
94
 
91
95
 
92
- def test_get_raw_gql_result_simple(requests_mock: requests_mock.Mocker):
96
+ def test_get_raw_gql_result_simple(requests_mock: requests_mock.Mocker, client: GithubClient):
93
97
  # Test Data
94
98
  test_query_body = "test_query_body"
95
99
  # Mock classes/data
@@ -97,9 +101,6 @@ def test_get_raw_gql_result_simple(requests_mock: requests_mock.Mocker):
97
101
  session_mock_caller_data = {'query': test_query_body}
98
102
  session_mock_return_data = {'data': {'test': {'remaining': 'test', 'resetAt': 'test'}}}
99
103
  requests_mock.post(url=f'{TEST_BASE_URL}/graphql', json=session_mock_return_data)
100
- # Mock out a Github GQL Client
101
- github_auth_config = GithubAuthConfig(company_slug='a company', base_url=TEST_BASE_URL, session=session, token='test')
102
- client = GithubClient(github_auth_config)
103
104
  client.session = session
104
105
 
105
106
  # call function
@@ -109,13 +110,10 @@ def test_get_raw_gql_result_simple(requests_mock: requests_mock.Mocker):
109
110
  assert requests_mock.last_request.json() == session_mock_caller_data
110
111
 
111
112
 
112
- def test_get_raw_gql_result_force_error_raise(mocker, requests_mock: requests_mock.Mocker):
113
+ def test_get_raw_gql_result_force_error_raise(mocker, requests_mock: requests_mock.Mocker, client: GithubClient):
113
114
  mocker.patch('time.sleep', return_value=None)
114
115
  # Mock classes/data
115
116
  session = requests.Session()
116
- # Mock out a Github GQL Client
117
- github_auth_config = GithubAuthConfig(company_slug='A Company', base_url=TEST_BASE_URL, session=session, token='test')
118
- client = GithubClient(github_auth_config)
119
117
  client.session = session
120
118
 
121
119
  requests_mock.post(
@@ -153,7 +151,7 @@ def test_get_raw_gql_result_force_error_raise(mocker, requests_mock: requests_mo
153
151
  client.get_raw_result(query_body='', max_attempts=2)
154
152
 
155
153
 
156
- def test_get_raw_gql_result_successful_retry(mocker, requests_mock: requests_mock.Mocker):
154
+ def test_get_raw_gql_result_successful_retry(mocker, requests_mock: requests_mock.Mocker, client: GithubClient):
157
155
  mocker.patch('time.sleep', return_value=None)
158
156
  session = requests.Session()
159
157
  successful_data = {'success': True}
@@ -169,15 +167,57 @@ def test_get_raw_gql_result_successful_retry(mocker, requests_mock: requests_moc
169
167
  ],
170
168
  )
171
169
 
172
- # Mock out a Github GQL Client
173
- github_auth_config = GithubAuthConfig(company_slug='A Company', base_url=TEST_BASE_URL, session=session, token='test')
174
- client = GithubClient(github_auth_config)
175
170
  client.session = session
176
171
 
177
172
  data = client.get_raw_result(query_body='', max_attempts=10)
178
173
  assert data == successful_data
179
174
 
180
175
 
176
+ def test_github_client_error_raise_403(mocker, requests_mock: requests_mock.Mocker, client: GithubClient):
177
+ mocker.patch('time.sleep', return_value=None)
178
+ session = requests.Session()
179
+ successful_data = {'success': True}
180
+ requests_mock.post(
181
+ url=f'{TEST_BASE_URL}/graphql',
182
+ response_list=[
183
+ {'status_code': 403},
184
+ ],
185
+ )
186
+
187
+ # Mock out a Github GQL Client
188
+ client.session = session
189
+
190
+ retry_count = 10
191
+ with pytest.raises(requests.HTTPError):
192
+ client.get_raw_result(query_body='', max_attempts=retry_count)
193
+ assert requests_mock.call_count == retry_count
194
+
195
+ def test_github_client_error_resolves_after_retry(mocker, requests_mock: requests_mock.Mocker, client: GithubClient):
196
+ mocker.patch('time.sleep', return_value=None)
197
+ session = requests.Session()
198
+ successful_data = {'mantra': 'never give UP!'}
199
+ response_list = [
200
+ # A Beautiful Error Medley
201
+ {'status_code': 403},
202
+ {'status_code': 429},
203
+ {'status_code': 500},
204
+ {'status_code': 502},
205
+ {'status_code': 503},
206
+ {'status_code': 504},
207
+ {'json': successful_data}
208
+ ]
209
+ retry_count = len(response_list)
210
+ requests_mock.post(
211
+ url=f'{TEST_BASE_URL}/graphql',
212
+ response_list=response_list,
213
+ )
214
+
215
+ # Mock out a Github GQL Client
216
+ client.session = session
217
+
218
+ assert client.get_raw_result(query_body='', max_attempts=retry_count) == successful_data
219
+ assert requests_mock.call_count == retry_count
220
+
181
221
  def test_page_results_gql(mocker, requests_mock: requests_mock.Mocker):
182
222
  session = requests.Session()
183
223
  # Mock out a Github GQL Client