Skip to content

Commit d2491ee

Browse files
committed
fix(jira): add missing _raw_data_* columns to _tool_jira_sprint_reports
The Sprint Report migration 20260722_add_sprint_report_table.go creates _tool_jira_sprint_reports from a struct that does not embed common.NoPKModel, while the runtime model models.JiraSprintReport does. The columns _raw_data_params / _raw_data_table / _raw_data_id / _raw_data_remark (plus created_at, updated_at) were therefore never created, so the ApiExtractor cleanup query WHERE _raw_data_table = ? AND _raw_data_params = ? made the extractSprintReport subtask fail with "Error 1054 (42S22): Unknown column '_raw_data_table' in 'where clause'". Add a new, additive migration that re-runs AutoMigrateTables on a struct embedding archived.NoPKModel. The original migration is left untouched: migration scripts are append-only, and editing it would not repair databases that already recorded its version. Add two schema-drift regression guards that run the REAL migration scripts instead of AutoMigrate-ing the runtime model, which would hide this class of drift: * plugins/jira/e2e/migration_schema_test.go - Jira-specific guard. * plugins/schema_e2e/migration_schema_test.go - cross-plugin guard for every built-in Go plugin, including TestAllGoPluginsListed so the guard stays complete when a new plugin is added. The cross-plugin guard immediately uncovered three pre-existing drifts of the same class, each fixed with its own additive migration: * _tool_taiga_scope_configs - missing type_mappings * _tool_teambition_scope_configs - missing id, created_at, updated_at * _tool_testmo_scope_configs - missing connection_id, name Finally, exclude plugins/schema_e2e from scripts/build-plugins.sh: it is not a plugin and has no main package, which broke `make build-plugin` with "-buildmode=plugin requires exactly one main package". Signed-off-by: DoDiODev <DoDiDev@proton.me>
1 parent 7f7cf09 commit d2491ee

11 files changed

Lines changed: 621 additions & 1 deletion

