From a56a2ac997478c2b41f1dab1f0cb8ea254ba8bbb Mon Sep 17 00:00:00 2001 From: Vitalii Vdovenko Date: Fri, 13 Aug 2021 17:32:42 +0300 Subject: [PATCH] Source Jira #4505 - change cursor field for Issues streams from fields.started to fields.updated, update IssuesWorklogs slicing --- .../integration_tests/abnormal_state.json | 2 +- .../full_configured_catalog.json | 8 +- .../inc_configured_catalog.json | 8 +- .../issue_worklogs_configured_catalog.json | 4 +- .../integration_tests/sample_state.json | 10 + .../sample_files/configured_catalog.json | 8 +- .../sample_files/full_configured_catalog.json | 8 +- .../sample_files/sample_state.json | 6 +- .../source-jira/source_jira/streams.py | 226 ++++++++++++++++-- 9 files changed, 243 insertions(+), 37 deletions(-) create mode 100644 airbyte-integrations/connectors/source-jira/integration_tests/sample_state.json diff --git a/airbyte-integrations/connectors/source-jira/integration_tests/abnormal_state.json b/airbyte-integrations/connectors/source-jira/integration_tests/abnormal_state.json index 7b5042c7125a..abed3d6bbca5 100644 --- a/airbyte-integrations/connectors/source-jira/integration_tests/abnormal_state.json +++ b/airbyte-integrations/connectors/source-jira/integration_tests/abnormal_state.json @@ -3,6 +3,6 @@ "created": "2121-02-14T22:01:22.313Z" }, "issue_worklogs": { - "startedAfter": "2121-04-14T11:30:22.313Z" + "started": "2121-04-14T11:30:22.313Z" } } diff --git a/airbyte-integrations/connectors/source-jira/integration_tests/full_configured_catalog.json b/airbyte-integrations/connectors/source-jira/integration_tests/full_configured_catalog.json index ba70e74a4d04..774a212c1e7f 100644 --- a/airbyte-integrations/connectors/source-jira/integration_tests/full_configured_catalog.json +++ b/airbyte-integrations/connectors/source-jira/integration_tests/full_configured_catalog.json @@ -7455,10 +7455,10 @@ }, "supported_sync_modes": ["incremental"], "source_defined_cursor": true, - "default_cursor_field": ["created"] + "default_cursor_field": ["fields","updated"] }, "sync_mode": "incremental", - "cursor_field": ["created"], + "cursor_field": ["fields","updated"], "destination_sync_mode": "append" }, { @@ -9534,10 +9534,10 @@ }, "supported_sync_modes": ["incremental"], "source_defined_cursor": true, - "default_cursor_field": ["startedAfter"] + "default_cursor_field": ["started"] }, "sync_mode": "incremental", - "cursor_field": ["startedAfter"], + "cursor_field": ["started"], "destination_sync_mode": "append" }, { diff --git a/airbyte-integrations/connectors/source-jira/integration_tests/inc_configured_catalog.json b/airbyte-integrations/connectors/source-jira/integration_tests/inc_configured_catalog.json index 448dde83ccdf..c84a346b4df2 100644 --- a/airbyte-integrations/connectors/source-jira/integration_tests/inc_configured_catalog.json +++ b/airbyte-integrations/connectors/source-jira/integration_tests/inc_configured_catalog.json @@ -6,10 +6,10 @@ "json_schema": {}, "supported_sync_modes": ["incremental"], "source_defined_cursor": true, - "default_cursor_field": ["created"] + "default_cursor_field": ["fields","updated"] }, "sync_mode": "incremental", - "cursor_field": ["created"], + "cursor_field": ["fields", "updated"], "destination_sync_mode": "append" }, { @@ -18,10 +18,10 @@ "json_schema": {}, "supported_sync_modes": ["incremental"], "source_defined_cursor": true, - "default_cursor_field": ["startedAfter"] + "default_cursor_field": ["started"] }, "sync_mode": "incremental", - "cursor_field": ["startedAfter"], + "cursor_field": ["started"], "destination_sync_mode": "append" } ] diff --git a/airbyte-integrations/connectors/source-jira/integration_tests/issue_worklogs_configured_catalog.json b/airbyte-integrations/connectors/source-jira/integration_tests/issue_worklogs_configured_catalog.json index 61aec81c7e49..c9e1517f217d 100644 --- a/airbyte-integrations/connectors/source-jira/integration_tests/issue_worklogs_configured_catalog.json +++ b/airbyte-integrations/connectors/source-jira/integration_tests/issue_worklogs_configured_catalog.json @@ -6,10 +6,10 @@ "json_schema": {}, "supported_sync_modes": ["incremental"], "source_defined_cursor": true, - "default_cursor_field": ["startedAfter"] + "default_cursor_field": ["started"] }, "sync_mode": "incremental", - "cursor_field": ["startedAfter"], + "cursor_field": ["started"], "destination_sync_mode": "append" } ] diff --git a/airbyte-integrations/connectors/source-jira/integration_tests/sample_state.json b/airbyte-integrations/connectors/source-jira/integration_tests/sample_state.json new file mode 100644 index 000000000000..34de4187323c --- /dev/null +++ b/airbyte-integrations/connectors/source-jira/integration_tests/sample_state.json @@ -0,0 +1,10 @@ +{ + "issues": { + "updated": "2021-06-02T22:18:39.882Z" + }, + "issue_worklogs": { + "10622": { + "started": "2021-02-14T11:30:22.313Z" + } + } +} diff --git a/airbyte-integrations/connectors/source-jira/sample_files/configured_catalog.json b/airbyte-integrations/connectors/source-jira/sample_files/configured_catalog.json index c9d03e764db2..be1c736c7d5a 100644 --- a/airbyte-integrations/connectors/source-jira/sample_files/configured_catalog.json +++ b/airbyte-integrations/connectors/source-jira/sample_files/configured_catalog.json @@ -7962,10 +7962,10 @@ }, "supported_sync_modes": ["incremental"], "source_defined_cursor": true, - "default_cursor_field": ["created"] + "default_cursor_field": ["fields","updated"] }, "sync_mode": "incremental", - "cursor_field": ["created"], + "cursor_field": ["fields", "updated"], "destination_sync_mode": "append" }, { @@ -10088,10 +10088,10 @@ }, "supported_sync_modes": ["incremental"], "source_defined_cursor": true, - "default_cursor_field": ["startedAfter"] + "default_cursor_field": ["started"] }, "sync_mode": "incremental", - "cursor_field": ["startedAfter"], + "cursor_field": ["started"], "destination_sync_mode": "append" }, { diff --git a/airbyte-integrations/connectors/source-jira/sample_files/full_configured_catalog.json b/airbyte-integrations/connectors/source-jira/sample_files/full_configured_catalog.json index ba70e74a4d04..774a212c1e7f 100644 --- a/airbyte-integrations/connectors/source-jira/sample_files/full_configured_catalog.json +++ b/airbyte-integrations/connectors/source-jira/sample_files/full_configured_catalog.json @@ -7455,10 +7455,10 @@ }, "supported_sync_modes": ["incremental"], "source_defined_cursor": true, - "default_cursor_field": ["created"] + "default_cursor_field": ["fields","updated"] }, "sync_mode": "incremental", - "cursor_field": ["created"], + "cursor_field": ["fields","updated"], "destination_sync_mode": "append" }, { @@ -9534,10 +9534,10 @@ }, "supported_sync_modes": ["incremental"], "source_defined_cursor": true, - "default_cursor_field": ["startedAfter"] + "default_cursor_field": ["started"] }, "sync_mode": "incremental", - "cursor_field": ["startedAfter"], + "cursor_field": ["started"], "destination_sync_mode": "append" }, { diff --git a/airbyte-integrations/connectors/source-jira/sample_files/sample_state.json b/airbyte-integrations/connectors/source-jira/sample_files/sample_state.json index 0c2b2c994800..34de4187323c 100644 --- a/airbyte-integrations/connectors/source-jira/sample_files/sample_state.json +++ b/airbyte-integrations/connectors/source-jira/sample_files/sample_state.json @@ -1,8 +1,10 @@ { "issues": { - "created": "2021-02-14T22:01:22.313Z" + "updated": "2021-06-02T22:18:39.882Z" }, "issue_worklogs": { - "startedAfter": "2021-04-14T11:30:22.313Z" + "10622": { + "started": "2021-02-14T11:30:22.313Z" + } } } diff --git a/airbyte-integrations/connectors/source-jira/source_jira/streams.py b/airbyte-integrations/connectors/source-jira/source_jira/streams.py index 199eb79ff692..6d39f117bc8c 100644 --- a/airbyte-integrations/connectors/source-jira/source_jira/streams.py +++ b/airbyte-integrations/connectors/source-jira/source_jira/streams.py @@ -24,7 +24,7 @@ import urllib.parse as urlparse from abc import ABC, abstractmethod -from typing import Any, Iterable, Mapping, MutableMapping, Optional +from typing import Any, Iterable, Mapping, MutableMapping, Optional, List from urllib.parse import parse_qs import pendulum @@ -115,11 +115,19 @@ def get_updated_state(self, current_stream_state: MutableMapping[str, Any], late class ApplicationRoles(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-application-roles/#api-rest-api-3-applicationrole-key-get + """ + def path(self, **kwargs) -> str: return "applicationrole" class Avatars(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-avatars/#api-rest-api-3-avatar-type-system-get + """ + parse_response_root = "system" def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str: @@ -133,6 +141,10 @@ def read_records(self, stream_slice: Optional[Mapping[str, Any]] = None, **kwarg class Dashboards(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-dashboards/#api-rest-api-3-dashboard-get + """ + parse_response_root = "dashboards" def path(self, **kwargs) -> str: @@ -140,6 +152,10 @@ def path(self, **kwargs) -> str: class Filters(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-filters/#api-rest-api-3-filter-search-get + """ + parse_response_root = "values" def path(self, **kwargs) -> str: @@ -147,6 +163,10 @@ def path(self, **kwargs) -> str: class FilterSharing(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-filter-sharing/#api-rest-api-3-filter-id-permission-get + """ + def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str: filter_id = stream_slice["filter_id"] return f"filter/{filter_id}/permission" @@ -158,6 +178,10 @@ def read_records(self, stream_slice: Optional[Mapping[str, Any]] = None, **kwarg class Groups(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-groups/#api-rest-api-3-group-bulk-get + """ + parse_response_root = "values" def path(self, **kwargs) -> str: @@ -165,7 +189,11 @@ def path(self, **kwargs) -> str: class Issues(IncrementalJiraStream): - cursor_field = "created" + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-search/#api-rest-api-3-search-get + """ + + cursor_field = "updated" parse_response_root = "issues" def path(self, **kwargs) -> str: @@ -174,11 +202,11 @@ def path(self, **kwargs) -> str: def request_params(self, stream_state=None, **kwargs): stream_state = stream_state or {} params = super().request_params(stream_state=stream_state, **kwargs) - params["fields"] = ["attachment", "issuelinks", "security", "issuetype", "created"] + params["fields"] = ["attachment", "issuelinks", "security", "issuetype", "updated"] if stream_state.get(self.cursor_field): issues_state = pendulum.parse(stream_state.get(self.cursor_field)) issues_state_row = issues_state.strftime("%Y/%m/%d %H:%M") - params["jql"] = f"created > '{issues_state_row}'" + params["jql"] = f"updated > '{issues_state_row}'" return params def get_updated_state(self, current_stream_state: MutableMapping[str, Any], latest_record: Mapping[str, Any]) -> Mapping[str, Any]: @@ -192,6 +220,10 @@ def get_updated_state(self, current_stream_state: MutableMapping[str, Any], late class IssueComments(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-comments/#api-rest-api-3-issue-issueidorkey-comment-get + """ + parse_response_root = "comments" def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str: @@ -205,11 +237,19 @@ def read_records(self, stream_slice: Optional[Mapping[str, Any]] = None, **kwarg class IssueFields(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-fields/#api-rest-api-3-field-get + """ + def path(self, **kwargs) -> str: return "field" class IssueFieldConfigurations(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-field-configurations/#api-rest-api-3-fieldconfiguration-get + """ + parse_response_root = "values" def path(self, **kwargs) -> str: @@ -217,6 +257,10 @@ def path(self, **kwargs) -> str: class IssueCustomFieldContexts(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-custom-field-contexts/#api-rest-api-3-field-fieldid-context-get + """ + parse_response_root = "values" def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str: @@ -231,6 +275,10 @@ def read_records(self, stream_slice: Optional[Mapping[str, Any]] = None, **kwarg class IssueLinkTypes(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-link-types/#api-rest-api-3-issuelinktype-get + """ + parse_response_root = "issueLinkTypes" def path(self, **kwargs) -> str: @@ -238,11 +286,19 @@ def path(self, **kwargs) -> str: class IssueNavigatorSettings(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-navigator-settings/#api-rest-api-3-settings-columns-get + """ + def path(self, **kwargs) -> str: return "settings/columns" class IssueNotificationSchemes(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-notification-schemes/#api-rest-api-3-notificationscheme-get + """ + parse_response_root = "values" def path(self, **kwargs) -> str: @@ -250,11 +306,19 @@ def path(self, **kwargs) -> str: class IssuePriorities(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-priorities/#api-rest-api-3-priority-get + """ + def path(self, **kwargs) -> str: return "priority" class IssuePropertyKeys(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-properties/#api-rest-api-3-issue-issueidorkey-properties-get + """ + parse_response_root = "key" def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str: @@ -267,6 +331,10 @@ def read_records(self, stream_slice: Optional[Mapping[str, Any]] = None, **kwarg class IssueProperties(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-properties/#api-rest-api-3-issue-issueidorkey-properties-propertykey-get + """ + def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str: key = stream_slice["key"] issue_key = stream_slice["issue_key"] @@ -281,6 +349,10 @@ def read_records(self, stream_slice: Optional[Mapping[str, Any]] = None, **kwarg class IssueRemoteLinks(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-remote-links/#api-rest-api-3-issue-issueidorkey-remotelink-get + """ + def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str: key = stream_slice["key"] return f"issue/{key}/remotelink" @@ -292,11 +364,19 @@ def read_records(self, stream_slice: Optional[Mapping[str, Any]] = None, **kwarg class IssueResolutions(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-resolutions/#api-rest-api-3-resolution-get + """ + def path(self, **kwargs) -> str: return "resolution" class IssueSecuritySchemes(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-security-schemes/#api-rest-api-3-issuesecurityschemes-get + """ + parse_response_root = "issueSecuritySchemes" def path(self, **kwargs) -> str: @@ -304,6 +384,10 @@ def path(self, **kwargs) -> str: class IssueTypeSchemes(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-type-schemes/#api-rest-api-3-issuetypescheme-get + """ + parse_response_root = "values" def path(self, **kwargs) -> str: @@ -311,6 +395,10 @@ def path(self, **kwargs) -> str: class IssueTypeScreenSchemes(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-type-screen-schemes/#api-rest-api-3-issuetypescreenscheme-get + """ + parse_response_root = "values" def path(self, **kwargs) -> str: @@ -318,6 +406,10 @@ def path(self, **kwargs) -> str: class IssueVotes(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-votes/#api-rest-api-3-issue-issueidorkey-votes-get + """ + parse_response_root = "voters" def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str: @@ -331,6 +423,10 @@ def read_records(self, stream_slice: Optional[Mapping[str, Any]] = None, **kwarg class IssueWatchers(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-watchers/#api-rest-api-3-issue-issueidorkey-watchers-get + """ + parse_response_root = "watchers" def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str: @@ -344,13 +440,24 @@ def read_records(self, stream_slice: Optional[Mapping[str, Any]] = None, **kwarg class IssueWorklogs(IncrementalJiraStream): - cursor_field = "startedAfter" + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-worklogs/#api-rest-api-3-issue-issueidorkey-worklog-get + """ + + cursor_field = "started" parse_response_root = "worklogs" def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str: key = stream_slice["key"] return f"issue/{key}/worklog" + def stream_slices( + self, sync_mode: SyncMode, cursor_field: List[str] = None, stream_state: Mapping[str, Any] = None + ) -> Iterable[Optional[Mapping[str, Any]]]: + issues_stream = Issues(authenticator=self.authenticator, domain=self._domain) + for issue in issues_stream.read_records(sync_mode=SyncMode.full_refresh): + yield {"key": issue["key"]} + def request_params(self, stream_state=None, **kwargs): stream_state = stream_state or {} params = super().request_params(stream_state=stream_state, **kwargs) @@ -361,31 +468,42 @@ def request_params(self, stream_state=None, **kwargs): return params def get_updated_state(self, current_stream_state: MutableMapping[str, Any], latest_record: Mapping[str, Any]) -> Mapping[str, Any]: - latest_record_date = pendulum.parse(latest_record.get("started")) - if current_stream_state: - current_stream_state = current_stream_state.get(self.cursor_field) - if current_stream_state: - return {self.cursor_field: str(max(latest_record_date, pendulum.parse(current_stream_state)))} + issue_id = latest_record.get("issueId") + latest_record_date = pendulum.parse(latest_record.get(self.cursor_field)) + current_state = current_stream_state.get(issue_id) + if current_state: + current_state_date = current_state.get(self.cursor_field) + if current_state_date: + current_stream_state[issue_id] = {self.cursor_field: str(max(latest_record_date, + pendulum.parse(current_state_date)))} else: - return {self.cursor_field: str(latest_record_date)} - - def read_records(self, stream_slice: Optional[Mapping[str, Any]] = None, **kwargs) -> Iterable[Mapping[str, Any]]: - issues_stream = Issues(authenticator=self.authenticator, domain=self._domain) - for issue in issues_stream.read_records(sync_mode=SyncMode.full_refresh): - yield from super().read_records(stream_slice={"key": issue["key"]}, **kwargs) + current_stream_state[issue_id] = {self.cursor_field: str(latest_record_date)} + return current_stream_state class JiraSettings(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-jira-settings/#api-rest-api-3-application-properties-get + """ + def path(self, **kwargs) -> str: return "application-properties" class Labels(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-labels/#api-rest-api-3-label-get + """ + def path(self, **kwargs) -> str: return "application-properties" class Permissions(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-permissions/#api-rest-api-3-permissions-get + """ + parse_response_root = "permissions" def path(self, **kwargs) -> str: @@ -398,6 +516,10 @@ def parse_response(self, response: requests.Response, **kwargs) -> Iterable[Mapp class PermissionSchemes(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-permission-schemes/#api-rest-api-3-permissionscheme-get + """ + parse_response_root = "permissionSchemes" def path(self, **kwargs) -> str: @@ -405,6 +527,10 @@ def path(self, **kwargs) -> str: class Projects(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-projects/#api-rest-api-3-project-search-get + """ + parse_response_root = "values" def path(self, **kwargs) -> str: @@ -412,6 +538,10 @@ def path(self, **kwargs) -> str: class ProjectAvatars(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-project-avatars/#api-rest-api-3-project-projectidorkey-avatars-get + """ + def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str: key = stream_slice["key"] return f"project/{key}/avatars" @@ -428,11 +558,19 @@ def read_records(self, stream_slice: Optional[Mapping[str, Any]] = None, **kwarg class ProjectCategories(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-project-categories/#api-rest-api-3-projectcategory-get + """ + def path(self, **kwargs) -> str: return "projectCategory" class ProjectComponents(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-project-components/#api-rest-api-3-project-projectidorkey-component-get + """ + parse_response_root = "values" def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str: @@ -446,6 +584,10 @@ def read_records(self, stream_slice: Optional[Mapping[str, Any]] = None, **kwarg class ProjectEmail(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-project-email/#api-rest-api-3-project-projectid-email-get + """ + def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str: project_id = stream_slice["project_id"] return f"project/{project_id}/email" @@ -457,6 +599,10 @@ def read_records(self, stream_slice: Optional[Mapping[str, Any]] = None, **kwarg class ProjectPermissionSchemes(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-project-permission-schemes/#api-rest-api-3-project-projectkeyorid-securitylevel-get + """ + def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str: key = stream_slice["key"] return f"project/{key}/securitylevel" @@ -468,11 +614,19 @@ def read_records(self, stream_slice: Optional[Mapping[str, Any]] = None, **kwarg class ProjectTypes(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-project-types/#api-rest-api-3-project-type-get + """ + def path(self, **kwargs) -> str: return "project/type" class ProjectVersions(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-project-versions/#api-rest-api-3-project-projectidorkey-version-get + """ + parse_response_root = "values" def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str: @@ -486,6 +640,10 @@ def read_records(self, stream_slice: Optional[Mapping[str, Any]] = None, **kwarg class Screens(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-screens/#api-rest-api-3-screens-get + """ + parse_response_root = "values" def path(self, **kwargs) -> str: @@ -493,6 +651,10 @@ def path(self, **kwargs) -> str: class ScreenTabs(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-screen-tabs/#api-rest-api-3-screens-screenid-tabs-get + """ + def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str: screen_id = stream_slice["screen_id"] return f"screens/{screen_id}/tabs" @@ -508,6 +670,10 @@ def read_tab_records(self, stream_slice: Optional[Mapping[str, Any]] = None, **k class ScreenTabFields(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-screen-tab-fields/#api-rest-api-3-screens-screenid-tabs-tabid-fields-get + """ + def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str: screen_id = stream_slice["screen_id"] tab_id = stream_slice["tab_id"] @@ -522,6 +688,10 @@ def read_records(self, stream_slice: Optional[Mapping[str, Any]] = None, **kwarg class ScreenSchemes(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-screen-schemes/#api-rest-api-3-screenscheme-get + """ + parse_response_root = "values" def path(self, **kwargs) -> str: @@ -529,16 +699,28 @@ def path(self, **kwargs) -> str: class TimeTracking(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-time-tracking/#api-rest-api-3-configuration-timetracking-list-get + """ + def path(self, **kwargs) -> str: return "configuration/timetracking/list" class Users(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-users/#api-rest-api-3-users-search-get + """ + def path(self, **kwargs) -> str: return "user/search?query=" class Workflows(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-workflows/#api-rest-api-3-workflow-search-get + """ + parse_response_root = "values" def path(self, **kwargs) -> str: @@ -546,6 +728,10 @@ def path(self, **kwargs) -> str: class WorkflowSchemes(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-workflow-schemes/#api-rest-api-3-workflowscheme-get + """ + parse_response_root = "values" def path(self, **kwargs) -> str: @@ -553,10 +739,18 @@ def path(self, **kwargs) -> str: class WorkflowStatuses(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-workflow-statuses/#api-rest-api-3-status-get + """ + def path(self, **kwargs) -> str: return "status" class WorkflowStatusCategories(JiraStream): + """ + https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-workflow-status-categories/#api-rest-api-3-statuscategory-get + """ + def path(self, **kwargs) -> str: return "statuscategory"