Skip to content

Commit a6c55aa

Browse files
authored
add tdengine support (#25)
* add tdengine support --------- Co-authored-by: Rick <[email protected]>
1 parent b20fd6d commit a6c55aa

File tree

8 files changed

+205
-82
lines changed

8 files changed

+205
-82
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
# atest-ext-store-orm
44
ORM database Store Extension for API Testing
55

6-
This project provides an ORM-based database store extension for API testing, simplifying data storage and retrieval operations. It supports various databases including SQLite, MySQL, and others, making it versatile for different testing environments.
6+
This project provides an ORM-based database store extension for API testing, simplifying data storage and retrieval operations. It supports various databases including SQLite, MySQL, PostgreSQL, TDengine, and others, making it versatile for different testing environments.
77

88
## Features
99
- Simplified database operations using ORM.
1010
- Integration with API testing frameworks.
11-
- Support for multiple databases (SQLite, MySQL, etc.).
11+
- Support for multiple databases (SQLite, MySQL, PostgreSQL, TDengine, etc.).
1212

1313
## Usage
1414
To use this extension in your API testing project, follow these steps:

cmd/root.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ func NewRootCommand() (c *cobra.Command) {
3838
}
3939

4040
func (o *option) runE(c *cobra.Command, args []string) (err error) {
41+
defer func() {
42+
if r := recover(); r != nil {
43+
c.Println(r)
44+
}
45+
}()
46+
4147
if o.version {
4248
c.Println(version.GetVersion())
4349
c.Println(version.GetDate())

go.mod

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/linuxsuren/api-testing v0.0.19-0.20250220092001-ed58adc30f20
99
github.com/spf13/cobra v1.8.1
1010
github.com/stretchr/testify v1.9.0
11+
github.com/taosdata/driver-go/v3 v3.6.0
1112
google.golang.org/protobuf v1.33.0
1213
gorm.io/driver/mysql v1.5.2
1314
gorm.io/driver/postgres v1.5.4
@@ -29,9 +30,6 @@ require (
2930
github.com/blang/semver/v4 v4.0.0 // indirect
3031
github.com/bufbuild/protocompile v0.6.0 // indirect
3132
github.com/cespare/xxhash/v2 v2.2.0 // indirect
32-
github.com/cucumber/gherkin-go/v19 v19.0.3 // indirect
33-
github.com/cucumber/godog v0.12.6 // indirect
34-
github.com/cucumber/messages-go/v16 v16.0.1 // indirect
3533
github.com/davecgh/go-spew v1.1.1 // indirect
3634
github.com/expr-lang/expr v1.15.6 // indirect
3735
github.com/flopp/go-findfont v0.1.0 // indirect
@@ -43,33 +41,30 @@ require (
4341
github.com/go-openapi/spec v0.21.0 // indirect
4442
github.com/go-openapi/swag v0.23.0 // indirect
4543
github.com/go-sql-driver/mysql v1.7.0 // indirect
46-
github.com/gofrs/uuid v4.2.0+incompatible // indirect
4744
github.com/golang/protobuf v1.5.4 // indirect
4845
github.com/google/uuid v1.6.0 // indirect
4946
github.com/gorilla/mux v1.8.1 // indirect
47+
github.com/gorilla/websocket v1.5.0 // indirect
5048
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
51-
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
52-
github.com/hashicorp/go-memdb v1.3.2 // indirect
53-
github.com/hashicorp/golang-lru v0.5.4 // indirect
5449
github.com/huandu/xstrings v1.4.0 // indirect
55-
github.com/iancoleman/orderedmap v0.3.0 // indirect
5650
github.com/imdario/mergo v0.3.16 // indirect
5751
github.com/inconshreveable/mousetrap v1.1.0 // indirect
58-
github.com/invopop/jsonschema v0.7.0 // indirect
5952
github.com/jackc/pgpassfile v1.0.0 // indirect
6053
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
6154
github.com/jackc/pgx/v5 v5.4.3 // indirect
6255
github.com/jinzhu/inflection v1.0.0 // indirect
6356
github.com/jinzhu/now v1.1.5 // indirect
6457
github.com/josharian/intern v1.0.0 // indirect
58+
github.com/json-iterator/go v1.1.12 // indirect
6559
github.com/linuxsuren/go-fake-runtime v0.0.4 // indirect
66-
github.com/linuxsuren/go-service v0.0.0-20231225060426-efabcd3a5161 // indirect
6760
github.com/linuxsuren/oauth-hub v0.0.0-20240809060240-e78c21b5d8d4 // indirect
6861
github.com/linuxsuren/unstructured v0.0.1 // indirect
6962
github.com/mailru/easyjson v0.7.7 // indirect
7063
github.com/mattn/go-sqlite3 v1.14.22 // indirect
7164
github.com/mitchellh/copystructure v1.2.0 // indirect
7265
github.com/mitchellh/reflectwalk v1.0.2 // indirect
66+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
67+
github.com/modern-go/reflect2 v1.0.2 // indirect
7368
github.com/phpdave11/gofpdi v1.0.14-0.20211212211723-1f10f9844311 // indirect
7469
github.com/pkg/errors v0.9.1 // indirect
7570
github.com/pmezard/go-difflib v1.0.0 // indirect

go.sum

Lines changed: 13 additions & 56 deletions
Large diffs are not rendered by default.

pkg/data_query.go

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ package pkg
1717

1818
import (
1919
"context"
20-
20+
"fmt"
2121
"github.com/linuxsuren/api-testing/pkg/server"
2222
"gorm.io/gorm"
23+
"reflect"
24+
"time"
2325
)
2426

2527
func (s *dbserver) Query(ctx context.Context, query *server.DataQuery) (result *server.DataQueryResult, err error) {
@@ -32,18 +34,31 @@ func (s *dbserver) Query(ctx context.Context, query *server.DataQuery) (result *
3234
if err != nil {
3335
return
3436
}
35-
defer rows.Close()
36-
37-
columns, err := rows.Columns()
38-
if err != nil {
39-
return
40-
}
37+
defer func() {
38+
if rows != nil {
39+
rows.Close()
40+
}
41+
}()
4142

4243
result = &server.DataQueryResult{
4344
Data: []*server.Pair{},
4445
Items: make([]*server.Pairs, 0),
4546
}
4647

48+
if rows == nil {
49+
if rows, err = db.ConnPool.QueryContext(ctx, query.Sql); err != nil {
50+
return
51+
} else if rows == nil {
52+
fmt.Println("no rows found")
53+
return
54+
}
55+
}
56+
57+
columns, err := rows.Columns()
58+
if err != nil {
59+
return
60+
}
61+
4762
for rows.Next() {
4863
// Create a slice of interface{}'s to represent each column,
4964
// and a second slice to contain pointers to each item in the columns slice.
@@ -63,10 +78,21 @@ func (s *dbserver) Query(ctx context.Context, query *server.DataQuery) (result *
6378
for i, colName := range columns {
6479
rowData := &server.Pair{}
6580
val := columnsData[i]
66-
b, ok := val.([]byte)
67-
if ok {
68-
rowData.Key = colName
69-
rowData.Value = string(b)
81+
82+
rowData.Key = colName
83+
switch v := val.(type) {
84+
case []byte:
85+
rowData.Value = string(v)
86+
case string:
87+
rowData.Value = v
88+
case int, uint64, uint32, int32, int64:
89+
rowData.Value = fmt.Sprintf("%d", v)
90+
case float32, float64:
91+
rowData.Value = fmt.Sprintf("%f", v)
92+
case time.Time:
93+
rowData.Value = v.String()
94+
default:
95+
fmt.Println("column", colName, "type", reflect.TypeOf(v))
7096
}
7197

7298
// Append the map to our slice of maps.

pkg/server.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ func createDB(user, password, address, database, driver string) (db *gorm.DB, er
6868
}
6969
dsn = fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Shanghai", host, user, password, database, port)
7070
dialector = postgres.Open(dsn)
71+
case "tdengine":
72+
dsn = fmt.Sprintf("%s:%s@ws(%s)/", user, password, address)
73+
dialector = NewTDengineDialector(dsn)
7174
default:
7275
err = fmt.Errorf("invalid database driver %q", driver)
7376
return
@@ -82,9 +85,11 @@ func createDB(user, password, address, database, driver string) (db *gorm.DB, er
8285
return
8386
}
8487

85-
err = errors.Join(err, db.AutoMigrate(&TestCase{}))
86-
err = errors.Join(err, db.AutoMigrate(&TestSuite{}))
87-
err = errors.Join(err, db.AutoMigrate(&HistoryTestResult{}))
88+
if driver != "tdengine" {
89+
err = errors.Join(err, db.AutoMigrate(&TestCase{}))
90+
err = errors.Join(err, db.AutoMigrate(&TestSuite{}))
91+
err = errors.Join(err, db.AutoMigrate(&HistoryTestResult{}))
92+
}
8893
return
8994
}
9095

pkg/tdengine_dialector.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
Copyright 2025 API Testing Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package pkg
17+
18+
import (
19+
"database/sql"
20+
"fmt"
21+
22+
_ "github.com/taosdata/driver-go/v3/taosWS"
23+
"gorm.io/driver/mysql"
24+
"gorm.io/gorm"
25+
"gorm.io/gorm/clause"
26+
"gorm.io/gorm/logger"
27+
"gorm.io/gorm/schema"
28+
)
29+
30+
var _ gorm.Dialector = &tdengineDialector{}
31+
32+
type tdengineDialector struct {
33+
DSN string
34+
}
35+
36+
func (d tdengineDialector) Name() string {
37+
return "tdengine"
38+
}
39+
40+
func (d tdengineDialector) Initialize(db *gorm.DB) (err error) {
41+
// Initialize the TDengine connection here
42+
if db.ConnPool == nil {
43+
db.ConnPool, err = sql.Open("taosWS", d.DSN)
44+
}
45+
return
46+
}
47+
48+
func (d tdengineDialector) Migrator(db *gorm.DB) gorm.Migrator {
49+
// Return the TDengine migrator here
50+
return &mysql.Migrator{}
51+
}
52+
53+
func (d tdengineDialector) DataTypeOf(field *schema.Field) string {
54+
// Return the TDengine data type for the given field
55+
switch field.DataType {
56+
case schema.Bool:
57+
return "bool"
58+
case schema.Int, schema.Uint:
59+
sqlType := "bigint"
60+
switch {
61+
case field.Size <= 8:
62+
sqlType = "tinyint"
63+
case field.Size <= 16:
64+
sqlType = "smallint"
65+
case field.Size <= 32:
66+
sqlType = "int"
67+
}
68+
return sqlType
69+
case schema.Float:
70+
if field.Size <= 32 {
71+
return "float"
72+
}
73+
return "double"
74+
case schema.String:
75+
size := field.Size
76+
if size == 0 {
77+
size = 64
78+
}
79+
return fmt.Sprintf("NCHAR(%d)", size)
80+
case schema.Time:
81+
return "TIMESTAMP"
82+
case schema.Bytes:
83+
size := field.Size
84+
if size == 0 {
85+
size = 64
86+
}
87+
return fmt.Sprintf("BINARY(%d)", size)
88+
}
89+
90+
return string(field.DataType)
91+
}
92+
93+
func (d tdengineDialector) DefaultValueOf(field *schema.Field) clause.Expression {
94+
return clause.Expr{SQL: "NULL"}
95+
}
96+
97+
func (d tdengineDialector) BindVarTo(writer clause.Writer, stmt *gorm.Statement, v interface{}) {
98+
// Bind variables for TDengine
99+
switch v.(type) {
100+
case string:
101+
writer.WriteString("'?'")
102+
default:
103+
writer.WriteByte('?')
104+
}
105+
}
106+
107+
func (d tdengineDialector) QuoteTo(writer clause.Writer, str string) {
108+
// Quote identifiers for TDengine
109+
writer.WriteString(str)
110+
}
111+
112+
func (d tdengineDialector) Explain(sql string, vars ...interface{}) string {
113+
// Explain the SQL query for TDengine
114+
return logger.ExplainSQL(sql, nil, "'", vars...)
115+
}
116+
117+
func NewTDengineDialector(dsn string) gorm.Dialector {
118+
return tdengineDialector{DSN: dsn}
119+
}

pkg/types.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
/*
2+
Copyright 2025 API Testing Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
116
package pkg
217

318
type TestCase struct {

0 commit comments

Comments
 (0)