qontract-reconcile 0.10.1rc193__py3-none-any.whl → 0.10.1rc195__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qontract-reconcile
3
- Version: 0.10.1rc193
3
+ Version: 0.10.1rc195
4
4
  Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
5
5
  Home-page: https://github.com/app-sre/qontract-reconcile
6
6
  Author: Red Hat App-SRE Team
@@ -41,7 +41,7 @@ reconcile/jenkins_webhooks.py,sha256=j8vhJMWcRhOdc9XzRSm0CPj84jsF3e4Syjm7r1BIsDE
41
41
  reconcile/jenkins_webhooks_cleaner.py,sha256=JsN_NVPfZJwv1JtSzZXDIHUqGiefL-DRffFnDGau9aY,1539
42
42
  reconcile/jenkins_worker_fleets.py,sha256=nwlsLSly85RukBGCVsEpoc92BVxvCjJetFlpNC5OwPc,5282
43
43
  reconcile/jira_permissions_validator.py,sha256=Iul5-2_QgQ8joGfP542UQYA0Y5Qm5chvdRzUltCo_yM,1434
44
- reconcile/jira_watcher.py,sha256=epeKY1ljMMKw1lTS50cIwCefEiSBtcywnejCNzasdfU,3653
44
+ reconcile/jira_watcher.py,sha256=CsSipmf7ThuNzj9Xy41vedPwJ-axxy47OPXMCZqZyks,5634
45
45
  reconcile/ldap_users.py,sha256=uEWQ0V41tN9KCZi4ZKPamjrJ6djSpdpvDBo7yJ0e7ZI,3008
46
46
  reconcile/mr_client_gateway.py,sha256=WhjMd-sIXDFCV8-rt8CEjurJ5OYB1pOD0K3o0tZRXQg,1885
47
47
  reconcile/ocm_additional_routers.py,sha256=KfcFDVbNoc6n5dHWjYdAf1_DiVqVG6Tw23WLKoV8cdg,3306
@@ -87,7 +87,7 @@ reconcile/quay_mirror.py,sha256=dOKvUeiXXs8ryqLSxgOITQPMOnx0bTZakFKb4UqhaQc,1340
87
87
  reconcile/quay_mirror_org.py,sha256=E1OdRe-ppxTkNCwu20iVRhEdG1fPDBroLY02NgiMN7c,10381
88
88
  reconcile/quay_permissions.py,sha256=_3PCWjNWoU7VHlYgHzUevvL_jJmEMsWfXV_nzjeiyhU,4099
89
89
  reconcile/quay_repos.py,sha256=7609RBVQihis96FNOOe-i9tCTYwcTVy4WpKAL6HpnkU,7031
90
- reconcile/queries.py,sha256=_UPrClhUh5c7fwzW3984RhLxq3XziGZ-GvWDiz7DbAE,48876
90
+ reconcile/queries.py,sha256=F6Q5RRWovTGh_s7hiqKThttjhYySLPjLSJn5HdaJ864,47914
91
91
  reconcile/query_validator.py,sha256=oLEZIAsQCzxmmZ7b9dSw-OKuEjpI1dbVu4XfCfjpmi8,1503
92
92
  reconcile/requests_sender.py,sha256=914iluuF4UVgG3VyxxtnHOu4yf6YKS2fIy6PViSsFTQ,3875
93
93
  reconcile/resource_scraper.py,sha256=vo1N9vLJCYWvXlTwFRIpEuWjx_39ZV9zxJlpoPq4g3U,2330
@@ -212,6 +212,8 @@ reconcile/gql_definitions/jenkins_configs/__init__.py,sha256=47DEQpj8HBSa-_TImW-
212
212
  reconcile/gql_definitions/jenkins_configs/jenkins_configs.py,sha256=8U4S1iRIQ86VTrKGd5k0xgi-DlwhYKOlevK_OWIKC98,2784
213
213
  reconcile/gql_definitions/jira_permissions_validator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
214
214
  reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py,sha256=5iHCIIfZYKopghfc7-BpoG2uvxNrm_qYwzdsRI_kVig,2266
215
+ reconcile/gql_definitions/jira_watcher/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
216
+ reconcile/gql_definitions/jira_watcher/jira_watcher_boards.py,sha256=BNB4DHdviKC3dKRQ3Dzs6P-mHWUfkRK7KlQ90GhuK4o,4011
215
217
  reconcile/gql_definitions/jumphosts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
