From f4b15da1e0e904252e4e63ce87221903daee6fea Mon Sep 17 00:00:00 2001 From: Savage Fivetran Date: Tue, 23 Sep 2025 16:24:18 -0700 Subject: [PATCH 01/11] feature/add_teams_to_jira_package --- .quickstart/quickstart.yml | 2 ++ dbt_project.yml | 1 + integration_tests/dbt_project.yml | 6 ++-- integration_tests/seeds/field.csv | 3 +- .../seeds/issue_field_history.csv | 3 +- integration_tests/seeds/team.csv | 6 ++++ macros/staging/get_team_columns.sql | 14 ++++++++ models/staging/src_jira.yml | 17 ++++++++++ models/staging/stg_jira.yml | 17 ++++++++++ models/staging/stg_jira__team.sql | 34 +++++++++++++++++++ models/staging/tmp/stg_jira__team_tmp.sql | 4 +++ 11 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 integration_tests/seeds/team.csv create mode 100644 macros/staging/get_team_columns.sql create mode 100644 models/staging/stg_jira__team.sql create mode 100644 models/staging/tmp/stg_jira__team_tmp.sql diff --git a/.quickstart/quickstart.yml b/.quickstart/quickstart.yml index 6487a894..36442243 100644 --- a/.quickstart/quickstart.yml +++ b/.quickstart/quickstart.yml @@ -12,6 +12,8 @@ table_variables: - version jira_using_priorities: - priority + jira_using_teams: + - team destination_configurations: databricks: diff --git a/dbt_project.yml b/dbt_project.yml index 2a7d138c..7bd2ab50 100644 --- a/dbt_project.yml +++ b/dbt_project.yml @@ -21,6 +21,7 @@ vars: field: "{{ source('jira', 'field') }}" field_option: "{{ source('jira', 'field_option') }}" sprint: "{{ source('jira', 'sprint') }}" + team: "{{ source('jira', 'team') }}" version: "{{ source('jira', 'version') }}" jira_issue_history_buffer: 1 diff --git a/integration_tests/dbt_project.yml b/integration_tests/dbt_project.yml index c0dd8c98..c6be977c 100644 --- a/integration_tests/dbt_project.yml +++ b/integration_tests/dbt_project.yml @@ -7,7 +7,7 @@ vars: # Comment out the below when generating docs issue_field_history_columns: ['summary', 'components'] # @docs-ignore jira: - jira_schema: jira_integrations_tests_42 + jira_schema: zz_dbt_savage jira_comment_identifier: "comment" jira_component_identifier: "component" jira_epic_identifier: "epic" @@ -32,8 +32,8 @@ vars: models: jira: - +schema: "{{ 'jira_integrations_tests_sqlw' if target.name == 'databricks-sql' else 'jira' }}" - # +schema: "jira_{{ var('directed_schema','dev') }}" + # +schema: "{{ 'jira_integrations_tests_sqlw' if target.name == 'databricks-sql' else 'jira' }}" + +schema: "jira_{{ var('directed_schema','dev') }}" seeds: +docs: diff --git a/integration_tests/seeds/field.csv b/integration_tests/seeds/field.csv index 5d58641d..9e8a1458 100644 --- a/integration_tests/seeds/field.csv +++ b/integration_tests/seeds/field.csv @@ -5,4 +5,5 @@ issuerestriction,2020-11-23 22:20:39.643,false,false,Restrict to components,2020-11-23 22:20:40.643,false,false,Components summary,2020-11-23 22:20:41.553,false,false,Summary epiclink,2020-11-23 22:20:41.553,false,false,Epic-Link -epic,2020-11-23 22:20:41.553,false,false,Epic \ No newline at end of file +epic,2020-11-23 22:20:41.553,false,false,Epic +customfield_10001,2020-11-23 22:20:41.553,false,true,Team \ No newline at end of file diff --git a/integration_tests/seeds/issue_field_history.csv b/integration_tests/seeds/issue_field_history.csv index 6d023cab..1ac98827 100644 --- a/integration_tests/seeds/issue_field_history.csv +++ b/integration_tests/seeds/issue_field_history.csv @@ -5,4 +5,5 @@ summary,10027,2020-11-10 19:19:41.472,2020-11-12 12:20:53.500,"As a developer, I components,10018,2020-11-10 19:19:41.472,2020-11-19 12:20:53.500,10019,true summary,10018,2020-11-10 19:19:41.472,2020-11-19 12:20:53.500,10026,true epic,10015,2020-11-10 19:19:41.472,2020-11-19 12:20:53.500,1111111,true -epiclink,10018,2020-11-10 19:19:41.472,2020-11-12 12:20:53.500,https://ulr-here/epic-key,true \ No newline at end of file +epiclink,10018,2020-11-10 19:19:41.472,2020-11-12 12:20:53.500,https://ulr-here/epic-key,true +customfield_10001,10027,2020-11-10 19:19:41.472,2020-11-12 12:20:53.500,01e9ac93-bccf-4ad2-af92-3317a37ec67b, \ No newline at end of file diff --git a/integration_tests/seeds/team.csv b/integration_tests/seeds/team.csv new file mode 100644 index 00000000..26229c86 --- /dev/null +++ b/integration_tests/seeds/team.csv @@ -0,0 +1,6 @@ +team_id,is_shared,is_visible,team_name,team_title,_fivetran_synced +01e9ac93-bccf-4ad2-af92-3317a37ec67b,true,true,HR,HR,2025-07-14 19:51:56.426000 UTC +891da445-73e5-49b9-9d0d-89f6a587822a,true,true,Cloud Security,Cloud Security,2026-07-14 19:51:56.426000 UTC +1bc241c7-6926-4b46-94b7-82d25069f067,true,true,People Ops,People Ops,2027-07-14 19:51:56.426000 UTC +e74a1782-f463-4542-8440-66de07d4b083,true,true,Detection & Response,Detection & Response,2028-07-14 19:51:56.426000 UTC +d8a8402f-bc1b-4990-9b11-06a52d2de68a,true,true,Platform Security,Platform Security,2029-07-14 19:51:56.426000 UTC \ No newline at end of file diff --git a/macros/staging/get_team_columns.sql b/macros/staging/get_team_columns.sql new file mode 100644 index 00000000..c8177b6c --- /dev/null +++ b/macros/staging/get_team_columns.sql @@ -0,0 +1,14 @@ +{% macro get_team_columns() %} + +{% set columns = [ + {"name": "_fivetran_synced", "datatype": dbt.type_timestamp()}, + {"name": "id", "datatype": dbt.type_string()}, + {"name": "is_shared", "datatype": "boolean"}, + {"name": "is_visible", "datatype": "boolean"}, + {"name": "name", "datatype": dbt.type_string()}, + {"name": "title", "datatype": dbt.type_string()}, +] %} + +{{ return(columns) }} + +{% endmacro %} diff --git a/models/staging/src_jira.yml b/models/staging/src_jira.yml index e0e264e0..001b3d9c 100644 --- a/models/staging/src_jira.yml +++ b/models/staging/src_jira.yml @@ -294,6 +294,23 @@ sources: - name: name description: Title of the status category. + - name: team + identifier: "{{ var('jira_team_identifier', 'team')}}" + description: Table of teams associated with your organization. + config: + enabled: "{{ var('jira_using_teams', true) }}" + columns: + - name: id + description: Unique ID of the team. + - name: is_shared + description: Boolean that is true if the team is shared with other teams in your organization. + - name: is_visible + description: Boolean that is true if the team is visible to all users in your organization. + - name: name + description: Title of the status category. + - name: title + description: Longer, more descriptive name of the team. + - name: user identifier: "{{ var('jira_user_identifier', 'user')}}" description: Table of users associated with your organization. diff --git a/models/staging/stg_jira.yml b/models/staging/stg_jira.yml index 491e9081..3fef5019 100644 --- a/models/staging/stg_jira.yml +++ b/models/staging/stg_jira.yml @@ -285,6 +285,23 @@ models: - not_null - name: status_category_name description: Title of the status category. + + - name: st_jira__team + description: Table of teams associated with your organization. + columns: + - name: team_id + description: Unique ID of the team. + tests: + - unique + - not_null + - name: is_shared + description: Boolean that is true if the team is shared with other teams in your organization. + - name: is_visible + description: Boolean that is true if the team is visible to all users in your organization. + - name: team_name + description: Title of the status category. + - name: team_title + description: Longer, more descriptive name of the team. - name: stg_jira__user description: Table of users associated with your organization. diff --git a/models/staging/stg_jira__team.sql b/models/staging/stg_jira__team.sql new file mode 100644 index 00000000..a626bae4 --- /dev/null +++ b/models/staging/stg_jira__team.sql @@ -0,0 +1,34 @@ +{{ config(enabled=var('jira_using_teams', True)) }} + +with base as ( + + select * + from {{ ref('stg_jira__team_tmp') }} +), + +fields as ( + + select + {{ + fivetran_utils.fill_staging_columns( + source_columns=adapter.get_columns_in_relation(ref('stg_jira__team_tmp')), + staging_columns=get_team_columns() + ) + }} + from base +), + +final as ( + + select + cast(id as {{ dbt.type_string() }}) as team_id, + is_shared, + is_visible, + name as team_name, + title as team_title, + _fivetran_synced + from fields +) + +select * +from final diff --git a/models/staging/tmp/stg_jira__team_tmp.sql b/models/staging/tmp/stg_jira__team_tmp.sql new file mode 100644 index 00000000..50dd7c15 --- /dev/null +++ b/models/staging/tmp/stg_jira__team_tmp.sql @@ -0,0 +1,4 @@ +{{ config(enabled=var('jira_using_teams', True)) }} + +select * +from {{ var('team') }} \ No newline at end of file From 81dd21a4d07d2bd17a8e5b31958814c4e392f9ed Mon Sep 17 00:00:00 2001 From: Savage Fivetran Date: Mon, 13 Oct 2025 15:41:13 -0700 Subject: [PATCH 02/11] feature/add_teams_to_jira_package --- integration_tests/dbt_project.yml | 1 + integration_tests/seeds/team.csv | 2 +- models/staging/src_jira.yml | 2 +- models/staging/stg_jira__team.sql | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/integration_tests/dbt_project.yml b/integration_tests/dbt_project.yml index c6be977c..815bc4e3 100644 --- a/integration_tests/dbt_project.yml +++ b/integration_tests/dbt_project.yml @@ -29,6 +29,7 @@ vars: jira_user_group_identifier: "user_group" jira_user_identifier: "user" jira_version_identifier: "version" + jira_team_identifier: "team" models: jira: diff --git a/integration_tests/seeds/team.csv b/integration_tests/seeds/team.csv index 26229c86..f1e4ac7a 100644 --- a/integration_tests/seeds/team.csv +++ b/integration_tests/seeds/team.csv @@ -1,4 +1,4 @@ -team_id,is_shared,is_visible,team_name,team_title,_fivetran_synced +id,is_shared,is_visible,name,title,_fivetran_synced 01e9ac93-bccf-4ad2-af92-3317a37ec67b,true,true,HR,HR,2025-07-14 19:51:56.426000 UTC 891da445-73e5-49b9-9d0d-89f6a587822a,true,true,Cloud Security,Cloud Security,2026-07-14 19:51:56.426000 UTC 1bc241c7-6926-4b46-94b7-82d25069f067,true,true,People Ops,People Ops,2027-07-14 19:51:56.426000 UTC diff --git a/models/staging/src_jira.yml b/models/staging/src_jira.yml index 001b3d9c..5cd929e7 100644 --- a/models/staging/src_jira.yml +++ b/models/staging/src_jira.yml @@ -307,7 +307,7 @@ sources: - name: is_visible description: Boolean that is true if the team is visible to all users in your organization. - name: name - description: Title of the status category. + description: Name of the team. - name: title description: Longer, more descriptive name of the team. diff --git a/models/staging/stg_jira__team.sql b/models/staging/stg_jira__team.sql index a626bae4..28ba28b9 100644 --- a/models/staging/stg_jira__team.sql +++ b/models/staging/stg_jira__team.sql @@ -22,8 +22,8 @@ final as ( select cast(id as {{ dbt.type_string() }}) as team_id, - is_shared, - is_visible, + is_shared as is_shared_team, + is_visible as is_visible_team, name as team_name, title as team_title, _fivetran_synced From 1ee7971a9a520654099836d9c6a5b43f663ca7af Mon Sep 17 00:00:00 2001 From: Savage Fivetran Date: Thu, 16 Oct 2025 11:48:17 -0700 Subject: [PATCH 03/11] add_team_column_to_end_models --- CHANGELOG.md | 25 ++++++++++ dbt_project.yml | 2 +- integration_tests/dbt_project.yml | 8 ++-- integration_tests/seeds/issue.csv | 1 + .../seeds/issue_field_history.csv | 4 +- .../int_jira__issue_field_history.sql | 20 +++++++- .../int_jira__pivot_daily_field_history.sql | 5 +- models/jira__daily_issue_field_history.sql | 46 +++++++++++++++---- models/staging/src_jira.yml | 6 +-- models/staging/stg_jira.yml | 12 ++--- 10 files changed, 101 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34cfa404..389884b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,28 @@ +# dbt_jira v1.1.0 + +[PR #XXX](https://github.com/fivetran/dbt_jira/pull/XXX) includes the following updates: + +## Schema/Data Change +**3 total changes • 3 possible breaking changes** + +| Data Model(s) | Change type | Old | New | Notes | +| ---------- | ----------- | -------- | -------- | ----- | +| `stg_jira__team` | New model | N/A | New staging model | Added staging model for Jira teams functionality. | +| `jira__daily_issue_field_history` | Column addition | N/A | `team` | Added team column to track team assignments for issues over time. | +| `jira__issue_enhanced` | Column addition | N/A | `team` | Added team column to provide current team assignment for each issue. | + +## Feature Update +- Added support for Jira teams functionality by introducing staging models `stg_jira__team` and `stg_jira__team_tmp`. +- Enhanced issue field history models to include team information by joining with team data when the field type is 'team'. +- Added team column to the `jira__daily_issue_field_history` model to track team assignments over time. +- Added team column to the `jira__issue_enhanced` model to provide current team assignment for each issue. + +## Under the Hood +- Added new seed file `team.csv` for integration testing. +- Added team-related test data to `issue.csv`, `issue_field_history.csv`, and `field.csv` seed files. +- Created `get_team_columns` macro for consistent team column definitions. +- Updated package variables to include team source reference and team identifier configuration. + # dbt_jira v1.0.0 [PR #145](https://github.com/fivetran/dbt_jira/pull/145) includes the following updates: diff --git a/dbt_project.yml b/dbt_project.yml index 7bd2ab50..306076f2 100644 --- a/dbt_project.yml +++ b/dbt_project.yml @@ -1,5 +1,5 @@ name: 'jira' -version: '1.0.0' +version: '1.1.0' config-version: 2 require-dbt-version: [">=1.3.0", "<2.0.0"] diff --git a/integration_tests/dbt_project.yml b/integration_tests/dbt_project.yml index 815bc4e3..1e2c6dd1 100644 --- a/integration_tests/dbt_project.yml +++ b/integration_tests/dbt_project.yml @@ -1,5 +1,5 @@ name: 'jira_integration_tests' -version: '1.0.0' +version: '1.1.0' config-version: 2 profile: 'integration_tests' @@ -7,7 +7,7 @@ vars: # Comment out the below when generating docs issue_field_history_columns: ['summary', 'components'] # @docs-ignore jira: - jira_schema: zz_dbt_savage + jira_schema: jira_integrations_tests_42 jira_comment_identifier: "comment" jira_component_identifier: "component" jira_epic_identifier: "epic" @@ -33,8 +33,8 @@ vars: models: jira: - # +schema: "{{ 'jira_integrations_tests_sqlw' if target.name == 'databricks-sql' else 'jira' }}" - +schema: "jira_{{ var('directed_schema','dev') }}" + +schema: "{{ 'jira_integrations_tests_sqlw' if target.name == 'databricks-sql' else 'jira' }}" + # +schema: "jira_{{ var('directed_schema','dev') }}" seeds: +docs: diff --git a/integration_tests/seeds/issue.csv b/integration_tests/seeds/issue.csv index 56d57b90..b5c7be62 100644 --- a/integration_tests/seeds/issue.csv +++ b/integration_tests/seeds/issue.csv @@ -2,3 +2,4 @@ id,_fivetran_deleted,_fivetran_synced,_original_estimate,_remaining_estimate,_ti 10011,FALSE,2020-11-06 22:23:38,,,,,2020-04-01 08:33:58,5c3326c24b248c315badf3ee,"Yes, this is an issue to test permissions",,,10103,TP-8,,,,3,10001,,5c3326c24b248c315badf3ee,,2020-05-29 11:33:53,3,2020-04-01 08:34:02,We want to test permissions,,2020-05-29 11:33:53,-1,10103 10015,FALSE,2020-11-06 22:23:41,,,,,2020-06-11 07:38:03,5ed0edbe2999b60c23255b4f,,,,,TP-12,,,,5,10001,,5ed0edbe2999b60c23255b4f,,2020-06-11 07:38:03,10003,2020-06-11 07:38:03,Part of test epic,,2020-06-11 07:38:03,-1,10103 10018,FALSE,2020-11-12 12:20:50,,,,,2020-11-10 19:11:45,557058:a9572f6a-3041-435a-96ed-5286e3811b33,,,,10103,TCP-1,2020-11-10 19:11:46,,,3,10005,,557058:a9572f6a-3041-435a-96ed-5286e3811b33,,2020-11-28 19:11:45,10003,2020-11-28 19:11:46,this is a story,,2020-11-28 19:11:45,-1, +10027,FALSE,2020-11-12 12:20:50,,,,,2020-11-15 19:11:45,5ed0edbe2999b60c23255b4f,Test issue to see if teams data populates,,,10103,TP-10,,,,1,10005,,5ed0edbe2999b60c23255b4f,,,3,,just a regular issue,,,, diff --git a/integration_tests/seeds/issue_field_history.csv b/integration_tests/seeds/issue_field_history.csv index 1ac98827..ef6654ce 100644 --- a/integration_tests/seeds/issue_field_history.csv +++ b/integration_tests/seeds/issue_field_history.csv @@ -6,4 +6,6 @@ components,10018,2020-11-10 19:19:41.472,2020-11-19 12:20:53.500,10019,true summary,10018,2020-11-10 19:19:41.472,2020-11-19 12:20:53.500,10026,true epic,10015,2020-11-10 19:19:41.472,2020-11-19 12:20:53.500,1111111,true epiclink,10018,2020-11-10 19:19:41.472,2020-11-12 12:20:53.500,https://ulr-here/epic-key,true -customfield_10001,10027,2020-11-10 19:19:41.472,2020-11-12 12:20:53.500,01e9ac93-bccf-4ad2-af92-3317a37ec67b, \ No newline at end of file +customfield_10001,10027,2020-11-10 19:19:41.472,2020-11-12 12:20:53.500,01e9ac93-bccf-4ad2-af92-3317a37ec67b,true +customfield_10001,10018,2020-11-10 19:19:41.472,2020-11-12 12:20:53.500,1bc241c7-6926-4b46-94b7-82d25069f067,true +customfield_10001,10015,2020-11-10 19:19:41.472,2020-11-12 12:20:53.500,e74a1782-f463-4542-8440-66de07d4b083,true \ No newline at end of file diff --git a/models/intermediate/field_history/int_jira__issue_field_history.sql b/models/intermediate/field_history/int_jira__issue_field_history.sql index ae51d9d7..e75e1767 100644 --- a/models/intermediate/field_history/int_jira__issue_field_history.sql +++ b/models/intermediate/field_history/int_jira__issue_field_history.sql @@ -12,15 +12,31 @@ fields as ( ), +team as ( + + select * + from {{ ref('stg_jira__team') }} +), + joined as ( select - field_history.*, - lower(fields.field_name) as field_name + field_history.field_id, + field_history.issue_id, + field_history.updated_at, + -- if the field is 'team', we want to replace the value with the team name + -- otherwise, just use the value as is + case when lower(fields.field_name) = 'team' then team.team_name + else field_history.field_value end as field_value, + lower(fields.field_name) as field_name, + field_history.is_active, + field_history._fivetran_synced from field_history join fields on fields.field_id = field_history.field_id + left join team on lower(fields.field_name) = 'team' + and cast(field_history.field_value as {{ dbt.type_string() }}) = cast(team.team_id as {{ dbt.type_string() }}) ) diff --git a/models/intermediate/field_history/int_jira__pivot_daily_field_history.sql b/models/intermediate/field_history/int_jira__pivot_daily_field_history.sql index d23f3192..dfef75fe 100644 --- a/models/intermediate/field_history/int_jira__pivot_daily_field_history.sql +++ b/models/intermediate/field_history/int_jira__pivot_daily_field_history.sql @@ -101,7 +101,7 @@ limit_to_relevant_fields as ( from get_valid_dates where lower(field_id) = 'status' - or lower(field_name) in ('sprint', 'story points', 'story point estimate' + or lower(field_name) in ('sprint', 'story points', 'story point estimate', 'team' {%- for col in var('issue_field_history_columns', []) -%} ,'{{ (col|lower) }}' {%- endfor -%} ) @@ -157,7 +157,8 @@ pivot_out as ( max(case when lower(field_id) = 'status' then field_value end) as status, max(case when lower(field_name) = 'sprint' then field_value end) as sprint, max(case when lower(field_name) = 'story points' then field_value end) as story_points, - max(case when lower(field_name) = 'story point estimate' then field_value end) as story_point_estimate + max(case when lower(field_name) = 'story point estimate' then field_value end) as story_point_estimate, + max(case when lower(field_name) = 'team' then field_value end) as team {% for col in var('issue_field_history_columns', []) -%} {% if col|lower not in ['story points', 'story point estimate'] %} diff --git a/models/jira__daily_issue_field_history.sql b/models/jira__daily_issue_field_history.sql index 6fceafda..7cc69ee0 100644 --- a/models/jira__daily_issue_field_history.sql +++ b/models/jira__daily_issue_field_history.sql @@ -69,6 +69,11 @@ issue_types as ( from {{ ref('stg_jira__issue_type') }} ), +teams as ( + select * + from {{ ref('stg_jira__team') }} +), + {% if var('jira_using_components', True) %} components as ( @@ -88,7 +93,10 @@ joined as ( {% if col.name|lower == 'components' and var('jira_using_components', True) %} , coalesce(pivoted_daily_history.components, most_recent_data.components) as components - {% elif col.name|lower not in ['issue_day_id', 'issue_id', 'valid_starting_on', 'valid_starting_at_week', 'components'] %} + {% elif col.name|lower == 'team' %} + , coalesce(pivoted_daily_history.team, most_recent_data.team) as team + + {% elif col.name|lower not in ['issue_day_id', 'issue_id', 'valid_starting_on', 'valid_starting_at_week', 'components', 'team'] %} , coalesce(pivoted_daily_history.{{ col.name }}, most_recent_data.{{ col.name }}) as {{ col.name }} {% endif %} @@ -97,9 +105,13 @@ joined as ( {% else %} {% for col in pivot_data_columns %} {% if col.name|lower == 'components' and var('jira_using_components', True) %} - , pivoted_daily_history.components + , pivoted_daily_history.components - {% elif col.name|lower not in ['issue_day_id', 'issue_id', 'valid_starting_on', 'valid_starting_at_week', 'components'] %} + {% elif col.name|lower == 'team' %} + , pivoted_daily_history.team + + + {% elif col.name|lower not in ['issue_day_id', 'issue_id', 'valid_starting_on', 'valid_starting_at_week', 'components','team'] %} , pivoted_daily_history.{{ col.name }} {% endif %} @@ -127,7 +139,7 @@ set_values as ( order by date_day rows unbounded preceding) as status_id_field_partition -- list of exception columns - {% set exception_cols = ['issue_id', 'issue_day_id', 'valid_starting_on', 'valid_starting_at_week', 'status', 'status_id', 'components', 'issue_type'] %} + {% set exception_cols = ['issue_id', 'issue_day_id', 'valid_starting_on', 'valid_starting_at_week', 'status', 'status_id', 'components', 'issue_type', 'team'] %} {% for col in pivot_data_columns %} {% if col.name|lower == 'components' and var('jira_using_components', True) %} @@ -137,6 +149,11 @@ set_values as ( {% elif col.name|lower == 'issue_type' %} , coalesce(issue_types.issue_type_name, joined.issue_type) as issue_type , sum(case when joined.issue_type is null then 0 else 1 end) over (partition by issue_id order by date_day rows unbounded preceding) as issue_type_field_partition + + {% elif col.name|lower == 'team' %} + , coalesce(teams.team_name, joined.team) as team + , sum(case when joined.team is null then 0 else 1 end) over ( partition by issue_id + order by date_day rows unbounded preceding) as team_field_partition {% elif col.name|lower not in exception_cols %} , coalesce(field_option_{{ col.name }}.field_option_name, joined.{{ col.name }}) as {{ col.name }} @@ -158,6 +175,10 @@ set_values as ( left join issue_types on cast(issue_types.issue_type_id as {{ dbt.type_string() }}) = joined.issue_type + {% elif col.name|lower == 'team' %} + left join teams + on cast(teams.team_name as {{ dbt.type_string() }}) = joined.team + {% elif col.name|lower not in exception_cols %} left join field_option as field_option_{{ col.name }} on cast(field_option_{{ col.name }}.field_id as {{ dbt.type_string() }}) = joined.{{ col.name }} @@ -181,7 +202,12 @@ fill_values as ( partition by issue_id, component_field_partition order by date_day asc rows between unbounded preceding and current row) as components - {% elif col.name|lower not in ['issue_id', 'issue_day_id', 'valid_starting_on', 'valid_starting_at_week', 'status', 'status_id', 'components'] %} + {% elif col.name|lower == 'team' %} + , first_value(team) over ( + partition by issue_id, team_field_partition + order by date_day asc rows between unbounded preceding and current row) as team + + {% elif col.name|lower not in ['issue_id', 'issue_day_id', 'valid_starting_on', 'valid_starting_at_week', 'status', 'status_id', 'components', 'team'] %} -- grab the value that started this batch/partition , first_value( {{ col.name }} ) over ( partition by issue_id, {{ col.name }}_field_partition @@ -197,13 +223,14 @@ fix_null_values as ( select date_day, - issue_id + issue_id, + case when team = 'is_null' then null else team end as team {% for col in pivot_data_columns %} {% if col.name|lower == 'components' and var('jira_using_components', True) %} , case when components = 'is_null' then null else components end as components - {% elif col.name|lower not in ['issue_id','issue_day_id','valid_starting_on', 'valid_starting_at_week', 'status', 'components'] %} + {% elif col.name|lower not in ['issue_id','issue_day_id','valid_starting_on', 'valid_starting_at_week', 'status', 'components','team'] %} -- we de-nulled the true null values earlier in order to differentiate them from nulls that just needed to be backfilled , case when {{ col.name }} = 'is_null' then null else {{ col.name }} end as {{ col.name }} @@ -220,13 +247,14 @@ surrogate_key as ( fix_null_values.date_day, cast({{ dbt.date_trunc('week', 'fix_null_values.date_day') }} as date) as date_week, fix_null_values.issue_id, - statuses.status_name as status + statuses.status_name as status, + fix_null_values.team {% for col in pivot_data_columns %} {% if col.name|lower == 'components' and var('jira_using_components', True) %} , fix_null_values.components as components - {% elif col.name|lower not in ['issue_id','issue_day_id','valid_starting_on', 'valid_starting_at_week', 'status', 'components'] %} + {% elif col.name|lower not in ['issue_id','issue_day_id','valid_starting_on', 'valid_starting_at_week', 'status', 'components','team'] %} , fix_null_values.{{ col.name }} as {{ col.name }} {% endif %} diff --git a/models/staging/src_jira.yml b/models/staging/src_jira.yml index 5cd929e7..b2e0cac6 100644 --- a/models/staging/src_jira.yml +++ b/models/staging/src_jira.yml @@ -62,7 +62,7 @@ sources: Unique ID of the field. Default fields will have descriptive IDs, whereas custom field IDs will be `'customfield_#####'`. - name: is_array - description: Boolean that is true if a field can have multiple values (is mulitselect). + description: Boolean that is true if a field can have multiple values (is multiselect). - name: is_custom description: > Boolean that is true if the field is custom to this organization, and false if @@ -303,9 +303,9 @@ sources: - name: id description: Unique ID of the team. - name: is_shared - description: Boolean that is true if the team is shared with other teams in your organization. + description: Boolean that is true if you share the team with other teams in your organization. - name: is_visible - description: Boolean that is true if the team is visible to all users in your organization. + description: Boolean that is true if all users in your organization can see the team. - name: name description: Name of the team. - name: title diff --git a/models/staging/stg_jira.yml b/models/staging/stg_jira.yml index 3fef5019..4138efea 100644 --- a/models/staging/stg_jira.yml +++ b/models/staging/stg_jira.yml @@ -50,7 +50,7 @@ models: - unique - not_null - name: is_array - description: Boolean that is true if a field can have multiple values (is mulitselect). + description: Boolean that is true if a field can have multiple values (is multiselect). - name: is_custom description: > Boolean that is true if the field is custom to this organization, and false if @@ -286,7 +286,7 @@ models: - name: status_category_name description: Title of the status category. - - name: st_jira__team + - name: stg_jira__team description: Table of teams associated with your organization. columns: - name: team_id @@ -294,12 +294,12 @@ models: tests: - unique - not_null - - name: is_shared + - name: is_shared_team description: Boolean that is true if the team is shared with other teams in your organization. - - name: is_visible + - name: is_visible_team description: Boolean that is true if the team is visible to all users in your organization. - name: team_name - description: Title of the status category. + description: Name of the team. - name: team_title description: Longer, more descriptive name of the team. @@ -312,7 +312,7 @@ models: - unique - not_null - name: email - description: Email associated with the user acccount. + description: Email associated with the user account. - name: locale description: The Java locale of the user. - name: user_display_name From ef70f6b38092eac596a96ecb0173db7ba1ff1d2e Mon Sep 17 00:00:00 2001 From: Savage Fivetran Date: Thu, 16 Oct 2025 12:02:23 -0700 Subject: [PATCH 04/11] update_seed_data --- integration_tests/seeds/team.csv | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/integration_tests/seeds/team.csv b/integration_tests/seeds/team.csv index f1e4ac7a..e039ff60 100644 --- a/integration_tests/seeds/team.csv +++ b/integration_tests/seeds/team.csv @@ -1,6 +1,6 @@ id,is_shared,is_visible,name,title,_fivetran_synced -01e9ac93-bccf-4ad2-af92-3317a37ec67b,true,true,HR,HR,2025-07-14 19:51:56.426000 UTC -891da445-73e5-49b9-9d0d-89f6a587822a,true,true,Cloud Security,Cloud Security,2026-07-14 19:51:56.426000 UTC -1bc241c7-6926-4b46-94b7-82d25069f067,true,true,People Ops,People Ops,2027-07-14 19:51:56.426000 UTC -e74a1782-f463-4542-8440-66de07d4b083,true,true,Detection & Response,Detection & Response,2028-07-14 19:51:56.426000 UTC -d8a8402f-bc1b-4990-9b11-06a52d2de68a,true,true,Platform Security,Platform Security,2029-07-14 19:51:56.426000 UTC \ No newline at end of file +01e9ac93-bccf-4ad2-af92-3317a37ec67b,true,true,HR,HR,2020-11-23 22:20:39.685 +891da445-73e5-49b9-9d0d-89f6a587822a,true,true,Cloud Security,Cloud Security,2020-11-23 22:20:39.711 +1bc241c7-6926-4b46-94b7-82d25069f067,true,true,People Ops,People Ops,2020-11-23 22:20:39.643 +e74a1782-f463-4542-8440-66de07d4b083,true,true,Detection & Response,Detection & Response,2020-11-23 22:20:40.643 +d8a8402f-bc1b-4990-9b11-06a52d2de68a,true,true,Platform Security,Platform Security,2020-11-23 22:20:41.553 \ No newline at end of file From 43ab6cfc02a024b79793aa3fdf58002906ba7641 Mon Sep 17 00:00:00 2001 From: Savage Fivetran Date: Tue, 21 Oct 2025 12:54:25 -0700 Subject: [PATCH 05/11] add_teams_to_timestamp_model --- .../int_jira__issue_field_history.sql | 21 ++++++++++----- ...nt_jira__pivot_timestamp_field_history.sql | 5 ++-- models/jira__daily_issue_field_history.sql | 26 ++++++++++++------- .../jira__timestamp_issue_field_history.sql | 19 ++++++++++++-- 4 files changed, 51 insertions(+), 20 deletions(-) diff --git a/models/intermediate/field_history/int_jira__issue_field_history.sql b/models/intermediate/field_history/int_jira__issue_field_history.sql index e75e1767..fe3f5f68 100644 --- a/models/intermediate/field_history/int_jira__issue_field_history.sql +++ b/models/intermediate/field_history/int_jira__issue_field_history.sql @@ -12,11 +12,13 @@ fields as ( ), -team as ( +{% if var('jira_using_teams', True) %} +teams as ( select * - from {{ ref('stg_jira__team') }} + from {{ ref('stg_jira__team') }} ), +{% endif %} joined as ( @@ -24,21 +26,28 @@ joined as ( field_history.field_id, field_history.issue_id, field_history.updated_at, + {% if var('jira_using_teams', True) %} -- if the field is 'team', we want to replace the value with the team name -- otherwise, just use the value as is - case when lower(fields.field_name) = 'team' then team.team_name + case when lower(fields.field_name) = 'team' then teams.team_name else field_history.field_value end as field_value, + {% else %} + field_history.field_value, + {% endif %} lower(fields.field_name) as field_name, field_history.is_active, + field_history.author_id, field_history._fivetran_synced from field_history join fields on fields.field_id = field_history.field_id - left join team on lower(fields.field_name) = 'team' - and cast(field_history.field_value as {{ dbt.type_string() }}) = cast(team.team_id as {{ dbt.type_string() }}) + {% if var('jira_using_teams', True) %} + left join teams on lower(fields.field_name) = 'team' + and cast(field_history.field_value as {{ dbt.type_string() }}) = cast(teams.team_id as {{ dbt.type_string() }}) + {% endif %} ) select * -from joined \ No newline at end of file +from joined \ No newline at end of file diff --git a/models/intermediate/field_history/int_jira__pivot_timestamp_field_history.sql b/models/intermediate/field_history/int_jira__pivot_timestamp_field_history.sql index 4c213174..b4d4b668 100644 --- a/models/intermediate/field_history/int_jira__pivot_timestamp_field_history.sql +++ b/models/intermediate/field_history/int_jira__pivot_timestamp_field_history.sql @@ -54,7 +54,7 @@ limit_to_relevant_fields as ( combine_field_history.* from combine_field_history where lower(field_id) = 'status' - or lower(field_name) in ('sprint', 'story points', 'story point estimate' + or lower(field_name) in ('sprint', 'story points', 'story point estimate', 'team' {%- for col in var('issue_field_history_columns', []) -%} ,'{{ (col|lower) }}' {%- endfor -%} ) @@ -82,7 +82,8 @@ final as ( max(case when lower(field_id) = 'status' then field_value end) as status, max(case when lower(field_name) = 'sprint' then field_value end) as sprint, max(case when lower(field_name) = 'story points' then field_value end) as story_points, - max(case when lower(field_name) = 'story point estimate' then field_value end) as story_point_estimate + max(case when lower(field_name) = 'story point estimate' then field_value end) as story_point_estimate, + max(case when lower(field_name) = 'team' then field_value end) as team {% for col in var('issue_field_history_columns', []) -%} {% if col|lower not in ['story points', 'story point estimate'] %} diff --git a/models/jira__daily_issue_field_history.sql b/models/jira__daily_issue_field_history.sql index 7cc69ee0..94865f49 100644 --- a/models/jira__daily_issue_field_history.sql +++ b/models/jira__daily_issue_field_history.sql @@ -69,10 +69,13 @@ issue_types as ( from {{ ref('stg_jira__issue_type') }} ), +{% if var('jira_using_teams', True) %} teams as ( + select * from {{ ref('stg_jira__team') }} ), +{% endif %} {% if var('jira_using_components', True) %} components as ( @@ -93,7 +96,7 @@ joined as ( {% if col.name|lower == 'components' and var('jira_using_components', True) %} , coalesce(pivoted_daily_history.components, most_recent_data.components) as components - {% elif col.name|lower == 'team' %} + {% elif col.name|lower == 'team' and var('jira_using_teams', True) %} , coalesce(pivoted_daily_history.team, most_recent_data.team) as team {% elif col.name|lower not in ['issue_day_id', 'issue_id', 'valid_starting_on', 'valid_starting_at_week', 'components', 'team'] %} @@ -107,10 +110,9 @@ joined as ( {% if col.name|lower == 'components' and var('jira_using_components', True) %} , pivoted_daily_history.components - {% elif col.name|lower == 'team' %} + {% elif col.name|lower == 'team' and var('jira_using_teams', True) %} , pivoted_daily_history.team - {% elif col.name|lower not in ['issue_day_id', 'issue_id', 'valid_starting_on', 'valid_starting_at_week', 'components','team'] %} , pivoted_daily_history.{{ col.name }} @@ -150,7 +152,7 @@ set_values as ( , coalesce(issue_types.issue_type_name, joined.issue_type) as issue_type , sum(case when joined.issue_type is null then 0 else 1 end) over (partition by issue_id order by date_day rows unbounded preceding) as issue_type_field_partition - {% elif col.name|lower == 'team' %} + {% elif col.name|lower == 'team' and var('jira_using_teams', True) %} , coalesce(teams.team_name, joined.team) as team , sum(case when joined.team is null then 0 else 1 end) over ( partition by issue_id order by date_day rows unbounded preceding) as team_field_partition @@ -175,7 +177,7 @@ set_values as ( left join issue_types on cast(issue_types.issue_type_id as {{ dbt.type_string() }}) = joined.issue_type - {% elif col.name|lower == 'team' %} + {% elif col.name|lower == 'team'and var('jira_using_teams', True) %} left join teams on cast(teams.team_name as {{ dbt.type_string() }}) = joined.team @@ -202,7 +204,7 @@ fill_values as ( partition by issue_id, component_field_partition order by date_day asc rows between unbounded preceding and current row) as components - {% elif col.name|lower == 'team' %} + {% elif col.name|lower == 'team' and var('jira_using_teams', True) %} , first_value(team) over ( partition by issue_id, team_field_partition order by date_day asc rows between unbounded preceding and current row) as team @@ -223,13 +225,15 @@ fix_null_values as ( select date_day, - issue_id, - case when team = 'is_null' then null else team end as team + issue_id {% for col in pivot_data_columns %} {% if col.name|lower == 'components' and var('jira_using_components', True) %} , case when components = 'is_null' then null else components end as components + {% elif col.name|lower == 'team' and var('jira_using_teams', True) %} + , case when team = 'is_null' then null else team end as team + {% elif col.name|lower not in ['issue_id','issue_day_id','valid_starting_on', 'valid_starting_at_week', 'status', 'components','team'] %} -- we de-nulled the true null values earlier in order to differentiate them from nulls that just needed to be backfilled , case when {{ col.name }} = 'is_null' then null else {{ col.name }} end as {{ col.name }} @@ -247,13 +251,15 @@ surrogate_key as ( fix_null_values.date_day, cast({{ dbt.date_trunc('week', 'fix_null_values.date_day') }} as date) as date_week, fix_null_values.issue_id, - statuses.status_name as status, - fix_null_values.team + statuses.status_name as status {% for col in pivot_data_columns %} {% if col.name|lower == 'components' and var('jira_using_components', True) %} , fix_null_values.components as components + {% elif col.name|lower == 'team' and var('jira_using_teams', True) %} + , fix_null_values.team as team + {% elif col.name|lower not in ['issue_id','issue_day_id','valid_starting_on', 'valid_starting_at_week', 'status', 'components','team'] %} , fix_null_values.{{ col.name }} as {{ col.name }} diff --git a/models/jira__timestamp_issue_field_history.sql b/models/jira__timestamp_issue_field_history.sql index d5e829b9..d96f520d 100644 --- a/models/jira__timestamp_issue_field_history.sql +++ b/models/jira__timestamp_issue_field_history.sql @@ -27,6 +27,14 @@ components as ( ), {% endif %} +{% if var('jira_using_teams', True) %} +teams as ( + + select * + from {{ ref('stg_jira__team') }} +), +{% endif %} + field_option as ( select * @@ -48,7 +56,7 @@ create_validity_periods as ( author_id -- list of exception columns - {% set exception_cols = ['issue_id', 'issue_timestamp_id', 'updated_at', 'updated_at_week', 'status', 'author_id', 'components', 'issue_type'] %} + {% set exception_cols = ['issue_id', 'issue_timestamp_id', 'updated_at', 'updated_at_week', 'status', 'author_id', 'issue_type'] %} {% for col in pivot_data_columns %} {% if col.name|lower not in exception_cols %} @@ -71,7 +79,7 @@ final as ( create_validity_periods.author_id -- list of exception columns - {% set exception_cols = ['issue_id', 'issue_timestamp_id', 'updated_at', 'updated_at_week', 'status', 'author_id', 'components', 'issue_type'] %} + {% set exception_cols = ['issue_id', 'issue_timestamp_id', 'updated_at', 'updated_at_week', 'status', 'author_id', 'components', 'issue_type', 'team'] %} {% for col in pivot_data_columns %} {% if col.name|lower == 'components' and var('jira_using_components', True) %} @@ -80,6 +88,9 @@ final as ( {% elif col.name|lower == 'issue_type' %} , coalesce(issue_types.issue_type_name, create_validity_periods.issue_type) as issue_type + {% elif col.name|lower == 'team' and var('jira_using_teams', True) %} + , coalesce(teams.team_name, create_validity_periods.team) as team + {% elif col.name|lower not in exception_cols %} , coalesce(field_option_{{ col.name }}.field_option_name, create_validity_periods.{{ col.name }}) as {{ col.name }} @@ -104,6 +115,10 @@ final as ( left join issue_types on cast(issue_types.issue_type_id as {{ dbt.type_string() }}) = create_validity_periods.issue_type + {% elif col.name|lower == 'team' and var('jira_using_teams', True) %} + left join teams + on cast(teams.team_name as {{ dbt.type_string() }}) = create_validity_periods.team + {% elif col.name|lower not in exception_cols %} left join field_option as field_option_{{ col.name }} on cast(field_option_{{ col.name }}.field_id as {{ dbt.type_string() }}) = create_validity_periods.{{ col.name }} From 79644593974bc7884afd17e9417ca7cb4e6ab79a Mon Sep 17 00:00:00 2001 From: Savage Fivetran Date: Tue, 21 Oct 2025 13:13:32 -0700 Subject: [PATCH 06/11] update_seed_data --- integration_tests/seeds/issue_field_history.csv | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/integration_tests/seeds/issue_field_history.csv b/integration_tests/seeds/issue_field_history.csv index ebf83737..6f7bf507 100644 --- a/integration_tests/seeds/issue_field_history.csv +++ b/integration_tests/seeds/issue_field_history.csv @@ -2,10 +2,10 @@ field_id,issue_id,time,_fivetran_synced,value,is_active,author_id created,10027,2020-11-10 19:19:41.472,2020-11-12 12:20:53.478,2020-11-10T19:19:41.472Z,true,5e61c1c5c8bee8005f04c88b customfield_10104,10027,2020-11-10 19:19:41.472,2020-11-12 12:20:53.472,3.0,true,5e61c1c5c8bee8005f04c88b summary,10027,2020-11-10 19:19:41.472,2020-11-12 12:20:53.500,"As a developer, I'd like to update story status during the sprint >> Click the Active sprints link at the top right of the screen to go to the Active sprints where the current Sprint's items can be updated",true,5e61c1c5c8bee8005f04c88b -components,10018,2020-11-10 19:19:41.472,2020-11-19 12:20:53.500,10019,true,5e61c1c5c8bee8005f04c88c -summary,10018,2020-11-10 19:19:41.472,2020-11-19 12:20:53.500,10026,true,5e61c1c5c8bee8005f04c88c -epic,10015,2020-11-10 19:19:41.472,2020-11-19 12:20:53.500,1111111,true,5e61c1c5c8bee8005f04c88d -epiclink,10018,2020-11-10 19:19:41.472,2020-11-12 12:20:53.500,https://ulr-here/epic-key,true,5e61c1c5c8bee8005f04c88c -customfield_10001,10027,2020-11-10 19:19:41.472,2020-11-12 12:20:53.500,01e9ac93-bccf-4ad2-af92-3317a37ec67b,true,5e61c1c5c8bee8005f04c88c -customfield_10001,10018,2020-11-10 19:19:41.472,2020-11-12 12:20:53.500,1bc241c7-6926-4b46-94b7-82d25069f067,true,5e61c1c5c8bee8005f04c88c -customfield_10001,10015,2020-11-10 19:19:41.472,2020-11-12 12:20:53.500,e74a1782-f463-4542-8440-66de07d4b083,true,5e61c1c5c8bee8005f04c88c \ No newline at end of file +components,10018,2020-11-10 19:19:41.472,2020-11-19 12:20:53.501,10019,true,5e61c1c5c8bee8005f04c88c +summary,10018,2020-11-10 19:19:41.472,2020-11-19 12:20:53.502,10026,true,5e61c1c5c8bee8005f04c88c +epic,10015,2020-11-10 19:19:41.472,2020-11-19 12:20:53.503,1111111,true,5e61c1c5c8bee8005f04c88d +epiclink,10018,2020-11-10 19:19:41.472,2020-11-12 12:20:53.504,https://ulr-here/epic-key,true,5e61c1c5c8bee8005f04c88c +customfield_10001,10027,2020-11-10 19:19:41.472,2020-11-12 12:20:53.505,01e9ac93-bccf-4ad2-af92-3317a37ec67b,true,5e61c1c5c8bee8005f04c88c +customfield_10001,10018,2020-11-10 19:19:41.472,2020-11-12 12:20:53.506,1bc241c7-6926-4b46-94b7-82d25069f067,true,5e61c1c5c8bee8005f04c88c +customfield_10001,10015,2020-11-10 19:19:41.472,2020-11-12 12:20:53.507,e74a1782-f463-4542-8440-66de07d4b083,true,5e61c1c5c8bee8005f04c88c \ No newline at end of file From 66ac4c9e64bd58ee9dec1694da1fbdd19c1bc2c7 Mon Sep 17 00:00:00 2001 From: Savage Fivetran Date: Tue, 21 Oct 2025 13:59:35 -0700 Subject: [PATCH 07/11] update_seed_data --- .../seeds/issue_field_history.csv | 18 +++++++++--------- .../seeds/issue_multiselect_history.csv | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/integration_tests/seeds/issue_field_history.csv b/integration_tests/seeds/issue_field_history.csv index 6f7bf507..04f09842 100644 --- a/integration_tests/seeds/issue_field_history.csv +++ b/integration_tests/seeds/issue_field_history.csv @@ -1,11 +1,11 @@ field_id,issue_id,time,_fivetran_synced,value,is_active,author_id created,10027,2020-11-10 19:19:41.472,2020-11-12 12:20:53.478,2020-11-10T19:19:41.472Z,true,5e61c1c5c8bee8005f04c88b -customfield_10104,10027,2020-11-10 19:19:41.472,2020-11-12 12:20:53.472,3.0,true,5e61c1c5c8bee8005f04c88b -summary,10027,2020-11-10 19:19:41.472,2020-11-12 12:20:53.500,"As a developer, I'd like to update story status during the sprint >> Click the Active sprints link at the top right of the screen to go to the Active sprints where the current Sprint's items can be updated",true,5e61c1c5c8bee8005f04c88b -components,10018,2020-11-10 19:19:41.472,2020-11-19 12:20:53.501,10019,true,5e61c1c5c8bee8005f04c88c -summary,10018,2020-11-10 19:19:41.472,2020-11-19 12:20:53.502,10026,true,5e61c1c5c8bee8005f04c88c -epic,10015,2020-11-10 19:19:41.472,2020-11-19 12:20:53.503,1111111,true,5e61c1c5c8bee8005f04c88d -epiclink,10018,2020-11-10 19:19:41.472,2020-11-12 12:20:53.504,https://ulr-here/epic-key,true,5e61c1c5c8bee8005f04c88c -customfield_10001,10027,2020-11-10 19:19:41.472,2020-11-12 12:20:53.505,01e9ac93-bccf-4ad2-af92-3317a37ec67b,true,5e61c1c5c8bee8005f04c88c -customfield_10001,10018,2020-11-10 19:19:41.472,2020-11-12 12:20:53.506,1bc241c7-6926-4b46-94b7-82d25069f067,true,5e61c1c5c8bee8005f04c88c -customfield_10001,10015,2020-11-10 19:19:41.472,2020-11-12 12:20:53.507,e74a1782-f463-4542-8440-66de07d4b083,true,5e61c1c5c8bee8005f04c88c \ No newline at end of file +customfield_10104,10027,2020-11-10 19:19:41.473,2020-11-12 12:20:53.472,3.0,true,5e61c1c5c8bee8005f04c88b +summary,10027,2020-11-10 19:19:41.474,2020-11-12 12:20:53.500,"As a developer, I'd like to update story status during the sprint >> Click the Active sprints link at the top right of the screen to go to the Active sprints where the current Sprint's items can be updated",true,5e61c1c5c8bee8005f04c88b +components,10018,2020-11-10 19:19:41.475,2020-11-19 12:20:53.501,10019,true,5e61c1c5c8bee8005f04c88c +summary,10018,2020-11-10 19:19:41.476,2020-11-19 12:20:53.502,10026,true,5e61c1c5c8bee8005f04c88c +epic,10015,2020-11-10 19:19:41.477,2020-11-19 12:20:53.503,1111111,true,5e61c1c5c8bee8005f04c88d +epiclink,10018,2020-11-10 19:19:41.478,2020-11-12 12:20:53.504,https://ulr-here/epic-key,true,5e61c1c5c8bee8005f04c88c +customfield_10001,10027,2020-11-10 19:19:41.479,2020-11-12 12:20:53.505,01e9ac93-bccf-4ad2-af92-3317a37ec67b,true,5e61c1c5c8bee8005f04c88c +customfield_10001,10018,2020-11-10 19:19:41.480,2020-11-12 12:20:53.506,1bc241c7-6926-4b46-94b7-82d25069f067,true,5e61c1c5c8bee8005f04c88c +customfield_10001,10015,2020-11-10 19:19:41.481,2020-11-12 12:20:53.507,e74a1782-f463-4542-8440-66de07d4b083,true,5e61c1c5c8bee8005f04c88c \ No newline at end of file diff --git a/integration_tests/seeds/issue_multiselect_history.csv b/integration_tests/seeds/issue_multiselect_history.csv index d239ce28..50187876 100644 --- a/integration_tests/seeds/issue_multiselect_history.csv +++ b/integration_tests/seeds/issue_multiselect_history.csv @@ -1,5 +1,5 @@ _fivetran_id,time,_fivetran_synced,field_id,issue_id,value,author_id,is_active w4UiT+hPMxJp3RhL/YFJm3uWL5U=,2020-11-10 19:19:41.472,2020-11-12 12:20:53.506,subtasks,10027,0,5e61c1c5c8bee8005f04c88b,true -4pVgGn0qSqR2hCmMdo4wWHXmgew=,2020-11-10 19:19:41.472,2020-11-12 12:20:53.479,customfield_10021,10027,0,5e61c1c5c8bee8005f04c88b,true -/zrY8m6q0VMW6ia1jGIerXqLIgQ=,2020-11-10 19:19:41.472,2020-11-12 12:20:53.479,customfield_10020,10027,0,5e61c1c5c8bee8005f04c88b,true -3p3gGn0qSqR2hCmMdo4wWHXa32m=,2020-11-10 19:19:41.472,2020-11-12 12:20:53.479,components,10027,0,5e61c1c5c8bee8005f04c88b,true \ No newline at end of file +4pVgGn0qSqR2hCmMdo4wWHXmgew=,2020-11-10 19:19:42.472,2020-11-12 12:20:53.479,customfield_10021,10027,0,5e61c1c5c8bee8005f04c88b,true +/zrY8m6q0VMW6ia1jGIerXqLIgQ=,2020-11-10 19:19:43.472,2020-11-12 12:20:53.479,customfield_10020,10027,0,5e61c1c5c8bee8005f04c88b,true +3p3gGn0qSqR2hCmMdo4wWHXa32m=,2020-11-10 19:19:44.472,2020-11-12 12:20:53.479,components,10027,0,5e61c1c5c8bee8005f04c88b,true \ No newline at end of file From 2036450c5eefa96e1e9a00ed086d35040cd995e1 Mon Sep 17 00:00:00 2001 From: Savage Fivetran Date: Tue, 21 Oct 2025 14:42:22 -0700 Subject: [PATCH 08/11] add_teams_to_exception_columns_list --- models/jira__timestamp_issue_field_history.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/jira__timestamp_issue_field_history.sql b/models/jira__timestamp_issue_field_history.sql index d96f520d..9f24fd6f 100644 --- a/models/jira__timestamp_issue_field_history.sql +++ b/models/jira__timestamp_issue_field_history.sql @@ -56,7 +56,7 @@ create_validity_periods as ( author_id -- list of exception columns - {% set exception_cols = ['issue_id', 'issue_timestamp_id', 'updated_at', 'updated_at_week', 'status', 'author_id', 'issue_type'] %} + {% set exception_cols = ['issue_id', 'issue_timestamp_id', 'updated_at', 'updated_at_week', 'status', 'author_id', 'issue_type','components',] %} {% for col in pivot_data_columns %} {% if col.name|lower not in exception_cols %} From b8901249aed7960768d5b223e63d18191c0f65a8 Mon Sep 17 00:00:00 2001 From: Savage Fivetran Date: Tue, 21 Oct 2025 15:04:51 -0700 Subject: [PATCH 09/11] update_columns --- models/jira__timestamp_issue_field_history.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/jira__timestamp_issue_field_history.sql b/models/jira__timestamp_issue_field_history.sql index 9f24fd6f..001ff3a1 100644 --- a/models/jira__timestamp_issue_field_history.sql +++ b/models/jira__timestamp_issue_field_history.sql @@ -56,7 +56,7 @@ create_validity_periods as ( author_id -- list of exception columns - {% set exception_cols = ['issue_id', 'issue_timestamp_id', 'updated_at', 'updated_at_week', 'status', 'author_id', 'issue_type','components',] %} + {% set exception_cols = ['issue_id', 'issue_timestamp_id', 'updated_at', 'updated_at_week', 'status', 'author_id', 'components', 'issue_type','team'] %} {% for col in pivot_data_columns %} {% if col.name|lower not in exception_cols %} From 6fa72986a88a117e342139883872d39b8402818c Mon Sep 17 00:00:00 2001 From: Savage Fivetran Date: Wed, 22 Oct 2025 11:44:16 -0700 Subject: [PATCH 10/11] remove_team__hardcoding --- CHANGELOG.md | 10 +++--- integration_tests/dbt_project.yml | 2 +- .../int_jira__issue_field_history.sql | 31 ++----------------- .../int_jira__pivot_daily_field_history.sql | 7 ++--- ...nt_jira__pivot_timestamp_field_history.sql | 5 ++- models/jira__daily_issue_field_history.sql | 2 +- .../jira__timestamp_issue_field_history.sql | 4 +-- 7 files changed, 16 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f87a2dc0..b4a08427 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,8 @@ | Data Model(s) | Change type | Old | New | Notes | | ---------- | ----------- | -------- | -------- | ----- | -| `stg_jira__team` | New model | N/A | New staging model | Added staging model for Jira teams functionality. | -| `jira__daily_issue_field_history` | Column addition | N/A | `team` | Added team column to track team assignments for issues over time. | -| `jira__issue_enhanced` | Column addition | N/A | `team` | Added team column to provide current team assignment for each issue. | +| [stg_jira__team_tmp](https://fivetran.github.io/dbt_jira/#!/model/model.jira.stg_jira__team) | New Model | N/A | New tmp model | Added temp model as Jira teams source. | +| [stg_jira__team](https://fivetran.github.io/dbt_jira/#!/model/model.jira.stg_jira__team) |New Model | N/A | New staging model | Added staging model for Jira teams functionality | [jira__timestamp_issue_field_history](https://fivetran.github.io/dbt_jira/#!/model/model.jira.jira__timestamp_issue_field_history) | New Model | | | SCD Type 2 table tracking field changes at timestamp level with validity periods. Each record shows complete field state during a time period with `valid_from`/`valid_until` timestamps. | | [int_jira__pivot_timestamp_field_history](https://fivetran.github.io/dbt_jira/#!/model/model.jira.int_jira__pivot_timestamp_field_history) | New Model | | | Pivots field history into columns at timestamp level, capturing ALL field changes rather than daily snapshots. | | [int_jira__timestamp_field_history_scd](https://fivetran.github.io/dbt_jira/#!/model/model.jira.int_jira__timestamp_field_history_scd) | New Model | | | SCD logic that backfills field values at timestamp level, showing complete field state at each update timestamp. | @@ -17,9 +16,8 @@ ## Feature Update - Added support for Jira teams functionality by introducing staging models `stg_jira__team` and `stg_jira__team_tmp`. -- Enhanced issue field history models to include team information by joining with team data when the field type is 'team'. -- Added team column to the `jira__daily_issue_field_history` model to track team assignments over time. -- Added team column to the `jira__issue_enhanced` model to provide current team assignment for each issue. +- Enhanced issue field history models to include team information if teams are enabled for the customer. + - When teams are enabled, team data will populate in the `jira__daily_issue_field_history` model to track team assignments over time and the `jira__issue_enhanced` model to provide current team assignment for each issue. ## Under the Hood - Added new seed file `team.csv` for integration testing. diff --git a/integration_tests/dbt_project.yml b/integration_tests/dbt_project.yml index ae2b475f..d747c36c 100644 --- a/integration_tests/dbt_project.yml +++ b/integration_tests/dbt_project.yml @@ -5,7 +5,7 @@ profile: 'integration_tests' vars: # Comment out the below when generating docs - issue_field_history_columns: ['summary', 'components'] # @docs-ignore + issue_field_history_columns: ['summary', 'components','team'] # @docs-ignore jira: jira_schema: jira_integrations_tests_42 diff --git a/models/intermediate/field_history/int_jira__issue_field_history.sql b/models/intermediate/field_history/int_jira__issue_field_history.sql index fe3f5f68..ae51d9d7 100644 --- a/models/intermediate/field_history/int_jira__issue_field_history.sql +++ b/models/intermediate/field_history/int_jira__issue_field_history.sql @@ -12,42 +12,17 @@ fields as ( ), -{% if var('jira_using_teams', True) %} -teams as ( - - select * - from {{ ref('stg_jira__team') }} -), -{% endif %} - joined as ( select - field_history.field_id, - field_history.issue_id, - field_history.updated_at, - {% if var('jira_using_teams', True) %} - -- if the field is 'team', we want to replace the value with the team name - -- otherwise, just use the value as is - case when lower(fields.field_name) = 'team' then teams.team_name - else field_history.field_value end as field_value, - {% else %} - field_history.field_value, - {% endif %} - lower(fields.field_name) as field_name, - field_history.is_active, - field_history.author_id, - field_history._fivetran_synced + field_history.*, + lower(fields.field_name) as field_name from field_history join fields on fields.field_id = field_history.field_id - {% if var('jira_using_teams', True) %} - left join teams on lower(fields.field_name) = 'team' - and cast(field_history.field_value as {{ dbt.type_string() }}) = cast(teams.team_id as {{ dbt.type_string() }}) - {% endif %} ) select * -from joined \ No newline at end of file +from joined \ No newline at end of file diff --git a/models/intermediate/field_history/int_jira__pivot_daily_field_history.sql b/models/intermediate/field_history/int_jira__pivot_daily_field_history.sql index f8cac638..d23f3192 100644 --- a/models/intermediate/field_history/int_jira__pivot_daily_field_history.sql +++ b/models/intermediate/field_history/int_jira__pivot_daily_field_history.sql @@ -94,14 +94,14 @@ get_valid_dates as ( ), limit_to_relevant_fields as ( - -- let's remove unnecessary rows moving forward and grab field names + -- let's remove unncessary rows moving forward and grab field names select get_valid_dates.* from get_valid_dates where lower(field_id) = 'status' - or lower(field_name) in ('sprint', 'story points', 'story point estimate', 'team' + or lower(field_name) in ('sprint', 'story points', 'story point estimate' {%- for col in var('issue_field_history_columns', []) -%} ,'{{ (col|lower) }}' {%- endfor -%} ) @@ -157,8 +157,7 @@ pivot_out as ( max(case when lower(field_id) = 'status' then field_value end) as status, max(case when lower(field_name) = 'sprint' then field_value end) as sprint, max(case when lower(field_name) = 'story points' then field_value end) as story_points, - max(case when lower(field_name) = 'story point estimate' then field_value end) as story_point_estimate, - max(case when lower(field_name) = 'team' then field_value end) as team + max(case when lower(field_name) = 'story point estimate' then field_value end) as story_point_estimate {% for col in var('issue_field_history_columns', []) -%} {% if col|lower not in ['story points', 'story point estimate'] %} diff --git a/models/intermediate/field_history/int_jira__pivot_timestamp_field_history.sql b/models/intermediate/field_history/int_jira__pivot_timestamp_field_history.sql index b4d4b668..4c213174 100644 --- a/models/intermediate/field_history/int_jira__pivot_timestamp_field_history.sql +++ b/models/intermediate/field_history/int_jira__pivot_timestamp_field_history.sql @@ -54,7 +54,7 @@ limit_to_relevant_fields as ( combine_field_history.* from combine_field_history where lower(field_id) = 'status' - or lower(field_name) in ('sprint', 'story points', 'story point estimate', 'team' + or lower(field_name) in ('sprint', 'story points', 'story point estimate' {%- for col in var('issue_field_history_columns', []) -%} ,'{{ (col|lower) }}' {%- endfor -%} ) @@ -82,8 +82,7 @@ final as ( max(case when lower(field_id) = 'status' then field_value end) as status, max(case when lower(field_name) = 'sprint' then field_value end) as sprint, max(case when lower(field_name) = 'story points' then field_value end) as story_points, - max(case when lower(field_name) = 'story point estimate' then field_value end) as story_point_estimate, - max(case when lower(field_name) = 'team' then field_value end) as team + max(case when lower(field_name) = 'story point estimate' then field_value end) as story_point_estimate {% for col in var('issue_field_history_columns', []) -%} {% if col|lower not in ['story points', 'story point estimate'] %} diff --git a/models/jira__daily_issue_field_history.sql b/models/jira__daily_issue_field_history.sql index 94865f49..75d77492 100644 --- a/models/jira__daily_issue_field_history.sql +++ b/models/jira__daily_issue_field_history.sql @@ -179,7 +179,7 @@ set_values as ( {% elif col.name|lower == 'team'and var('jira_using_teams', True) %} left join teams - on cast(teams.team_name as {{ dbt.type_string() }}) = joined.team + on cast(teams.team_id as {{ dbt.type_string() }}) = joined.team {% elif col.name|lower not in exception_cols %} left join field_option as field_option_{{ col.name }} diff --git a/models/jira__timestamp_issue_field_history.sql b/models/jira__timestamp_issue_field_history.sql index 001ff3a1..d9f6d2bc 100644 --- a/models/jira__timestamp_issue_field_history.sql +++ b/models/jira__timestamp_issue_field_history.sql @@ -56,7 +56,7 @@ create_validity_periods as ( author_id -- list of exception columns - {% set exception_cols = ['issue_id', 'issue_timestamp_id', 'updated_at', 'updated_at_week', 'status', 'author_id', 'components', 'issue_type','team'] %} + {% set exception_cols = ['issue_id', 'issue_timestamp_id', 'updated_at', 'updated_at_week', 'status', 'author_id', 'issue_type'] %} {% for col in pivot_data_columns %} {% if col.name|lower not in exception_cols %} @@ -117,7 +117,7 @@ final as ( {% elif col.name|lower == 'team' and var('jira_using_teams', True) %} left join teams - on cast(teams.team_name as {{ dbt.type_string() }}) = create_validity_periods.team + on cast(teams.team_id as {{ dbt.type_string() }}) = create_validity_periods.team {% elif col.name|lower not in exception_cols %} left join field_option as field_option_{{ col.name }} From 10539c7e121b3d1bd8b87c89dbc3516cbae54f02 Mon Sep 17 00:00:00 2001 From: Savage Fivetran Date: Thu, 23 Oct 2025 10:42:47 -0700 Subject: [PATCH 11/11] update_changelog --- CHANGELOG.md | 6 +++--- integration_tests/dbt_project.yml | 2 +- .../field_history/int_jira__pivot_daily_field_history.sql | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4a08427..f44288f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,12 @@ # dbt_jira v1.1.0 ## Schema/Data Change -**9 total changes • 7 possible breaking changes** +**8 total changes • 6 possible breaking changes** | Data Model(s) | Change type | Old | New | Notes | | ---------- | ----------- | -------- | -------- | ----- | -| [stg_jira__team_tmp](https://fivetran.github.io/dbt_jira/#!/model/model.jira.stg_jira__team) | New Model | N/A | New tmp model | Added temp model as Jira teams source. | -| [stg_jira__team](https://fivetran.github.io/dbt_jira/#!/model/model.jira.stg_jira__team) |New Model | N/A | New staging model | Added staging model for Jira teams functionality +| [stg_jira__team_tmp](https://fivetran.github.io/dbt_jira/#!/model/model.jira.stg_jira__team) | New Model | | | Added temp model as Jira teams source. | +| [stg_jira__team](https://fivetran.github.io/dbt_jira/#!/model/model.jira.stg_jira__team) |New Model | | | Added staging model for Jira teams functionality | [jira__timestamp_issue_field_history](https://fivetran.github.io/dbt_jira/#!/model/model.jira.jira__timestamp_issue_field_history) | New Model | | | SCD Type 2 table tracking field changes at timestamp level with validity periods. Each record shows complete field state during a time period with `valid_from`/`valid_until` timestamps. | | [int_jira__pivot_timestamp_field_history](https://fivetran.github.io/dbt_jira/#!/model/model.jira.int_jira__pivot_timestamp_field_history) | New Model | | | Pivots field history into columns at timestamp level, capturing ALL field changes rather than daily snapshots. | | [int_jira__timestamp_field_history_scd](https://fivetran.github.io/dbt_jira/#!/model/model.jira.int_jira__timestamp_field_history_scd) | New Model | | | SCD logic that backfills field values at timestamp level, showing complete field state at each update timestamp. | diff --git a/integration_tests/dbt_project.yml b/integration_tests/dbt_project.yml index d747c36c..998c0c3b 100644 --- a/integration_tests/dbt_project.yml +++ b/integration_tests/dbt_project.yml @@ -5,7 +5,7 @@ profile: 'integration_tests' vars: # Comment out the below when generating docs - issue_field_history_columns: ['summary', 'components','team'] # @docs-ignore + issue_field_history_columns: ['summary', 'components', 'team'] # @docs-ignore jira: jira_schema: jira_integrations_tests_42 diff --git a/models/intermediate/field_history/int_jira__pivot_daily_field_history.sql b/models/intermediate/field_history/int_jira__pivot_daily_field_history.sql index d23f3192..ac8bb6db 100644 --- a/models/intermediate/field_history/int_jira__pivot_daily_field_history.sql +++ b/models/intermediate/field_history/int_jira__pivot_daily_field_history.sql @@ -94,7 +94,7 @@ get_valid_dates as ( ), limit_to_relevant_fields as ( - -- let's remove unncessary rows moving forward and grab field names + -- let's remove unnecessary rows moving forward and grab field names select get_valid_dates.*