From de7919581b6988d0024a253779c77aabd4e001a4 Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Sun, 7 Sep 2025 17:07:35 +0700 Subject: [PATCH 1/9] hotfix: ensure reproduction rate always appears in JSON response even when null - Fix RT values to always be included in API responses for consistency - Update ReproductionRate struct to use pointer types for proper null handling - Modify transformation logic to always include RT structure - Update tests to handle new pointer-based RT values - Bump version to 2.0.1 for hotfix release This ensures API consumers always receive the reproduction_rate object structure, with null values when data is not available. --- internal/handler/covid_handler_test.go | 2 +- internal/models/national_case_response.go | 18 ++++++------- .../models/national_case_response_test.go | 26 +++++++++++++------ internal/models/province_case_response.go | 14 +++++----- .../models/province_case_response_test.go | 12 ++++----- 5 files changed, 39 insertions(+), 33 deletions(-) diff --git a/internal/handler/covid_handler_test.go b/internal/handler/covid_handler_test.go index ffb9625..421f175 100644 --- a/internal/handler/covid_handler_test.go +++ b/internal/handler/covid_handler_test.go @@ -286,7 +286,7 @@ func TestCovidHandler_HealthCheck(t *testing.T) { assert.True(t, ok) assert.Equal(t, "degraded", data["status"]) assert.Equal(t, "COVID-19 API", data["service"]) - assert.Equal(t, "2.0.0", data["version"]) + assert.Equal(t, "2.0.1", data["version"]) assert.Contains(t, data, "database") dbData, ok := data["database"].(map[string]interface{}) diff --git a/internal/models/national_case_response.go b/internal/models/national_case_response.go index 06a2b40..3b29586 100644 --- a/internal/models/national_case_response.go +++ b/internal/models/national_case_response.go @@ -42,9 +42,9 @@ type CasePercentages struct { // ReproductionRate represents the R-value with confidence bounds type ReproductionRate struct { - Value float64 `json:"value"` - UpperBound float64 `json:"upper_bound"` - LowerBound float64 `json:"lower_bound"` + Value *float64 `json:"value"` + UpperBound *float64 `json:"upper_bound"` + LowerBound *float64 `json:"lower_bound"` } // TransformToResponse converts a NationalCase model to the response format @@ -74,13 +74,11 @@ func (nc *NationalCase) TransformToResponse() NationalCaseResponse { }, } - // Add reproduction rate if available - if nc.Rt != nil && nc.RtUpper != nil && nc.RtLower != nil { - response.Statistics.ReproductionRate = &ReproductionRate{ - Value: *nc.Rt, - UpperBound: *nc.RtUpper, - LowerBound: *nc.RtLower, - } + // Always include reproduction rate structure, even when values are null + response.Statistics.ReproductionRate = &ReproductionRate{ + Value: nc.Rt, + UpperBound: nc.RtUpper, + LowerBound: nc.RtLower, } return response diff --git a/internal/models/national_case_response_test.go b/internal/models/national_case_response_test.go index 27ec345..bf01d90 100644 --- a/internal/models/national_case_response_test.go +++ b/internal/models/national_case_response_test.go @@ -69,14 +69,14 @@ func TestNationalCase_TransformToResponse(t *testing.T) { if response.Statistics.ReproductionRate == nil { t.Error("Expected ReproductionRate to be present") } else { - if response.Statistics.ReproductionRate.Value != rtValue { - t.Errorf("Expected Rt.Value %f, got %f", rtValue, response.Statistics.ReproductionRate.Value) + if *response.Statistics.ReproductionRate.Value != rtValue { + t.Errorf("Expected Rt.Value %f, got %f", rtValue, *response.Statistics.ReproductionRate.Value) } - if response.Statistics.ReproductionRate.UpperBound != rtUpper { - t.Errorf("Expected Rt.UpperBound %f, got %f", rtUpper, response.Statistics.ReproductionRate.UpperBound) + if *response.Statistics.ReproductionRate.UpperBound != rtUpper { + t.Errorf("Expected Rt.UpperBound %f, got %f", rtUpper, *response.Statistics.ReproductionRate.UpperBound) } - if response.Statistics.ReproductionRate.LowerBound != rtLower { - t.Errorf("Expected Rt.LowerBound %f, got %f", rtLower, response.Statistics.ReproductionRate.LowerBound) + if *response.Statistics.ReproductionRate.LowerBound != rtLower { + t.Errorf("Expected Rt.LowerBound %f, got %f", rtLower, *response.Statistics.ReproductionRate.LowerBound) } } @@ -103,8 +103,18 @@ func TestNationalCase_TransformToResponse_NoReproductionRate(t *testing.T) { response := nc.TransformToResponse() - if response.Statistics.ReproductionRate != nil { - t.Error("Expected ReproductionRate to be nil when not provided") + if response.Statistics.ReproductionRate == nil { + t.Error("Expected ReproductionRate to always be present") + } else { + if response.Statistics.ReproductionRate.Value != nil { + t.Error("Expected Rt.Value to be nil when not provided") + } + if response.Statistics.ReproductionRate.UpperBound != nil { + t.Error("Expected Rt.UpperBound to be nil when not provided") + } + if response.Statistics.ReproductionRate.LowerBound != nil { + t.Error("Expected Rt.LowerBound to be nil when not provided") + } } } diff --git a/internal/models/province_case_response.go b/internal/models/province_case_response.go index dfc6994..e802282 100644 --- a/internal/models/province_case_response.go +++ b/internal/models/province_case_response.go @@ -41,7 +41,7 @@ type ProvinceCumulativeCases struct { // ProvinceCaseStatistics contains calculated statistics and metrics for province data type ProvinceCaseStatistics struct { Percentages CasePercentages `json:"percentages"` - ReproductionRate *ReproductionRate `json:"reproduction_rate,omitempty"` + ReproductionRate *ReproductionRate `json:"reproduction_rate"` } // TransformToResponse converts a ProvinceCase model to the response format @@ -86,13 +86,11 @@ func (pc *ProvinceCase) TransformToResponse(date time.Time) ProvinceCaseResponse Province: pc.Province, } - // Add reproduction rate if available - if pc.Rt != nil && pc.RtUpper != nil && pc.RtLower != nil { - response.Statistics.ReproductionRate = &ReproductionRate{ - Value: *pc.Rt, - UpperBound: *pc.RtUpper, - LowerBound: *pc.RtLower, - } + // Always include reproduction rate structure, even when values are null + response.Statistics.ReproductionRate = &ReproductionRate{ + Value: pc.Rt, + UpperBound: pc.RtUpper, + LowerBound: pc.RtLower, } return response diff --git a/internal/models/province_case_response_test.go b/internal/models/province_case_response_test.go index d4abd2d..ceb7558 100644 --- a/internal/models/province_case_response_test.go +++ b/internal/models/province_case_response_test.go @@ -80,9 +80,9 @@ func TestProvinceCase_TransformToResponse(t *testing.T) { Deceased: 6.0, // (300 / 5000) * 100 }, ReproductionRate: &ReproductionRate{ - Value: 1.5, - UpperBound: 1.8, - LowerBound: 1.2, + Value: &[]float64{1.5}[0], + UpperBound: &[]float64{1.8}[0], + LowerBound: &[]float64{1.2}[0], }, }, Province: &Province{ @@ -307,9 +307,9 @@ func TestProvinceCaseWithDate_TransformToResponse(t *testing.T) { Deceased: 6.666666666666667, // (200 / 3000) * 100 }, ReproductionRate: &ReproductionRate{ - Value: 1.2, - UpperBound: 1.5, - LowerBound: 0.9, + Value: &[]float64{1.2}[0], + UpperBound: &[]float64{1.5}[0], + LowerBound: &[]float64{0.9}[0], }, }, Province: &Province{ From 4eed891db4c1f653152c72f8020026e286be10ce Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Sun, 7 Sep 2025 16:58:17 +0700 Subject: [PATCH 2/9] chore(version): bump version to 2.0.1 for hotfix release --- internal/handler/covid_handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/handler/covid_handler.go b/internal/handler/covid_handler.go index e8e3d79..fa09bde 100644 --- a/internal/handler/covid_handler.go +++ b/internal/handler/covid_handler.go @@ -137,7 +137,7 @@ func (h *CovidHandler) HealthCheck(w http.ResponseWriter, r *http.Request) { health := map[string]interface{}{ "status": "healthy", "service": "COVID-19 API", - "version": "2.0.0", + "version": "2.0.1", "timestamp": time.Now().UTC().Format(time.RFC3339), } From 9cfee8d6dce317d7784be2a239e25369d825262b Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Sun, 7 Sep 2025 16:57:10 +0700 Subject: [PATCH 3/9] fix: correct database column typo and ensure RT fields always present in JSON response - Fix database column name typo from 'cumulative_finished_persoon_under_observation' to 'cumulative_finished_person_under_observation' - Remove 'omitempty' from RT fields (rt, rt_upper, rt_lower) to ensure they always appear in JSON responses even when null - Update all SQL queries and tests to use correct column name - Critical production hotfix for database errors and missing RT data --- internal/models/national_case.go | 6 +++--- internal/models/province_case.go | 8 ++++---- internal/repository/province_case_repository.go | 10 +++++----- internal/repository/province_case_repository_test.go | 10 +++++----- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/internal/models/national_case.go b/internal/models/national_case.go index 0e3a5fa..4465020 100644 --- a/internal/models/national_case.go +++ b/internal/models/national_case.go @@ -16,9 +16,9 @@ type NationalCase struct { CumulativePositive int64 `json:"cumulative_positive" db:"cumulative_positive"` CumulativeRecovered int64 `json:"cumulative_recovered" db:"cumulative_recovered"` CumulativeDeceased int64 `json:"cumulative_deceased" db:"cumulative_deceased"` - Rt *float64 `json:"rt,omitempty" db:"rt"` - RtUpper *float64 `json:"rt_upper,omitempty" db:"rt_upper"` - RtLower *float64 `json:"rt_lower,omitempty" db:"rt_lower"` + Rt *float64 `json:"rt" db:"rt"` + RtUpper *float64 `json:"rt_upper" db:"rt_upper"` + RtLower *float64 `json:"rt_lower" db:"rt_lower"` } type NullFloat64 struct { diff --git a/internal/models/province_case.go b/internal/models/province_case.go index b1d9171..456928c 100644 --- a/internal/models/province_case.go +++ b/internal/models/province_case.go @@ -17,12 +17,12 @@ type ProvinceCase struct { CumulativeRecovered int64 `json:"cumulative_recovered" db:"cumulative_recovered"` CumulativeDeceased int64 `json:"cumulative_deceased" db:"cumulative_deceased"` CumulativePersonUnderObservation int64 `json:"cumulative_person_under_observation" db:"cumulative_person_under_observation"` - CumulativeFinishedPersonUnderObservation int64 `json:"cumulative_finished_person_under_observation" db:"cumulative_finished_persoon_under_observation"` + CumulativeFinishedPersonUnderObservation int64 `json:"cumulative_finished_person_under_observation" db:"cumulative_finished_person_under_observation"` CumulativePersonUnderSupervision int64 `json:"cumulative_person_under_supervision" db:"cumulative_person_under_supervision"` CumulativeFinishedPersonUnderSupervision int64 `json:"cumulative_finished_person_under_supervision" db:"cumulative_finished_person_under_supervision"` - Rt *float64 `json:"rt,omitempty" db:"rt"` - RtUpper *float64 `json:"rt_upper,omitempty" db:"rt_upper"` - RtLower *float64 `json:"rt_lower,omitempty" db:"rt_lower"` + Rt *float64 `json:"rt" db:"rt"` + RtUpper *float64 `json:"rt_upper" db:"rt_upper"` + RtLower *float64 `json:"rt_lower" db:"rt_lower"` Province *Province `json:"province,omitempty"` } diff --git a/internal/repository/province_case_repository.go b/internal/repository/province_case_repository.go index 378f2fe..06a8687 100644 --- a/internal/repository/province_case_repository.go +++ b/internal/repository/province_case_repository.go @@ -30,7 +30,7 @@ func (r *provinceCaseRepository) GetAll() ([]models.ProvinceCaseWithDate, error) pc.person_under_observation, pc.finished_person_under_observation, pc.person_under_supervision, pc.finished_person_under_supervision, pc.cumulative_positive, pc.cumulative_recovered, pc.cumulative_deceased, - pc.cumulative_person_under_observation, pc.cumulative_finished_persoon_under_observation, + pc.cumulative_person_under_observation, pc.cumulative_finished_person_under_observation, pc.cumulative_person_under_supervision, pc.cumulative_finished_person_under_supervision, pc.rt, pc.rt_upper, pc.rt_lower, nc.date, p.name FROM province_cases pc @@ -46,7 +46,7 @@ func (r *provinceCaseRepository) GetByProvinceID(provinceID string) ([]models.Pr pc.person_under_observation, pc.finished_person_under_observation, pc.person_under_supervision, pc.finished_person_under_supervision, pc.cumulative_positive, pc.cumulative_recovered, pc.cumulative_deceased, - pc.cumulative_person_under_observation, pc.cumulative_finished_persoon_under_observation, + pc.cumulative_person_under_observation, pc.cumulative_finished_person_under_observation, pc.cumulative_person_under_supervision, pc.cumulative_finished_person_under_supervision, pc.rt, pc.rt_upper, pc.rt_lower, nc.date, p.name FROM province_cases pc @@ -63,7 +63,7 @@ func (r *provinceCaseRepository) GetByProvinceIDAndDateRange(provinceID string, pc.person_under_observation, pc.finished_person_under_observation, pc.person_under_supervision, pc.finished_person_under_supervision, pc.cumulative_positive, pc.cumulative_recovered, pc.cumulative_deceased, - pc.cumulative_person_under_observation, pc.cumulative_finished_persoon_under_observation, + pc.cumulative_person_under_observation, pc.cumulative_finished_person_under_observation, pc.cumulative_person_under_supervision, pc.cumulative_finished_person_under_supervision, pc.rt, pc.rt_upper, pc.rt_lower, nc.date, p.name FROM province_cases pc @@ -80,7 +80,7 @@ func (r *provinceCaseRepository) GetByDateRange(startDate, endDate time.Time) ([ pc.person_under_observation, pc.finished_person_under_observation, pc.person_under_supervision, pc.finished_person_under_supervision, pc.cumulative_positive, pc.cumulative_recovered, pc.cumulative_deceased, - pc.cumulative_person_under_observation, pc.cumulative_finished_persoon_under_observation, + pc.cumulative_person_under_observation, pc.cumulative_finished_person_under_observation, pc.cumulative_person_under_supervision, pc.cumulative_finished_person_under_supervision, pc.rt, pc.rt_upper, pc.rt_lower, nc.date, p.name FROM province_cases pc @@ -97,7 +97,7 @@ func (r *provinceCaseRepository) GetLatestByProvinceID(provinceID string) (*mode pc.person_under_observation, pc.finished_person_under_observation, pc.person_under_supervision, pc.finished_person_under_supervision, pc.cumulative_positive, pc.cumulative_recovered, pc.cumulative_deceased, - pc.cumulative_person_under_observation, pc.cumulative_finished_persoon_under_observation, + pc.cumulative_person_under_observation, pc.cumulative_finished_person_under_observation, pc.cumulative_person_under_supervision, pc.cumulative_finished_person_under_supervision, pc.rt, pc.rt_upper, pc.rt_lower, nc.date, p.name FROM province_cases pc diff --git a/internal/repository/province_case_repository_test.go b/internal/repository/province_case_repository_test.go index a32160f..a08948d 100644 --- a/internal/repository/province_case_repository_test.go +++ b/internal/repository/province_case_repository_test.go @@ -26,7 +26,7 @@ func TestProvinceCaseRepository_GetAll(t *testing.T) { "person_under_observation", "finished_person_under_observation", "person_under_supervision", "finished_person_under_supervision", "cumulative_positive", "cumulative_recovered", "cumulative_deceased", - "cumulative_person_under_observation", "cumulative_finished_persoon_under_observation", + "cumulative_person_under_observation", "cumulative_finished_person_under_observation", "cumulative_person_under_supervision", "cumulative_finished_person_under_supervision", "rt", "rt_upper", "rt_lower", "date", "name", }).AddRow(1, 1, "11", 50, 40, 2, 10, 8, 5, 3, 500, 400, 20, 100, 80, 50, 30, rt, nil, nil, now, "Aceh") @@ -66,7 +66,7 @@ func TestProvinceCaseRepository_GetByProvinceID(t *testing.T) { "person_under_observation", "finished_person_under_observation", "person_under_supervision", "finished_person_under_supervision", "cumulative_positive", "cumulative_recovered", "cumulative_deceased", - "cumulative_person_under_observation", "cumulative_finished_persoon_under_observation", + "cumulative_person_under_observation", "cumulative_finished_person_under_observation", "cumulative_person_under_supervision", "cumulative_finished_person_under_supervision", "rt", "rt_upper", "rt_lower", "date", "name", }).AddRow(1, 1, provinceID, 50, 40, 2, 10, 8, 5, 3, 500, 400, 20, 100, 80, 50, 30, nil, nil, nil, now, "Aceh") @@ -105,7 +105,7 @@ func TestProvinceCaseRepository_GetByProvinceIDAndDateRange(t *testing.T) { "person_under_observation", "finished_person_under_observation", "person_under_supervision", "finished_person_under_supervision", "cumulative_positive", "cumulative_recovered", "cumulative_deceased", - "cumulative_person_under_observation", "cumulative_finished_persoon_under_observation", + "cumulative_person_under_observation", "cumulative_finished_person_under_observation", "cumulative_person_under_supervision", "cumulative_finished_person_under_supervision", "rt", "rt_upper", "rt_lower", "date", "name", }).AddRow(1, 1, provinceID, 50, 40, 2, 10, 8, 5, 3, 500, 400, 20, 100, 80, 50, 30, nil, nil, nil, now, "Aceh") @@ -142,7 +142,7 @@ func TestProvinceCaseRepository_GetLatestByProvinceID(t *testing.T) { "person_under_observation", "finished_person_under_observation", "person_under_supervision", "finished_person_under_supervision", "cumulative_positive", "cumulative_recovered", "cumulative_deceased", - "cumulative_person_under_observation", "cumulative_finished_persoon_under_observation", + "cumulative_person_under_observation", "cumulative_finished_person_under_observation", "cumulative_person_under_supervision", "cumulative_finished_person_under_supervision", "rt", "rt_upper", "rt_lower", "date", "name", }).AddRow(1, 1, provinceID, 50, 40, 2, 10, 8, 5, 3, 500, 400, 20, 100, 80, 50, 30, rt, nil, nil, now, "Aceh") @@ -178,7 +178,7 @@ func TestProvinceCaseRepository_GetLatestByProvinceID_NotFound(t *testing.T) { "person_under_observation", "finished_person_under_observation", "person_under_supervision", "finished_person_under_supervision", "cumulative_positive", "cumulative_recovered", "cumulative_deceased", - "cumulative_person_under_observation", "cumulative_finished_persoon_under_observation", + "cumulative_person_under_observation", "cumulative_finished_person_under_observation", "cumulative_person_under_supervision", "cumulative_finished_person_under_supervision", "rt", "rt_upper", "rt_lower", "date", "name", }) From 6a0538912b056689f29b8fc6850485577040fd67 Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Sun, 7 Sep 2025 17:27:51 +0700 Subject: [PATCH 4/9] feat: add hotfix branch support to changelog generator - Update generate-changelog.rb to support hotfix/vX.Y.Z branches in addition to release/vX.Y.Z - Add 'Hotfixes' category with high priority for hotfix commits - Update help text and documentation to reflect hotfix support - Enable changelog generation from hotfix branches for patch releases --- generate-changelog.rb | 52 +++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/generate-changelog.rb b/generate-changelog.rb index e269364..636c381 100755 --- a/generate-changelog.rb +++ b/generate-changelog.rb @@ -20,7 +20,7 @@ # - Cross-platform compatibility (Ruby vs. bash-specific features) # # Features: -# - Only runs from release branches (release/vx.x.x) +# - Only runs from release or hotfix branches (release/vx.x.x or hotfix/vx.x.x) # - Categorizes commits by conventional commit types # - Determines semantic version increment automatically # - Updates CHANGELOG.md with proper formatting @@ -38,6 +38,7 @@ class ChangelogGenerator COMMIT_CATEGORIES = { 'feat' => { category: 'Added', breaking: false }, 'fix' => { category: 'Fixed', breaking: false }, + 'hotfix' => { category: 'Hotfixes', breaking: false }, 'docs' => { category: 'Documentation', breaking: false }, 'style' => { category: 'Style', breaking: false }, 'refactor' => { category: 'Changed', breaking: false }, @@ -49,8 +50,9 @@ class ChangelogGenerator 'revert' => { category: 'Reverted', breaking: false } }.freeze - # Release branch pattern + # Release and hotfix branch patterns RELEASE_BRANCH_PATTERN = /^release\/v(\d+)\.(\d+)\.(\d+)$/ + HOTFIX_BRANCH_PATTERN = /^hotfix\/v(\d+)\.(\d+)\.(\d+)$/ # Changelog file path CHANGELOG_PATH = 'CHANGELOG.md' @@ -128,10 +130,13 @@ def get_current_branch # Parse version information from the current branch name # # @return [Hash] Version components (major, minor, patch) - # @raise [RuntimeError] if not on a valid release branch + # @raise [RuntimeError] if not on a valid release or hotfix branch def parse_version_from_branch - match = current_branch.match(RELEASE_BRANCH_PATTERN) - raise "Not on a release branch. Expected format: release/vX.Y.Z" unless match + release_match = current_branch.match(RELEASE_BRANCH_PATTERN) + hotfix_match = current_branch.match(HOTFIX_BRANCH_PATTERN) + match = release_match || hotfix_match + + raise "Not on a release or hotfix branch. Expected format: release/vX.Y.Z or hotfix/vX.Y.Z" unless match { major: match[1].to_i, @@ -148,6 +153,14 @@ def version_string "v#{version_info[:major]}.#{version_info[:minor]}.#{version_info[:patch]}" end + ## + # Check if we're currently on a hotfix branch + # + # @return [Boolean] true if on hotfix branch, false if on release branch + def hotfix_branch? + current_branch.match?(HOTFIX_BRANCH_PATTERN) + end + ## # Validate the environment before proceeding # @@ -296,19 +309,20 @@ def should_skip_commit?(type, commit) def category_priority(category) priorities = { 'Breaking Changes' => 1, - 'Added' => 2, - 'Changed' => 3, - 'Fixed' => 4, - 'Deprecated' => 5, - 'Removed' => 6, - 'Security' => 7, - 'Performance' => 8, - 'Documentation' => 9, - 'Tests' => 10, - 'CI/CD' => 11, - 'Build' => 12, - 'Maintenance' => 13, - 'Other' => 14 + 'Hotfixes' => 2, + 'Added' => 3, + 'Changed' => 4, + 'Fixed' => 5, + 'Deprecated' => 6, + 'Removed' => 7, + 'Security' => 8, + 'Performance' => 9, + 'Documentation' => 10, + 'Tests' => 11, + 'CI/CD' => 12, + 'Build' => 13, + 'Maintenance' => 14, + 'Other' => 15 } priorities[category] || 99 end @@ -452,7 +466,7 @@ def self.run(args = ARGV) opts.separator "following conventional commit format and Keep a Changelog style." opts.separator "" opts.separator "Requirements:" - opts.separator "- Must be run from a release branch (release/vX.Y.Z)" + opts.separator "- Must be run from a release or hotfix branch (release/vX.Y.Z or hotfix/vX.Y.Z)" opts.separator "- Git repository with existing tags" opts.separator "- CHANGELOG.md file with [Unreleased] section" opts.separator "" From 175a67e4cd1ab73882ddf25c40a896bf9d459648 Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Sun, 7 Sep 2025 17:29:24 +0700 Subject: [PATCH 5/9] docs(CHANGELOG): update changelog --- CHANGELOG.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cc4ffd..3b770a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,3 @@ - # Changelog All notable changes to this project will be documented in this file. @@ -6,6 +5,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +[Unreleased] + +## [v2.0.1] - 2025-09-07 + +### Hotfixes + +- Ensure reproduction rate always appears in json response even when null - fix rt values to always be included in api responses for consistency - update reproductionrate struct to use pointer types for proper null handling - modify transformation logic to always include rt structure - update tests to handle new pointer-based rt values - bump version to 2.0.1 for hotfix release this ensures api consumers always receive the reproduction_rate object structure, with null values when data is not available. + +### Fixed + +- Correct database column typo and ensure rt fields always present in json response - fix database column name typo from 'cumulative_finished_persoon_under_observation' to 'cumulative_finished_person_under_observation' - remove 'omitempty' from rt fields (rt, rt_upper, rt_lower) to ensure they always appear in json responses even when null - update all sql queries and tests to use correct column name - critical production hotfix for database errors and missing rt data + +### Maintenance + +- Bump version to 2.0.1 for hotfix release (version) ## [v2.0.0] - 2025-09-07 From 81e09a216968c64070962bf7c8bb0b4db6c26d2a Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Sun, 7 Sep 2025 17:30:20 +0700 Subject: [PATCH 6/9] fix: correct [Unreleased] section format in CHANGELOG.md - Change from plain [Unreleased] to ## [Unreleased] header format - Ensures changelog generator can properly find and update the unreleased section --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b770a5..44e1054 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -[Unreleased] +## [Unreleased] ## [v2.0.1] - 2025-09-07 From c245c29fe1a67b2310e2cd71cf69491525966fb3 Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Sun, 7 Sep 2025 17:30:30 +0700 Subject: [PATCH 7/9] chore: update changelog for v2.0.1 hotfix release - Add hotfix entries for reproduction rate fixes and database column typo corrections - Generated using updated changelog generator with hotfix branch support --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44e1054..917e9a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] + +## [v2.0.1] - 2025-09-07 + +### Hotfixes + +- Ensure reproduction rate always appears in json response even when null - fix rt values to always be included in api responses for consistency - update reproductionrate struct to use pointer types for proper null handling - modify transformation logic to always include rt structure - update tests to handle new pointer-based rt values - bump version to 2.0.1 for hotfix release this ensures api consumers always receive the reproduction_rate object structure, with null values when data is not available. + +### Fixed + +- Correct database column typo and ensure rt fields always present in json response - fix database column name typo from 'cumulative_finished_persoon_under_observation' to 'cumulative_finished_person_under_observation' - remove 'omitempty' from rt fields (rt, rt_upper, rt_lower) to ensure they always appear in json responses even when null - update all sql queries and tests to use correct column name - critical production hotfix for database errors and missing rt data + +### Documentation + +- Update changelog (CHANGELOG) + +### Maintenance + +- Bump version to 2.0.1 for hotfix release (version) + ## [v2.0.1] - 2025-09-07 ### Hotfixes From d55675890d49d65f456b14256694aaa1e9fddee9 Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Sun, 7 Sep 2025 17:31:01 +0700 Subject: [PATCH 8/9] fix: clean up duplicate changelog entries - Remove duplicate v2.0.1 section that was generated twice - Properly organize all commits into appropriate categories - Fix markdown formatting issues --- CHANGELOG.md | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 917e9a7..c7b0094 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - ## [v2.0.1] - 2025-09-07 ### Hotfixes @@ -17,24 +16,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Correct database column typo and ensure rt fields always present in json response - fix database column name typo from 'cumulative_finished_persoon_under_observation' to 'cumulative_finished_person_under_observation' - remove 'omitempty' from rt fields (rt, rt_upper, rt_lower) to ensure they always appear in json responses even when null - update all sql queries and tests to use correct column name - critical production hotfix for database errors and missing rt data +- Correct [Unreleased] section format in CHANGELOG.md -### Documentation - -- Update changelog (CHANGELOG) - -### Maintenance - -- Bump version to 2.0.1 for hotfix release (version) - -## [v2.0.1] - 2025-09-07 - -### Hotfixes - -- Ensure reproduction rate always appears in json response even when null - fix rt values to always be included in api responses for consistency - update reproductionrate struct to use pointer types for proper null handling - modify transformation logic to always include rt structure - update tests to handle new pointer-based rt values - bump version to 2.0.1 for hotfix release this ensures api consumers always receive the reproduction_rate object structure, with null values when data is not available. - -### Fixed +### Added -- Correct database column typo and ensure rt fields always present in json response - fix database column name typo from 'cumulative_finished_persoon_under_observation' to 'cumulative_finished_person_under_observation' - remove 'omitempty' from rt fields (rt, rt_upper, rt_lower) to ensure they always appear in json responses even when null - update all sql queries and tests to use correct column name - critical production hotfix for database errors and missing rt data +- Add hotfix branch support to changelog generator ### Maintenance From 1254d74ef520b62c1867485e3a551de3b5a34298 Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Sun, 7 Sep 2025 17:36:47 +0700 Subject: [PATCH 9/9] test: update province case response tests for always-present reproduction rate - Update test expectations to match new behavior where ReproductionRate struct is always returned - Tests now expect ReproductionRate with nil values instead of nil pointer - Aligns with hotfix behavior ensuring RT fields always appear in JSON responses --- internal/models/province_case_response_test.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/internal/models/province_case_response_test.go b/internal/models/province_case_response_test.go index ceb7558..3f362b1 100644 --- a/internal/models/province_case_response_test.go +++ b/internal/models/province_case_response_test.go @@ -151,7 +151,11 @@ func TestProvinceCase_TransformToResponse(t *testing.T) { Recovered: 90.0, // (1800 / 2000) * 100 Deceased: 5.0, // (100 / 2000) * 100 }, - ReproductionRate: nil, + ReproductionRate: &ReproductionRate{ + Value: nil, + UpperBound: nil, + LowerBound: nil, + }, }, Province: &Province{ ID: "ID-JB", @@ -219,7 +223,11 @@ func TestProvinceCase_TransformToResponse(t *testing.T) { Recovered: 0.0, Deceased: 0.0, }, - ReproductionRate: nil, + ReproductionRate: &ReproductionRate{ + Value: nil, + UpperBound: nil, + LowerBound: nil, + }, }, Province: &Province{ ID: "ID-AC",