216
218
  reconcile/gql_definitions/jumphosts/jumphosts.py,sha256=V0K6V9_O8LFztCcBtJfaZ9PMkpXF2N43ju_R1ajTzho,2384
217
219
  reconcile/gql_definitions/ocp_release_mirror/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -509,7 +511,7 @@ reconcile/utils/imap_client.py,sha256=byFAJATbITJPsGECSbvXBOcCnoeTUpDFiEjzOAxLm_
509
511
  reconcile/utils/instrumented_wrappers.py,sha256=eVwMoa6FCrYxLv3RML3WpZF9qKVfCTjMxphgVXG03OM,1073
510
512
  reconcile/utils/jenkins_api.py,sha256=BlHOUDG3Nn5K4qPU-js2ewV_IhciISfCnQwfaZlewu8,7054
511
513
  reconcile/utils/jinja2_ext.py,sha256=l628RR9r9dAGBWLVegoCbSqnjojeizNGiq9Cstt02nE,1129
512
- reconcile/utils/jira_client.py,sha256=pQw4LKZL5d-Guaj4BMiIVrL6EZsmAMYII1-b8xYZ8Yk,5021
514
+ reconcile/utils/jira_client.py,sha256=r5KsAD-Kxj6pUbwhlq1ME_B_tbgkC5vwf4w_Nk_dksI,5022
513
515
  reconcile/utils/jjb_client.py,sha256=bj40UNRbvQ93K-1tndzJI6Drt_sebRXq-qUpfDx2ozw,14496
514
516
  reconcile/utils/jsonpath.py,sha256=NRpAEijKN4cMDjo7qivNPqpm0__GQQ1TiE0PBEBO45s,5572
515
517
  reconcile/utils/jump_host.py,sha256=yJZBRnoCJ-fknhT9f_cli_1J5hBRh1PsRVDyDWpjhws,4935
@@ -523,7 +525,7 @@ reconcile/utils/oc.py,sha256=AIkBSXLnsJCE_9HCMN66-QK359pMBNPs4Q2XPI9zjyU,64780
523
525
  reconcile/utils/oc_connection_parameters.py,sha256=mUP0WtpozdFDufcdNKy2OGweRvI3Faq8YLiR0ngJYes,9799
524
526
  reconcile/utils/oc_filters.py,sha256=RWn8pC5A7ZZT7C6WPq9bOw5KBNkiAb5puFSr_FpdAf8,1358
525
527
  reconcile/utils/oc_map.py,sha256=nT69J5pdPeIDnIYjD9fwY6GkE3BMQCf-AF0rmHJuUNw,9068
526
- reconcile/utils/ocm_base_client.py,sha256=bUE9uUJVVwHaNNKdL86pIA_GnpHraXvtS-3KMRmszps,5177
528
+ reconcile/utils/ocm_base_client.py,sha256=7WHzSQ2j3FhZ1d8DA9v2szfg0tpzavfGCaVgGkvoquY,5003
527
529
  reconcile/utils/openshift_resource.py,sha256=fLqURJcjrg8kd9jxbJ91fJBB5woiB4KT_wMKyADzffo,24214
528
530
  reconcile/utils/openssl.py,sha256=QVvhzhpChq_4Daf_5wE1qeZJr4thg3DDjJPn4bOPD4E,365
529
531
  reconcile/utils/output.py,sha256=htcMXMe0y2dNnwu8MW8x0JZ5YBaEJRy5w4tR8yLF0OU,1738
@@ -612,8 +614,8 @@ tools/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
612
614
  tools/test/test_qontract_cli.py,sha256=awwTHEc2DWlykuqGIYM0WOBoSL0KRnOraCLk3C7izis,1401
613
615
  tools/test/test_sd_app_sre_alert_report.py,sha256=JeLhgzpKCPgLvptwg_4ZvJHLVWKNG1T5845HXTkMBxA,1826