File tree

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package e2e
19+
20+
import (
21+
"sync"
22+
"testing"
23+
24+
"github.com/apache/incubator-devlake/core/dal"
25+
"github.com/apache/incubator-devlake/core/migration"
26+
coreMigration "github.com/apache/incubator-devlake/core/models/migrationscripts"
27+
"github.com/apache/incubator-devlake/core/plugin"
28+
"github.com/apache/incubator-devlake/core/runner"
29+
"github.com/apache/incubator-devlake/helpers/e2ehelper"
30+
"github.com/apache/incubator-devlake/impls/dalgorm"
31+
"github.com/apache/incubator-devlake/plugins/jira/impl"
32+
"github.com/stretchr/testify/assert"
33+
"github.com/stretchr/testify/require"
34+
"gorm.io/gorm/schema"
35+
)
36+
37+
// TestMigrationSchema guards against schema drift between Jira's migration
38+
// scripts and its runtime GORM models.
39+
//
40+
// Regression test for the Sprint Report bug (introduced by PR #8967/#9010):
41+
// the migration that created `_tool_jira_sprint_reports` used a struct that did
42+
// NOT embed common.NoPKModel, so the `_raw_data_table` / `_raw_data_params` /
43+
// `_raw_data_id` / `_raw_data_remark` columns were missing. The runtime model
44+
// DID embed common.NoPKModel, so the ApiExtractor's cleanup query
45+
// (`WHERE _raw_data_table = ? AND _raw_data_params = ?`) failed at runtime with
46+
// "Error 1054: Unknown column '_raw_data_table' in 'where clause'".
47+
//
48+
// The test runs the REAL migration scripts (framework + jira) to build the
49+
// schema exactly the way a production install would — deliberately NOT via
50+
// AutoMigrate on the runtime model, which would silently hide such drift — and
51+
// then asserts that every column each runtime model expects actually exists in
52+
// the migrated table. Any future migration that forgets to embed
53+
// common.NoPKModel (or otherwise omits a column) will fail this test.
54+
//
55+
// Requires E2E_DB_URL (runs under `make e2e-test` / `make e2e-test-go-plugins`).
56+
func TestMigrationSchema(t *testing.T) {
57+
var pluginInstance impl.Jira
58+
dataflowTester := e2ehelper.NewDataFlowTester(t, "jira", pluginInstance)
59+
60+
// Some jira migration scripts refuse to run without an encryption secret
61+
// (20220716: "jira v0.11 invalid encKey"); CI does not necessarily provide
62+
// one, so fall back to a deterministic test value. dalgorm.Init registers
63+
// the `encdec` GORM serializer used by connection models — without it the
64+
// migrations fail with "invalid serializer type encdec".
65+
if dataflowTester.Cfg.GetString(plugin.EncodeKeyEnvStr) == "" {
66+
dataflowTester.Cfg.Set(plugin.EncodeKeyEnvStr, "jira-e2e-test-encryption-secret")
67+
}
68+
dalgorm.Init(dataflowTester.Cfg.GetString(plugin.EncodeKeyEnvStr))
69+
70+
// Apply the real migration scripts so the schema matches a production install.
71+
basicRes := runner.CreateBasicRes(dataflowTester.Cfg, dataflowTester.Log, dataflowTester.Db)
72+
migrator, err := migration.NewMigrator(basicRes)
73+
require.NoError(t, err)
74+
migrator.Register(coreMigration.All(), "Framework")
75+
migrator.Register(pluginInstance.MigrationScripts(), "jira")
76+
require.NoError(t, migrator.Execute())
77+
78+
keepAll := func(dal.ColumnMeta) bool { return true }
79+
80+
for _, table := range pluginInstance.GetTablesInfo() {
81+
table := table
82+
t.Run(table.TableName(), func(t *testing.T) {
83+
// Columns the runtime GORM model expects.
84+
sch, err := schema.Parse(table, &sync.Map{}, schema.NamingStrategy{})
85+
require.NoErrorf(t, err, "unable to parse schema for %T", table)
86+
87+
// Columns that actually exist in the migrated table.
88+
actualColumns, err := dal.GetColumnNames(dataflowTester.Dal, table, keepAll)
89+
if err != nil || len(actualColumns) == 0 {
90+
// No migration creates this table (e.g. API response models
91+
// that are listed in GetTablesInfo but never persisted) —
92+
// there is no schema to drift from.
93+
t.Skipf("table %q not present after migrations", table.TableName())
94+
}
95+
existing := make(map[string]struct{}, len(actualColumns))
96+
for _, c := range actualColumns {
97+
existing[c] = struct{}{}
98+
}
99+
100+
for _, field := range sch.Fields {
101+
if field.DBName == "" || field.IgnoreMigration {
102+
continue
103+
}
104+
_, ok := existing[field.DBName]
105+
assert.Truef(t, ok,
106+
"table %q is missing column %q expected by model %T — "+
107+
"did a migration script forget to embed common.NoPKModel (raw-data columns) or add the field?",
108+
table.TableName(), field.DBName, table)
109+
}
110+
})
111+
}
112+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package migrationscripts
19+
20+
import (
21+
"github.com/apache/incubator-devlake/core/context"
22+
"github.com/apache/incubator-devlake/core/errors"
23+
"github.com/apache/incubator-devlake/core/models/migrationscripts/archived"
24+
"github.com/apache/incubator-devlake/helpers/migrationhelper"
25+
)
26+
27+
// jiraSprintReport20260727 mirrors the JiraSprintReport model. The original
28+
// migration (20260722) created _tool_jira_sprint_reports without embedding
29+
// common.NoPKModel, so the _raw_data_table / _raw_data_params / _raw_data_id /
30+
// _raw_data_remark columns (and created_at / updated_at) were missing. The
31+
// runtime model expects them, which made the ApiExtractor's cleanup query
32+
// (WHERE _raw_data_table = ? AND _raw_data_params = ?) fail with
33+
// "Unknown column '_raw_data_table' in 'where clause'". Re-running
34+
// AutoMigrateTables adds the missing columns without dropping existing data.
35+
type jiraSprintReport20260727 struct {
36+
archived.NoPKModel
37+
ConnectionId uint64 `gorm:"primaryKey"`
38+
BoardId uint64 `gorm:"primaryKey"`
39+
SprintId uint64 `gorm:"primaryKey"`
40+
IssueId uint64 `gorm:"primaryKey"`
41+
42+
IssueKey string `gorm:"type:varchar(255)"`
43+
Bucket string `gorm:"type:varchar(32);index"`
44+
Done bool
45+
StoryPointsAtSprintStart *float64
46+
StoryPointsAtSprintEnd *float64
47+
}
48+
49+
func (jiraSprintReport20260727) TableName() string {
50+
return "_tool_jira_sprint_reports"
51+
}
52+
53+
type addRawDataColumnsToSprintReport struct{}
54+
55+
func (script *addRawDataColumnsToSprintReport) Up(basicRes context.BasicRes) errors.Error {
56+
return migrationhelper.AutoMigrateTables(basicRes, &jiraSprintReport20260727{})
57+
}
58+
59+
func (*addRawDataColumnsToSprintReport) Version() uint64 {
60+
return 20260727000000
61+
}
62+
63+
func (*addRawDataColumnsToSprintReport) Name() string {
64+
return "add missing _raw_data_* columns to _tool_jira_sprint_reports"
65+
}

backend/plugins/jira/models/migrationscripts/register.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,6 @@ func All() []plugin.MigrationScript {
5959
new(changeFixVersionsToText20260707),
6060
new(addExtraJQLToScopeConfig),
6161
new(addSprintReportTable),
62+
new(addRawDataColumnsToSprintReport),
6263
}
6364
}

0 commit comments

Comments
 (0)