614
616
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
615
- qontract_reconcile-0.10.1rc193.dist-info/METADATA,sha256=sC7-hMIzVi8e6YolhzhJwX5zi-t-xPd6RjWyXH1kh50,2320
616
- qontract_reconcile-0.10.1rc193.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
617
- qontract_reconcile-0.10.1rc193.dist-info/entry_points.txt,sha256=Af70EWPJxsTiCNF6gA-pWdw1A0Heqn-PZF-oBc5NmiU,302
618
- qontract_reconcile-0.10.1rc193.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
619
- qontract_reconcile-0.10.1rc193.dist-info/RECORD,,
617
+ qontract_reconcile-0.10.1rc195.dist-info/METADATA,sha256=PUU-Wdx0R2NJQ6PZRfei54GtnyNCp2GZsMVHQn2PXvg,2320
618
+ qontract_reconcile-0.10.1rc195.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
619
+ qontract_reconcile-0.10.1rc195.dist-info/entry_points.txt,sha256=Af70EWPJxsTiCNF6gA-pWdw1A0Heqn-PZF-oBc5NmiU,302
620
+ qontract_reconcile-0.10.1rc195.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
621
+ qontract_reconcile-0.10.1rc195.dist-info/RECORD,,
File without changes
@@ -0,0 +1,149 @@
1
+ """
2
+ Generated by qenerate plugin=pydantic_v1. DO NOT MODIFY MANUALLY!
3
+ """
4
+ from collections.abc import Callable # noqa: F401 # pylint: disable=W0611
5
+ from datetime import datetime # noqa: F401 # pylint: disable=W0611
6
+ from enum import Enum # noqa: F401 # pylint: disable=W0611
7
+ from typing import ( # noqa: F401 # pylint: disable=W0611
8
+ Any,
9
+ Optional,
10
+ Union,
11
+ )
12
+
13
+ from pydantic import ( # noqa: F401 # pylint: disable=W0611
14
+ BaseModel,
15
+ Extra,
16
+ Field,
17
+ Json,
18
+ )
19
+
20
+ from reconcile.gql_definitions.fragments.vault_secret import VaultSecret
21
+
22
+
23
+ DEFINITION = """
24
+ fragment VaultSecret on VaultSecret_v1 {
25
+ path
26
+ field
27
+ version
28
+ format
29
+ }
30
+
31
+ query JiraWatcherBoards {
32
+ jira_boards: jira_boards_v1 {
33
+ path
34
+ name
35
+ server {
36
+ serverUrl
37
+ token {
38
+ ... VaultSecret
39
+ }
40
+ }
41
+ slack {
42
+ workspace {
43
+ name
44
+ integrations {
45
+ name
46
+ token {
47
+ ... VaultSecret
48
+ }
49
+ channel
50
+ icon_emoji
51
+ username
52
+ }
53
+ api_client {
54
+ global {
55
+ max_retries
56
+ timeout
57
+ }
58
+ methods {
59
+ name
60
+ args
61
+ }
62
+ }
63
+ }
64
+ channel
65
+ }
66
+ }
67
+ }
68
+ """
69
+
70
+
71
+ class ConfiguredBaseModel(BaseModel):
72
+ class Config:
73
+ smart_union = True
74
+ extra = Extra.forbid
75
+
76
+
77
+ class JiraServerV1(ConfiguredBaseModel):
78
+ server_url: str = Field(..., alias="serverUrl")
79
+ token: VaultSecret = Field(..., alias="token")
80
+
81
+
82
+ class SlackWorkspaceIntegrationV1(ConfiguredBaseModel):
83
+ name: str = Field(..., alias="name")
84
+ token: VaultSecret = Field(..., alias="token")
85
+ channel: str = Field(..., alias="channel")
86
+ icon_emoji: str = Field(..., alias="icon_emoji")
87
+ username: str = Field(..., alias="username")
88
+
89
+
90
+ class SlackWorkspaceApiClientGlobalConfigV1(ConfiguredBaseModel):
91
+ max_retries: Optional[int] = Field(..., alias="max_retries")
92
+ timeout: Optional[int] = Field(..., alias="timeout")
93
+
94
+
95
+ class SlackWorkspaceApiClientMethodConfigV1(ConfiguredBaseModel):
96
+ name: str = Field(..., alias="name")
97
+ args: Json = Field(..., alias="args")
98
+
99
+
100
+ class SlackWorkspaceApiClientV1(ConfiguredBaseModel):
101
+ q_global: Optional[SlackWorkspaceApiClientGlobalConfigV1] = Field(
102
+ ..., alias="global"
103
+ )
104
+ methods: Optional[list[SlackWorkspaceApiClientMethodConfigV1]] = Field(
105
+ ..., alias="methods"
106
+ )
107
+
108
+
109
+ class SlackWorkspaceV1(ConfiguredBaseModel):
110
+ name: str = Field(..., alias="name")
111
+ integrations: Optional[list[SlackWorkspaceIntegrationV1]] = Field(
112
+ ..., alias="integrations"
113
+ )
114
+ api_client: Optional[SlackWorkspaceApiClientV1] = Field(..., alias="api_client")
115
+
116
+
117
+ class SlackOutputV1(ConfiguredBaseModel):
118
+ workspace: SlackWorkspaceV1 = Field(..., alias="workspace")
119
+ channel: Optional[str] = Field(..., alias="channel")
120
+
121
+
122
+ class JiraBoardV1(ConfiguredBaseModel):
123
+ path: str = Field(..., alias="path")
124
+ name: str = Field(..., alias="name")
125
+ server: JiraServerV1 = Field(..., alias="server")
126
+ slack: Optional[SlackOutputV1] = Field(..., alias="slack")
127
+
128
+
129
+ class JiraWatcherBoardsQueryData(ConfiguredBaseModel):
130
+ jira_boards: Optional[list[JiraBoardV1]] = Field(..., alias="jira_boards")
131
+
132
+
133
+ def query(query_func: Callable, **kwargs: Any) -> JiraWatcherBoardsQueryData:
134
+ """
135
+ This is a convenience function which queries and parses the data into
136
+ concrete types. It should be compatible with most GQL clients.
137
+ You do not have to use it to consume the generated data classes.
138
+ Alternatively, you can also mime and alternate the behavior
139
+ of this function in the caller.
140
+
141
+ Parameters:
142
+ query_func (Callable): Function which queries your GQL Server
143
+ kwargs: optional arguments that will be passed to the query function
144
+
145
+ Returns:
146
+ JiraWatcherBoardsQueryData: queried data parsed into generated classes
147
+ """
148
+ raw_data: dict[Any, Any] = query_func(DEFINITION, **kwargs)
149
+ return JiraWatcherBoardsQueryData(**raw_data)
reconcile/jira_watcher.py CHANGED
@@ -1,10 +1,34 @@
1
1
  import logging
2
+ from collections.abc import (
3
+ Callable,
4
+ Iterable,
5
+ Mapping,
6
+ )
7
+ from typing import Optional
2
8
 
3
- from reconcile import queries
4
- from reconcile.slack_base import slackapi_from_slack_workspace
9
+ from reconcile.gql_definitions.common.jira_settings import AppInterfaceSettingsV1
10
+ from reconcile.gql_definitions.jira_watcher.jira_watcher_boards import (
11
+ JiraBoardV1,
12
+ SlackOutputV1,
13
+ )
14
+ from reconcile.gql_definitions.jira_watcher.jira_watcher_boards import (
15
+ query as query_jira_boards,
16
+ )
17
+ from reconcile.slack_base import (
18
+ SlackApi,
19
+ slackapi_from_slack_workspace,
20
+ )
21
+ from reconcile.typed_queries.app_interface_vault_settings import (
22
+ get_app_interface_vault_settings,
23
+ )
24
+ from reconcile.typed_queries.jira_settings import get_jira_settings
25
+ from reconcile.utils import gql
5
26
  from reconcile.utils.defer import defer
6
27
  from reconcile.utils.jira_client import JiraClient
7
- from reconcile.utils.secret_reader import SecretReader
28
+ from reconcile.utils.secret_reader import (
29
+ SecretReaderBase,
30
+ create_secret_reader,
31
+ )
8
32
  from reconcile.utils.sharding import is_in_shard_round_robin
9
33
  from reconcile.utils.state import (
10
34
  State,
@@ -14,8 +38,18 @@ from reconcile.utils.state import (
14
38
  QONTRACT_INTEGRATION = "jira-watcher"
15
39
 
16
40
 
17
- def fetch_current_state(jira_board, settings):
18
- jira = JiraClient(jira_board, settings=settings)
41
+ def fetch_current_state(
42
+ jira_board: JiraBoardV1,
43
+ settings: AppInterfaceSettingsV1,
44
+ secret_reader: SecretReaderBase,
45
+ ) -> tuple[JiraClient, dict[str, dict]]:
46
+ token = secret_reader.read_secret(jira_board.server.token)
47
+ jira = JiraClient.create(
48
+ project_name=jira_board.name,
49
+ token=token,
50
+ server_url=jira_board.server.server_url,
51
+ jira_watcher_settings=settings.jira_watcher,
52
+ )
19
53
  issues = jira.get_issues(fields=["key", "status", "summary"])
20
54
  return jira, {
21
55
  issue.key: {"status": issue.fields.status.name, "summary": issue.fields.summary}
@@ -23,11 +57,18 @@ def fetch_current_state(jira_board, settings):
23
57
  }
24
58
 
25
59
 
26
- def fetch_previous_state(state, project):
60
+ def fetch_previous_state(state: State, project: str) -> dict[str, dict]:
27
61
  return state.get(project, {})
28
62
 
29
63
 
30
- def format_message(server, key, data, event, previous_state=None, current_state=None):
64
+ def format_message(
65
+ server: str,
66
+ key: str,
67
+ data: Mapping,
68
+ event: str,
69
+ previous_state: Optional[Mapping] = None,
70
+ current_state: Optional[Mapping] = None,
71
+ ) -> str:
31
72
  summary = data["summary"]
32
73
  info = (
33
74
  ": {} -> {}".format(previous_state["status"], current_state["status"])
@@ -38,8 +79,12 @@ def format_message(server, key, data, event, previous_state=None, current_state=
38
79
  return "{} ({}) {}{}".format(url, summary, event, info)
39
80
 
40
81
 
41
- def calculate_diff(server, current_state, previous_state):
42
- messages = []
82
+ def calculate_diff(
83
+ server: str,
84
+ current_state: Mapping[str, Mapping],
85
+ previous_state: Mapping[str, Mapping],
86
+ ) -> list[str]:
87
+ messages: list[str] = []
43
88
  new_issues = [
44
89
  format_message(server, key, data, "created")
45
90
  for key, data in current_state.items()
@@ -66,52 +111,74 @@ def calculate_diff(server, current_state, previous_state):
66
111
  return messages
67
112
 
68
113
 
69
- def init_slack(jira_board):
70
- secret_reader = SecretReader(queries.get_secret_reader_settings())
71
- slack_info = jira_board["slack"]
72
-
114
+ def init_slack(slack: SlackOutputV1, secret_reader: SecretReaderBase) -> SlackApi:
73
115
  return slackapi_from_slack_workspace(
74
- slack_info,
116
+ slack.dict(by_alias=True),
75
117
  secret_reader,
76
118
  QONTRACT_INTEGRATION,
77
- channel=slack_info.get("channel"),
119
+ channel=slack.channel,
78
120
  init_usergroups=False,
79
121
  )
80
122
 
81
123
 
82
- def act(dry_run, jira_board, diffs):
124
+ def act(
125
+ dry_run: bool,
126
+ slack: SlackOutputV1,
127
+ diffs: Iterable[str],
128
+ secret_reader: SecretReaderBase,
129
+ ) -> None:
130
+ slack_api: Optional[SlackApi] = None
83
131
  if not dry_run and diffs:
84
- slack = init_slack(jira_board)
132
+ slack_api = init_slack(slack=slack, secret_reader=secret_reader)
85
133
 
86
- for diff in reversed(diffs):
134
+ for diff in reversed(list(diffs)):
87
135
  logging.info(diff)
88
136
  if not dry_run:
89
- slack.chat_post_message(diff)
137
+ if not slack_api:
138
+ raise RuntimeError("Slack API not initialized")
139
+ slack_api.chat_post_message(diff)
90
140
 
91
141
 
92
- def write_state(state: State, project, state_to_write):
142
+ def write_state(
143
+ state: State, project: str, state_to_write: Mapping[str, Mapping]
144
+ ) -> None:
93
145
  state.add(project, value=state_to_write, force=True)
94
146
 
95
147
 
96
148
  @defer
97
- def run(dry_run, defer):
98
- jira_boards = [j for j in queries.get_jira_boards() if j.get("slack")]
99
- settings = queries.get_app_interface_settings()
149
+ def run(dry_run: bool, defer: Optional[Callable]) -> None:
150
+ gql_api = gql.get_api()
151
+ vault_settings = get_app_interface_vault_settings()
152
+ secret_reader = create_secret_reader(use_vault=vault_settings.vault)
153
+ jira_boards = query_jira_boards(query_func=gql_api.query).jira_boards or []
154
+ settings = get_jira_settings(gql_api=gql_api)
100
155
  state = init_state(integration=QONTRACT_INTEGRATION)
101
- defer(state.cleanup)
156
+ if defer:
157
+ defer(state.cleanup)
102
158
  for index, jira_board in enumerate(jira_boards):
103
- if not is_in_shard_round_robin(jira_board["name"], index):
159
+ if not jira_board.slack:
160
+ continue
161
+ if not is_in_shard_round_robin(jira_board.name, index):
104
162
  continue
105
- jira, current_state = fetch_current_state(jira_board, settings)
163
+ jira, current_state = fetch_current_state(
164
+ jira_board=jira_board, settings=settings, secret_reader=secret_reader
165
+ )
106
166
  if not current_state:
107
167
  logging.warning(
108
168
  "not acting on empty Jira boards. "
109
169
  + "please create a ticket to get started."
110
170
  )
111
171
  continue
112
- previous_state = fetch_previous_state(state, jira.project)
172
+ previous_state = fetch_previous_state(state=state, project=jira.project)
113
173
  if previous_state:
114
- diffs = calculate_diff(jira.server, current_state, previous_state)
115
- act(dry_run, jira_board, diffs)
174
+ diffs = calculate_diff(
175
+ jira_board.server.server_url, current_state, previous_state
176
+ )
177
+ act(
178
+ dry_run=dry_run,
179
+ slack=jira_board.slack,
180
+ diffs=diffs,
181
+ secret_reader=secret_reader,
182
+ )
116
183
  if not dry_run:
117
184
  write_state(state, jira.project, current_state)
reconcile/queries.py CHANGED
@@ -2171,62 +2171,6 @@ def get_pipelines_providers():
2171
2171
  return pipelines_providers
2172
2172
 
2173
2173
 
2174
- JIRA_BOARDS_QUERY = """
2175
- {
2176
- jira_boards: jira_boards_v1 {
2177
- path
2178
- name
2179
- server {
2180
- serverUrl
2181
- token {
2182
- path
2183
- field
2184
- version
2185
- format
2186
- }
2187
- }
2188
- {% if with_slack %}
2189
- slack {
2190
- workspace {
2191
- name
2192
- integrations {
2193
- name
2194
- token {
2195
- path
2196
- field
2197
- version
2198
- format
2199
- }
2200
- channel
2201
- icon_emoji
2202
- username
2203
- }
2204
- api_client {
2205
- global {
2206
- max_retries
2207
- timeout
2208
- }
2209
- methods {
2210
- name
2211
- args
2212
- }
2213
- }
2214
- }
2215
- channel
2216
- }
2217
- {% endif %}
2218
- }
2219
- }
2220
- """
2221
-
2222
-
2223
- def get_jira_boards(with_slack: Optional[bool] = True):
2224
- """Returns Jira boards resources defined in app-interface"""
2225
- gqlapi = gql.get_api()
2226
- query = Template(JIRA_BOARDS_QUERY).render(with_slack=with_slack)
2227
- return gqlapi.query(query)["jira_boards"]
2228
-
2229
-
2230
2174
  # Use APATH as the place holder because Python formatting interferes
2231
2175
  # with graphql use of curly braces
2232
2176
  JIRA_BOARDS_QUICK_QUERY = """
@@ -97,7 +97,7 @@ class JiraClient:
97
97
  project=project_name,
98
98
  )
99
99
 
100
- def get_issues(self, fields: Optional[Mapping] = None) -> list[Issue]:
100
+ def get_issues(self, fields: Optional[Iterable] = None) -> list[Issue]:
101
101
  block_size = 100
102
102
  block_num = 0
103
103
 
@@ -89,23 +89,17 @@ class OCMBaseClient:
89
89
  params_copy = params.copy()
90
90
  params_copy["size"] = max_page_size
91
91
 
92
- # fetch pages
93
- records_seen = 0
94
92
  while True:
95
- rs = self.get(api_path, params=params)
93
+ rs = self.get(api_path, params=params_copy)
96
94
  for item in rs.get("items", []):
97
95
  yield item
98
- total_records = rs.get("total", 0)
99
96
  current_page = rs.get("page", 0)
100
97
  records_on_page = rs.get("size", len(rs.get("items", [])))
101
- records_seen += records_on_page
102
- if total_records > records_seen and (
103
- max_pages and max_pages < current_page
104
- ):
105
- # more page available
106
- params_copy["page"] = current_page + 1
107
- else:
98
+ if records_on_page < max_page_size:
108
99
  return
100
+ if max_pages is not None and current_page >= max_pages:
101
+ return
102
+ params_copy["page"] = current_page + 1
109
103
 
110
104
  def post(
111
105
  self,