Compare commits

...

66 Commits

Author SHA1 Message Date
peze 470c77cf37 fix the sse 2025-08-14 19:47:42 +08:00
peze 0393e5468f add context 2025-07-21 15:29:15 +08:00
peze f93724f865 fix the stringify 2025-04-21 18:18:26 +08:00
peze fdbb29ef15 fix the toMap when input is basic map 2025-04-07 17:32:23 +08:00
peze a4d8c8f7f5 fix default condition 2025-04-07 17:32:23 +08:00
peze 4820a881e8 add retry options config 2025-04-07 17:32:23 +08:00
peze 6d4a89b84e add httpclient config in dara 2025-03-27 14:47:24 +08:00
nanhe a17e6d71ba feat: support custom httpClient 2025-03-26 19:26:04 +08:00
peze 5379d4dfbb fix the desc type 2025-03-25 21:07:01 +08:00
peze 027c7539de fix resp error 2025-03-25 20:44:49 +08:00
peze 6192dca562 fix arr append 2025-03-14 16:27:38 +08:00
peze c032b7c3e5 compatible with old err 2025-03-12 15:51:16 +08:00
peze 171239d819 compatible with old err 2025-03-11 21:08:20 +08:00
peze 4f3624270b add to writer 2025-03-11 21:08:20 +08:00
peze 8d3b5385f0 fix the readSSE 2025-02-25 11:37:57 +08:00
peze cf679a8bdd compatible with old sdk error 2025-02-17 16:44:27 +08:00
peze 6af7becd53 fix to reader 2025-02-12 20:43:19 +08:00
yndu13 eb3f1e523f Merge pull request #123 from alibabacloud-go/v2
V2
2025-02-12 16:19:18 +08:00
peze b59ecf51be add tea build 2025-02-12 16:14:35 +08:00
page 39fa3715e4 Merge branch 'master' into v2 2025-02-12 16:10:47 +08:00
peze 1a7a3b600b fix by compatible bug 2025-02-12 14:27:23 +08:00
peze 89d6a19474 upgrade by darabonba v2 2024-11-14 22:05:43 +08:00
dependabot[bot] b8dbae11d9 Bump golang.org/x/net from 0.22.0 to 0.26.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.22.0 to 0.26.0.
- [Commits](https://github.com/golang/net/compare/v0.22.0...v0.26.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-21 20:42:19 +08:00
dependabot[bot] 8e3b4252f0 Bump golang.org/x/net from 0.20.0 to 0.22.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.20.0 to 0.22.0.
- [Commits](https://github.com/golang/net/compare/v0.20.0...v0.22.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-22 13:53:16 +08:00
dependabot[bot] bb7ae5fd3e disable fail-fast 2024-01-22 17:30:17 +08:00
dependabot[bot] c1f8e32399 Bump github.com/alibabacloud-go/debug
Bumps [github.com/alibabacloud-go/debug](https://github.com/alibabacloud-go/debug) from 0.0.0-20190504072949-9472017b5c68 to 1.0.0.
- [Release notes](https://github.com/alibabacloud-go/debug/releases)
- [Commits](https://github.com/alibabacloud-go/debug/commits/v1.0.0)

---
updated-dependencies:
- dependency-name: github.com/alibabacloud-go/debug
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 17:17:14 +08:00
dependabot[bot] d755295be2 Bump golang.org/x/net from 0.11.0 to 0.20.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.11.0 to 0.20.0.
- [Commits](https://github.com/golang/net/compare/v0.11.0...v0.20.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 17:14:25 +08:00
yndu13 b81493c626 fix: unescape unicode in sdk error 2024-01-22 17:12:09 +08:00
nanhe 2b0e131d00 docs: update comments 2023-06-28 17:46:15 +08:00
nanhe e3922d2afb improve tls client config 2023-06-28 17:31:08 +08:00
dependabot[bot] 5ef2fcc54f Bump golang.org/x/net from 0.10.0 to 0.11.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.10.0 to 0.11.0.
- [Commits](https://github.com/golang/net/compare/v0.10.0...v0.11.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-27 23:13:13 +08:00
Jackson Tian 121efa4413 update actions setup-go to v4 2023-05-15 15:40:51 +08:00
dependabot[bot] aa99f50b3f Bump golang.org/x/net from 0.9.0 to 0.10.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.9.0 to 0.10.0.
- [Commits](https://github.com/golang/net/compare/v0.9.0...v0.10.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-15 15:36:25 +08:00
王理想 cb3a42bc23 judge maxidleconns int(0) pointer 2023-05-08 16:52:58 +08:00
王理想 712238a5c4 remove keep alive default 2023-05-08 16:52:58 +08:00
王理想 07ca3b69ad deal with MaxIdleConns, MaxIdleConnsPerHost, TLSHandshakeTimeout, and KeepAlive 2023-05-08 16:52:58 +08:00
dependabot[bot] e158cc5dc5 Bump golang.org/x/net from 0.0.0-20200226121028-0de0cce0169b to 0.9.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.0.0-20200226121028-0de0cce0169b to 0.9.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/commits/v0.9.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-26 11:32:33 +08:00
dependabot[bot] b46b464236 Bump github.com/json-iterator/go from 1.1.10 to 1.1.12
Bumps [github.com/json-iterator/go](https://github.com/json-iterator/go) from 1.1.10 to 1.1.12.
- [Release notes](https://github.com/json-iterator/go/releases)
- [Commits](https://github.com/json-iterator/go/compare/v1.1.10...v1.1.12)

---
updated-dependencies:
- dependency-name: github.com/json-iterator/go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-26 11:29:32 +08:00
Jackson Tian 636d89faa7 Create dependabot.yml 2023-04-26 11:27:59 +08:00
Jackson Tian c4f78703d7 Update README-CN.md 2023-04-26 11:21:46 +08:00
Jackson Tian 8a7c8512f8 Update README.md 2023-04-26 11:21:33 +08:00
Jackson Tian 02f393be3f Delete .travis.yml 2023-03-23 15:12:14 +08:00
nanhe c87ce79386 support port for url request 2023-03-23 15:01:26 +08:00
nanhe b9ace42a5b feat: return description and accessDeniedDetail in error info 2022-10-20 15:56:55 +08:00
nanhe 3a75a83be1 support when statusCode is *int 2022-06-20 20:19:19 +08:00
nanhe af66437067 support statusCode in data for SDKError 2022-06-14 16:16:13 +08:00
nanhe e0a739a681 add Action 2022-06-14 16:16:13 +08:00
ziggy d9ff2bfbeb add StatusCode for SDK Error
Co-authored-by: zigang.wang <zigang.wang@alibaba-inc.com>
2021-07-23 13:54:08 +08:00
peze fc13b6ebee fix the big number unmarshal error 2021-05-19 09:42:26 +08:00
wb-wzc505509 6355725204 modify Convert 2021-01-26 17:34:04 +08:00
wb-wzc505509 b6aa048c4a add ToInt and ToInt32 2021-01-18 09:45:18 +08:00
wb-wzc505509 5da4c33a57 rm port 2021-01-06 14:42:54 +08:00
wb-wzc505509 2a316b984d support ignore tag 2021-01-05 09:21:42 +08:00
wb-wzc505509 fa780870a7 fix pattern error 2020-11-30 13:12:50 +08:00
wb-wzc505509 9144ca7f27 support ssl 2020-10-19 13:36:46 +08:00
wb-wzc505509 174725cf27 add more basic type 2020-09-08 10:08:47 +08:00
wb-wzc505509 45df60e197 FixUserAgentError 2020-09-07 14:56:23 +08:00
wb-wzc505509 ecd18128a4 add slice validate 2020-08-21 17:24:40 +08:00
wb-wzc505509 f16f5b0116 improve test 2020-07-16 15:41:36 +08:00
wb-wzc505509 8291a17aca validate support null param 2020-07-09 13:52:35 +08:00
wb-wzc505509 7ad16974b6 validate support null param 2020-07-09 13:39:59 +08:00
wb-wzc505509 8918f7f891 fix timeout error 2020-07-08 17:20:59 +08:00
wb-wzc505509 29a23a75ba add recover 2020-07-06 10:25:53 +08:00
wb-wzc505509 d9c5d0857b add SetErrMsg to SDKError 2020-07-03 13:21:52 +08:00
wb-wzc505509 852924e5df fix the error caused by omitempty 2020-06-22 13:03:43 +08:00
wenzuochao 4c13582572 support validate minLength minimum maxmum 2020-05-13 14:31:45 +08:00
47 changed files with 11126 additions and 180 deletions
+11
View File
@@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "gomod" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
+38
View File
@@ -0,0 +1,38 @@
name: Go CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
go: ["1.13", "1.14", "1.15", "1.16", "1.17", "1.18", "1.19", "1.20"]
fail-fast: false
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go }}
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Build Darabonba
run: go build ./dara
- name: Build Tea
run: go build ./tea
- name: Build Util
run: go build ./utils
- name: Test
run: go test -race -coverprofile=coverage.txt -covermode=atomic ./dara/... ./utils/... ./tea/...
- name: CodeCov
run: bash <(curl -s https://codecov.io/bash)
+1 -1
View File
@@ -3,4 +3,4 @@ coverage.txt
coverage.out
.DS_Store
.history/
go.sum
vendor/
-24
View File
@@ -1,24 +0,0 @@
language: go
go:
- 1.12.x
branches: # build only on these branches
only:
- master
install:
- export GO111MODULE=on
notifications:
webhooks: https://oapi.dingtalk.com/robot/send?access_token=096ed387df243a6d60835aadeccc47165f3813bc7cb81cdd0cfeadfd28e3acc1
email: false
on_success: change
on_failure: always
script:
- go mod tidy
- go test -race -coverprofile=coverage.txt -covermode=atomic ./tea/... ./utils/...
after_success:
- bash <(curl -s https://codecov.io/bash)
+2 -2
View File
@@ -1,8 +1,8 @@
all:
fmt:
go fmt ./
go fmt ./tea ./dara ./utils
test:
go test -race -coverprofile=coverage.txt -covermode=atomic ./tea ./utils
go test -race -coverprofile=coverage.txt -covermode=atomic ./tea ./utils ./dara
go tool cover -html=coverage.txt -o coverage.html
+3 -3
View File
@@ -2,11 +2,11 @@
<a href="https://badge.fury.io/gh/alibabacloud-go%2Ftea"><img src="https://badge.fury.io/gh/alibabacloud-go%2Ftea.svg" alt="Latest Stable Version"></a>
<a href="https://codecov.io/gh/alibabacloud-go/tea"><img src="https://codecov.io/gh/alibabacloud-go/tea/branch/master/graph/badge.svg" alt="codecov"></a>
<a href="https://travis-ci.org/alibabacloud-go/tea"><img src="https://travis-ci.org/alibabacloud-go/tea.svg?branch=master" alt="Travis Build Status"></a>
[![Go CI](https://github.com/alibabacloud-go/tea/actions/workflows/go.yml/badge.svg)](https://github.com/alibabacloud-go/tea/actions/workflows/go.yml)
该项目用于支持TEA OpenAPI DSL。它是http请求的底层库.
该项目用于支持 Darabonba OpenAPI DSL。它是http请求的底层库.
## 许可证
[Apache-2.0](/LICENSE)
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
+3 -3
View File
@@ -2,11 +2,11 @@
<a href="https://badge.fury.io/gh/alibabacloud-go%2Ftea"><img src="https://badge.fury.io/gh/alibabacloud-go%2Ftea.svg" alt="Latest Stable Version"></a>
<a href="https://codecov.io/gh/alibabacloud-go/tea"><img src="https://codecov.io/gh/alibabacloud-go/tea/branch/master/graph/badge.svg" alt="codecov"></a>
<a href="https://travis-ci.org/alibabacloud-go/tea"><img src="https://travis-ci.org/alibabacloud-go/tea.svg?branch=master" alt="Travis Build Status"></a>
[![Go CI](https://github.com/alibabacloud-go/tea/actions/workflows/go.yml/badge.svg)](https://github.com/alibabacloud-go/tea/actions/workflows/go.yml)
This project is used for support TEA OpenAPI DSL. It's a low-level library for http request.
This project is used for support Darabonba OpenAPI DSL. It's a low-level library for http request.
## License
[Apache-2.0](/LICENSE)
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
+439
View File
File diff suppressed because it is too large Load Diff
+597
View File
File diff suppressed because it is too large Load Diff
+1135
View File
File diff suppressed because it is too large Load Diff
+1368
View File
File diff suppressed because it is too large Load Diff
+162
View File
@@ -0,0 +1,162 @@
package dara
import (
"fmt"
"strings"
"time"
)
type Date struct {
date time.Time
}
func NewDate(dateInput string) (*Date, error) {
var t time.Time
var err error
// 解析输入时间,如果输入格式不对,返回错误
formats := []string{
"2006-01-02 15:04:05",
"2006-01-02 15:04:05.999999999 -0700 MST",
"2006-01-02T15:04:05-07:00",
"2006-01-02T15:04:05Z",
}
for _, format := range formats {
t, err = time.Parse(format, dateInput)
if err == nil {
return &Date{date: t}, nil
}
}
return nil, fmt.Errorf("unable to parse date: %v", dateInput)
}
func (t *Date) Format(layout string) string {
layout = strings.Replace(layout, "yyyy", "2006", 1)
layout = strings.Replace(layout, "MM", "01", 1)
layout = strings.Replace(layout, "dd", "02", 1)
// layout = strings.Replace(layout, "HH", "15", 1)
layout = strings.Replace(layout, "hh", "15", 1)
layout = strings.Replace(layout, "mm", "04", 1)
layout = strings.Replace(layout, "ss", "05", 1)
layout = strings.Replace(layout, "a", "PM", 1)
layout = strings.Replace(layout, "EEEE", "Monday", 1)
layout = strings.Replace(layout, "E", "Mon", 1)
return t.date.Format(layout)
}
func (t *Date) Unix() int64 {
return t.date.Unix()
}
func (t *Date) UTC() string {
return t.date.UTC().Format("2006-01-02 15:04:05.000000000 -0700 MST")
}
func (t *Date) Sub(amount int, unit string) *Date {
var duration time.Duration
switch unit {
case "second", "seconds":
duration = time.Duration(-amount) * time.Second
case "minute", "minutes":
duration = time.Duration(-amount) * time.Minute
case "hour", "hours":
duration = time.Duration(-amount) * time.Hour
case "day", "days":
duration = time.Duration(-amount) * 24 * time.Hour
case "week", "weeks":
duration = time.Duration(-amount) * 7 * 24 * time.Hour
case "month", "months":
return &Date{date: t.date.AddDate(0, -amount, 0)}
case "year", "years":
return &Date{date: t.date.AddDate(-amount, 0, 0)}
default:
return nil
}
newDate := t.date.Add(duration)
return &Date{date: newDate}
}
func (t *Date) Add(amount int, unit string) *Date {
var duration time.Duration
switch unit {
case "second", "seconds":
duration = time.Duration(amount) * time.Second
case "minute", "minutes":
duration = time.Duration(amount) * time.Minute
case "hour", "hours":
duration = time.Duration(amount) * time.Hour
case "day", "days":
duration = time.Duration(amount) * 24 * time.Hour
case "week", "weeks":
duration = time.Duration(amount) * 7 * 24 * time.Hour
case "month", "months":
return &Date{date: t.date.AddDate(0, amount, 0)}
case "year", "years":
return &Date{date: t.date.AddDate(amount, 0, 0)}
default:
return nil
}
newDate := t.date.Add(duration)
return &Date{date: newDate}
}
func (t *Date) Diff(amount string, diffDate *Date) int64 {
switch amount {
case "second", "seconds":
return int64(t.date.Sub(diffDate.date).Seconds())
case "minute", "minutes":
return int64(t.date.Sub(diffDate.date).Minutes())
case "hour", "hours":
return int64(t.date.Sub(diffDate.date).Hours())
case "day", "days":
return int64(t.date.Sub(diffDate.date).Hours() / 24)
case "week", "weeks":
return int64(t.date.Sub(diffDate.date).Hours() / (24 * 7))
case "month", "months":
return int64(diffDate.date.Year()*12 + int(diffDate.date.Month()) - (t.date.Year()*12 + int(t.date.Month())))
case "year", "years":
return int64(t.date.Year() - diffDate.date.Year())
default:
return 0
}
}
func (t *Date) Hour() int {
return t.date.Hour()
}
func (t *Date) Minute() int {
return t.date.Minute()
}
func (t *Date) Second() int {
return t.date.Second()
}
func (t *Date) Month() int {
return int(t.date.Month())
}
func (t *Date) Year() int {
return t.date.Year()
}
func (t *Date) DayOfMonth() int {
return t.date.Day()
}
func (t *Date) DayOfWeek() int {
weekday := int(t.date.Weekday())
if weekday == 0 {
return 7 // Sunday
}
return weekday
}
func (t *Date) WeekOfYear() int {
_, week := t.date.ISOWeek()
return week
}
+160
View File
@@ -0,0 +1,160 @@
package dara
import (
"testing"
"time"
)
func TestConstructWithNow(t *testing.T) {
date := &Date{date: time.Now()}
currentTime := time.Now()
if currentTime.Format("2006-01-02 15:04:05") != date.Format("yyyy-MM-dd hh:mm:ss") {
t.Errorf("Expected %v, got %v", currentTime.Format("2006-01-02 15:04:05"), date.Format("yyyy-MM-dd hh:mm:ss"))
}
}
func TestConstructWithDateTimeString(t *testing.T) {
datetime := "2023-03-01T12:00:00Z" // Use RFC3339 format
date, err := NewDate(datetime)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if datetime != date.Format("yyyy-MM-ddThh:mm:ssZ") {
t.Errorf("Expected %v, got %v", datetime, date.Format("yyyy-MM-ddThh:mm:ssZ"))
}
}
func TestConstructWithWrongType(t *testing.T) {
_, err := NewDate("20230301 12:00:00 +0000 UTC")
if err == nil {
t.Errorf("Expected error, but got nil")
}
}
func TestConstructWithUTC(t *testing.T) {
datetimeUTC := "2023-03-01T12:00:00Z"
dateWithUTC, err := NewDate(datetimeUTC)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
referenceDateTime, _ := time.Parse(time.RFC3339, datetimeUTC)
if referenceDateTime.Unix() != dateWithUTC.Unix() {
t.Errorf("Expected %v, got %v", referenceDateTime.Unix(), dateWithUTC.Unix())
}
formattedDateTime := dateWithUTC.UTC()
expectedFormattedDateTime := referenceDateTime.UTC().Format("2006-01-02 15:04:05.000000000 -0700 MST")
if formattedDateTime != expectedFormattedDateTime {
t.Errorf("Expected %v, got %v", expectedFormattedDateTime, formattedDateTime)
}
}
func TestFormat(t *testing.T) {
datetime := "2023-03-01T12:00:00Z"
date, _ := NewDate(datetime)
expected := "2023-03-01 12:00 PM"
if result := date.Format("yyyy-MM-dd hh:mm a"); result != expected {
t.Errorf("Expected %v, got %v", expected, result)
}
}
func TestUTC(t *testing.T) {
datetime := "2023-03-01T12:00:00+08:00"
date, _ := NewDate(datetime)
expected := "2023-03-01 04:00:00.000000000 +0000 UTC"
if result := date.UTC(); result != expected {
t.Errorf("Expected %v, got %v", expected, result)
}
}
func TestUnix(t *testing.T) {
datetime := "1970-01-01T00:00:00Z"
date, _ := NewDate(datetime)
if result := date.Unix(); result != 0 {
t.Errorf("Expected 0, got %v", result)
}
datetime = "2023-12-31T08:00:00+08:00"
date, _ = NewDate(datetime)
if result := date.Unix(); result != 1703980800 {
t.Errorf("Expected 1703980800, got %v", result)
}
}
func TestAddSub(t *testing.T) {
datetime := "2023-03-01T12:00:00Z"
date, _ := NewDate(datetime)
date = date.Add(1, "day")
expectedDate := time.Date(2023, 3, 2, 12, 0, 0, 0, time.UTC)
if date.date != expectedDate {
t.Errorf("Expected %v, got %v", expectedDate, date.date)
}
date = date.Sub(1, "day") // Subtract 1 day
expectedDate = time.Date(2023, 3, 1, 12, 0, 0, 0, time.UTC)
if date.date != expectedDate {
t.Errorf("Expected %v, got %v", expectedDate, date.date)
}
}
func TestDiff(t *testing.T) {
datetime1 := "2023-03-01T12:00:00Z"
datetime2 := "2023-04-01T12:00:00Z"
date1, _ := NewDate(datetime1)
date2, _ := NewDate(datetime2)
diffInSeconds := date1.Diff("seconds", date2)
if diffInSeconds != -31*24*60*60 {
t.Errorf("Expected %v, got %v", -31*24*60*60, diffInSeconds)
}
}
func TestHourMinuteSecond(t *testing.T) {
datetime := "2023-03-01T12:34:56Z"
date, _ := NewDate(datetime)
if result := date.Hour(); result != 12 {
t.Errorf("Expected 12, got %d", result)
}
if result := date.Minute(); result != 34 {
t.Errorf("Expected 34, got %d", result)
}
if result := date.Second(); result != 56 {
t.Errorf("Expected 56, got %d", result)
}
}
func TestMonthYearDay(t *testing.T) {
datetime := "2023-03-01T12:00:00Z"
date, _ := NewDate(datetime)
if result := date.Month(); result != 3 {
t.Errorf("Expected 3, got %d", result)
}
if result := date.Year(); result != 2023 {
t.Errorf("Expected 2023, got %d", result)
}
if result := date.DayOfMonth(); result != 1 {
t.Errorf("Expected 1, got %d", result)
}
}
func TestDayOfWeekWeekOfYear(t *testing.T) {
datetime := "2023-03-01 00:00:00"
date, _ := NewDate(datetime)
if result := date.DayOfWeek(); result != 3 {
t.Errorf("Expected 3, got %d", result)
}
if result := date.WeekOfYear(); result != 9 {
t.Errorf("Expected 9, got %d", result)
}
datetime1 := "2023-12-31T12:00:00Z"
date1, _ := NewDate(datetime1)
if result := date1.DayOfMonth(); result != 31 {
t.Errorf("Expected 31, got %d", result)
}
if result := date1.DayOfWeek(); result != 7 {
t.Errorf("Expected 7, got %d", result)
}
if result := date1.WeekOfYear(); result != 52 {
t.Errorf("Expected 52, got %d", result)
}
}
+206
View File
@@ -0,0 +1,206 @@
package dara
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"reflect"
"strconv"
"github.com/alibabacloud-go/tea/tea"
)
type BaseError interface {
error
GetName() *string
GetCode() *string
}
type ResponseError interface {
BaseError
GetRetryAfter() *int64
GetStatusCode() *int
GetAccessDeniedDetail() map[string]interface{}
GetDescription() *string
GetData() map[string]interface{}
}
// SDKError struct is used save error code and message
type SDKError struct {
BaseError
Code *string
Name *string
StatusCode *int
Message *string
Data *string
Stack *string
errMsg *string
Description *string
AccessDeniedDetail map[string]interface{}
}
// CastError is used for cast type fails
type CastError struct {
Message *string
}
func TeaSDKError(err error) error {
if(err == nil) {
return nil
}
if te, ok := err.(*SDKError); ok {
return tea.NewSDKError(map[string]interface{}{
"code": StringValue(te.Code),
"statusCode": IntValue(te.StatusCode),
"message": StringValue(te.Message),
"data": te.Data,
"description": StringValue(te.Description),
"accessDeniedDetail": te.AccessDeniedDetail,
})
}
if respErr, ok := err.(ResponseError); ok {
return tea.NewSDKError(map[string]interface{}{
"code": StringValue(respErr.GetCode()),
"statusCode": IntValue(respErr.GetStatusCode()),
"message": respErr.Error(),
"description": StringValue(respErr.GetDescription()),
"data": respErr.GetData(),
"accessDeniedDetail": respErr.GetAccessDeniedDetail(),
})
}
if baseErr, ok := err.(BaseError); ok {
return tea.NewSDKError(map[string]interface{}{
"code": StringValue(baseErr.GetCode()),
"message": baseErr.Error(),
})
}
return err
}
// NewSDKError is used for shortly create SDKError object
func NewSDKError(obj map[string]interface{}) *SDKError {
err := &SDKError{}
err.Name = String("BaseError")
if val, ok := obj["code"].(int); ok {
err.Code = String(strconv.Itoa(val))
} else if val, ok := obj["code"].(string); ok {
err.Code = String(val)
}
if obj["message"] != nil {
err.Message = String(obj["message"].(string))
}
if obj["name"] != nil {
}
if obj["description"] != nil {
err.Description = String(obj["description"].(string))
}
if detail := obj["accessDeniedDetail"]; detail != nil {
r := reflect.ValueOf(detail)
if r.Kind().String() == "map" {
res := make(map[string]interface{})
tmp := r.MapKeys()
for _, key := range tmp {
res[key.String()] = r.MapIndex(key).Interface()
}
err.AccessDeniedDetail = res
}
}
if data := obj["data"]; data != nil {
r := reflect.ValueOf(data)
if r.Kind().String() == "map" {
res := make(map[string]interface{})
tmp := r.MapKeys()
for _, key := range tmp {
res[key.String()] = r.MapIndex(key).Interface()
}
if statusCode := res["statusCode"]; statusCode != nil {
if code, ok := statusCode.(int); ok {
err.StatusCode = Int(code)
} else if tmp, ok := statusCode.(string); ok {
code, err_ := strconv.Atoi(tmp)
if err_ == nil {
err.StatusCode = Int(code)
}
} else if code, ok := statusCode.(*int); ok {
err.StatusCode = code
}
}
}
byt := bytes.NewBuffer([]byte{})
jsonEncoder := json.NewEncoder(byt)
jsonEncoder.SetEscapeHTML(false)
jsonEncoder.Encode(data)
err.Data = String(string(bytes.TrimSpace(byt.Bytes())))
}
if statusCode, ok := obj["statusCode"].(int); ok {
err.StatusCode = Int(statusCode)
} else if status, ok := obj["statusCode"].(string); ok {
statusCode, err_ := strconv.Atoi(status)
if err_ == nil {
err.StatusCode = Int(statusCode)
}
}
return err
}
func (err *SDKError) ErrorName() *string {
return err.Name
}
func (err *SDKError) ErrorMessage() *string {
return err.Message
}
func (err *SDKError) GetCode() *string {
return err.Code
}
// Set ErrMsg by msg
func (err *SDKError) SetErrMsg(msg string) {
err.errMsg = String(msg)
}
func (err *SDKError) Error() string {
if err.errMsg == nil {
str := fmt.Sprintf("SDKError:\n StatusCode: %d\n Code: %s\n Message: %s\n Data: %s\n",
IntValue(err.StatusCode), StringValue(err.Code), StringValue(err.Message), StringValue(err.Data))
err.SetErrMsg(str)
}
return StringValue(err.errMsg)
}
// Return message of CastError
func (err *CastError) Error() string {
return StringValue(err.Message)
}
func Retryable(err error) *bool {
if err == nil {
return Bool(false)
}
if realErr, ok := err.(*SDKError); ok {
if realErr.StatusCode == nil {
return Bool(false)
}
code := IntValue(realErr.StatusCode)
return Bool(code >= http.StatusInternalServerError)
}
return Bool(true)
}
// NewCastError is used for cast type fails
func NewCastError(message *string) (err error) {
return &CastError{
Message: message,
}
}
+146
View File
@@ -0,0 +1,146 @@
package dara
import (
"os"
)
// File struct to represent the file
type DaraFile struct {
path string
fileInfo os.FileInfo
file *os.File
position int64
}
// NewFile creates a new instance of File
func NewDaraFile(path string) *DaraFile {
return &DaraFile{
path: path,
position: 0,
}
}
// Path returns the path of the file
func (tf *DaraFile) Path() string {
return tf.path
}
// CreateTime returns the creation time of the file
func (tf *DaraFile) CreateTime() (*Date, error) {
if tf.fileInfo == nil {
var err error
tf.fileInfo, err = os.Stat(tf.path)
if err != nil {
return nil, err
}
}
return &Date{tf.fileInfo.ModTime()}, nil
}
// ModifyTime returns the modification time of the file
func (tf *DaraFile) ModifyTime() (*Date, error) {
if tf.fileInfo == nil {
var err error
tf.fileInfo, err = os.Stat(tf.path)
if err != nil {
return nil, err
}
}
return &Date{tf.fileInfo.ModTime()}, nil
}
// Length returns the size of the file
func (tf *DaraFile) Length() (int64, error) {
if tf.fileInfo == nil {
var err error
tf.fileInfo, err = os.Stat(tf.path)
if err != nil {
return 0, err
}
}
return tf.fileInfo.Size(), nil
}
// Read reads a specified number of bytes from the file
func (tf *DaraFile) Read(size int) ([]byte, error) {
if tf.file == nil {
file, err := os.OpenFile(tf.path, os.O_RDWR|os.O_CREATE, 0755)
if err != nil {
return nil, err
}
tf.file = file
}
fileInfo, err := tf.file.Stat()
if err != nil {
return nil, err
}
// 获取文件大小
fileSize := fileInfo.Size()
// 计算可以读取的实际大小
if tf.position >= fileSize {
return nil, nil // End of file reached
}
// 确保 size 不超过剩余文件大小
actualSize := size
if tf.position+int64(size) > fileSize {
actualSize = int(fileSize - tf.position)
}
buf := make([]byte, actualSize)
bytesRead, err := tf.file.ReadAt(buf, tf.position)
if err != nil {
return nil, err
}
tf.position += int64(bytesRead)
return buf[:bytesRead], nil
}
// Write writes data to the file
func (tf *DaraFile) Write(data []byte) error {
if tf.file == nil {
file, err := os.OpenFile(tf.path, os.O_RDWR|os.O_CREATE, 0755)
if err != nil {
return err
}
tf.file = file
}
_, err := tf.file.Write(data)
if err != nil {
return err
}
tf.fileInfo, err = os.Stat(tf.path) // Update fileInfo after write
return err
}
// Close closes the file
func (tf *DaraFile) Close() error {
if tf.file == nil {
return nil
}
return tf.file.Close()
}
// Exists checks if the file exists
func Exists(path string) (bool, error) {
_, err := os.Stat(path)
if os.IsNotExist(err) {
return false, nil
}
return err == nil, err
}
// CreateReadStream would typically return an os.File or similar
func CreateReadStream(path string) (*os.File, error) {
return os.Open(path)
}
// CreateWriteStream would typically return an os.File or similar
func CreateWriteStream(path string) (*os.File, error) {
return os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
}
+183
View File
@@ -0,0 +1,183 @@
package dara
import (
"io"
"io/ioutil"
"os"
"path/filepath"
"testing"
)
// TestNewDaraFile tests the NewDaraFile function
func TestNewDaraFile(t *testing.T) {
path := "testfile.txt"
tf := NewDaraFile(path)
if tf.Path() != path {
t.Errorf("Expected path to be %s, got %s", path, tf.Path())
}
}
// TestCreateTime tests the CreateTime method
func TestCreateTime(t *testing.T) {
path := "testfile.txt"
ioutil.WriteFile(path, []byte("test"), 0644) // 创建文件以确保它存在
defer os.Remove(path)
tf := NewDaraFile(path)
date, err := tf.CreateTime()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if date == nil {
t.Error("expected a valid TeaDate, got nil")
}
}
// TestModifyTime tests the ModifyTime method
func TestModifyTime(t *testing.T) {
path := "testfile.txt"
ioutil.WriteFile(path, []byte("test"), 0644)
defer os.Remove(path)
tf := NewDaraFile(path)
date, err := tf.ModifyTime()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if date == nil {
t.Error("expected a valid TeaDate, got nil")
}
}
// TestLength tests the Length method
func TestLength(t *testing.T) {
path := "testfile.txt"
content := []byte("Hello, World!")
ioutil.WriteFile(path, content, 0644)
defer os.Remove(path)
tf := NewDaraFile(path)
length, err := tf.Length()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if length != int64(len(content)) {
t.Errorf("expected length %d, got %d", len(content), length)
}
}
// TestRead tests the Read method
func TestRead(t *testing.T) {
path := "testfile.txt"
content := []byte("Hello, World!")
ioutil.WriteFile(path, content, 0644)
defer os.Remove(path)
tf := NewDaraFile(path)
data, err := tf.Read(5)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if string(data) != "Hello" {
t.Errorf("expected 'Hello', got '%s'", string(data))
}
// Read the rest of the file
data, err = tf.Read(10)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if string(data) != ", World!" {
t.Errorf("expected ', World!', got '%s'", string(data))
}
}
// TestWrite tests the Write method
func TestWrite(t *testing.T) {
path := "testfile.txt"
tf := NewDaraFile(path)
data := []byte("Hello, Write Test!")
err := tf.Write(data)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// Validate the content of the file
readData, _ := ioutil.ReadFile(path)
if string(readData) != "Hello, Write Test!" {
t.Errorf("expected file content to be %s, got %s", string(data), string(readData))
}
}
// TestClose tests the Close method
func TestClose(t *testing.T) {
path := "testfile.txt"
tf := NewDaraFile(path)
err := tf.Close()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
// TestExists tests the Exists function
func TestExists(t *testing.T) {
path := "testfile.txt"
ioutil.WriteFile(path, []byte("test"), 0644)
defer os.Remove(path)
exists, err := Exists(path)
if err != nil || !exists {
t.Errorf("expected file to exist, got error: %v", err)
}
exists, err = Exists("nonexistent.txt")
if err != nil || exists {
t.Errorf("expected file to not exist, got error: %v", err)
}
}
func TestCreateReadWriteStream(t *testing.T) {
tempDir, err := ioutil.TempDir("", "example")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tempDir)
testFile := filepath.Join(tempDir, "test.txt")
testWFile := filepath.Join(tempDir, "test2.txt")
// Prepare the test file
originalContent := "Hello, World!"
if err := ioutil.WriteFile(testFile, []byte(originalContent), 0644); err != nil {
t.Fatalf("failed to write test file: %v", err)
}
// Test CreateReadStream
rs, err := CreateReadStream(testFile)
if err != nil {
t.Fatalf("failed to create read stream: %v", err)
}
defer rs.Close()
// Test CreateWriteStream
ws, err := CreateWriteStream(testWFile)
if err != nil {
t.Fatalf("failed to create write stream: %v", err)
}
defer ws.Close()
// Pipe data from read stream to write stream
if _, err := io.Copy(ws, rs); err != nil {
t.Fatalf("failed to copy data from read stream to write stream: %v", err)
}
// Read back the content to check if it's correct
data, err := ioutil.ReadFile(testWFile)
if err != nil {
t.Fatalf("failed to read back test file: %v", err)
}
if string(data) != originalContent {
t.Fatalf("expected %q but got %q", originalContent, string(data))
}
}
+177
View File
@@ -0,0 +1,177 @@
package dara
import (
"bytes"
"fmt"
"io"
"math/rand"
"net/url"
"strings"
)
type FileField struct {
Filename *string `json:"filename" xml:"filename" require:"true"`
ContentType *string `json:"content-type" xml:"content-type" require:"true"`
Content io.Reader `json:"content" xml:"content" require:"true"`
}
func (s *FileField) SetFilename(v string) *FileField {
s.Filename = &v
return s
}
func (s *FileField) SetContentType(v string) *FileField {
s.ContentType = &v
return s
}
func (s *FileField) SetContent(v io.Reader) *FileField {
s.Content = v
return s
}
type FileFormReader struct {
formFiles []*formFile
formField io.Reader
index int
streaming bool
ifField bool
}
type formFile struct {
StartField io.Reader
EndField io.Reader
File io.Reader
start bool
end bool
}
const numBytes = "1234567890"
func GetBoundary() string {
b := make([]byte, 14)
for i := range b {
b[i] = numBytes[rand.Intn(len(numBytes))]
}
return string(b)
}
func ToFileForm(body map[string]interface{}, boundary string) io.Reader {
out := bytes.NewBuffer(nil)
line := "--" + boundary + "\r\n"
forms := make(map[string]string)
files := make(map[string]map[string]interface{})
for key, value := range body {
switch value.(type) {
case *FileField:
if val, ok := value.(*FileField); ok {
out := make(map[string]interface{})
out["filename"] = StringValue(val.Filename)
out["content-type"] = StringValue(val.ContentType)
out["content"] = val.Content
files[key] = out
}
case map[string]interface{}:
if val, ok := value.(map[string]interface{}); ok {
files[key] = val
}
default:
forms[key] = fmt.Sprintf("%v", value)
}
}
for key, value := range forms {
if value != "" {
out.Write([]byte(line))
out.Write([]byte("Content-Disposition: form-data; name=\"" + key + "\"" + "\r\n\r\n"))
out.Write([]byte(value + "\r\n"))
}
}
formFiles := make([]*formFile, 0)
for key, value := range files {
var file io.Reader
start := line
start += "Content-Disposition: form-data; name=\"" + key + "\"; filename=\"" + value["filename"].(string) + "\"\r\n"
start += "Content-Type: " + value["content-type"].(string) + "\r\n\r\n"
if content, ok := value["content"].(io.Reader); ok {
file = content
} else {
file = strings.NewReader("")
}
formFile := &formFile{
File: file,
start: true,
StartField: strings.NewReader(start),
}
if len(files) == len(formFiles)+1 {
end := "\r\n\r\n--" + boundary + "--\r\n"
formFile.EndField = strings.NewReader(end)
} else {
formFile.EndField = strings.NewReader("\r\n\r\n")
}
formFiles = append(formFiles, formFile)
}
return &FileFormReader{
formFiles: formFiles,
formField: out,
ifField: true,
}
}
func (f *FileFormReader) Read(p []byte) (n int, err error) {
if f.ifField {
n, err = f.formField.Read(p)
if err != nil && err != io.EOF {
return n, err
} else if err == io.EOF {
err = nil
f.ifField = false
f.streaming = true
}
} else if f.streaming {
form := f.formFiles[f.index]
if form.start {
n, err = form.StartField.Read(p)
if err != nil && err != io.EOF {
return n, err
} else if err == io.EOF {
err = nil
form.start = false
}
} else if form.end {
n, err = form.EndField.Read(p)
if err != nil && err != io.EOF {
return n, err
} else if err == io.EOF {
f.index++
form.end = false
if f.index < len(f.formFiles) {
err = nil
}
}
} else {
n, err = form.File.Read(p)
if err != nil && err != io.EOF {
return n, err
} else if err == io.EOF {
err = nil
form.end = true
}
}
}
return n, err
}
func ToFormString(a map[string]interface{}) string {
if a == nil {
return ""
}
res := ""
urlEncoder := url.Values{}
for key, value := range a {
v := fmt.Sprintf("%v", value)
urlEncoder.Add(key, v)
}
res = urlEncoder.Encode()
return res
}
+64
View File
@@ -0,0 +1,64 @@
package dara
import (
"io/ioutil"
"strings"
"testing"
"github.com/alibabacloud-go/tea/utils"
)
func Test_ToFormString(t *testing.T) {
str := ToFormString(nil)
utils.AssertEqual(t, "", str)
a := map[string]interface{}{
"key1": "value1",
"key2": "value2",
}
str = ToFormString(a)
utils.AssertEqual(t, str, "key1=value1&key2=value2")
}
type TestForm struct {
Ak *string `json:"ak"`
File1 *FileField `json:"file1"`
File2 *FileField `json:"file2"`
}
func Test_ToFileForm(t *testing.T) {
file1 := new(FileField).
SetContent(strings.NewReader("ok")).
SetContentType("jpg").
SetFilename("a.jpg")
body := map[string]interface{}{
"ak": "accesskey",
"file1": file1,
}
res := ToFileForm(ToMap(body), "28802961715230")
byt, err := ioutil.ReadAll(res)
utils.AssertNil(t, err)
utils.AssertEqual(t, string(byt), "--28802961715230\r\nContent-Disposition: "+
"form-data; name=\"ak\"\r\n\r\naccesskey\r\n--28802961715230\r\nContent-Disposition: "+
"form-data; name=\"file1\"; filename=\"a.jpg\"\r\nContent-Type: jpg\r\n\r\nok\r\n\r\n--28802961715230--\r\n")
body1 := &TestForm{
Ak: String("accesskey"),
File1: &FileField{
Filename: String("a.jpg"),
ContentType: String("jpg"),
Content: strings.NewReader("ok"),
},
}
res = ToFileForm(ToMap(body1), "28802961715230")
byt, err = ioutil.ReadAll(res)
utils.AssertNil(t, err)
utils.AssertEqual(t, string(byt), "--28802961715230\r\nContent-Disposition: form-data; "+
"name=\"ak\"\r\n\r\naccesskey\r\n--28802961715230\r\nContent-Disposition: "+
"form-data; name=\"file1\"; filename=\"a.jpg\"\r\nContent-Type: jpg\r\n\r\n\r\n\r\n--28802961715230--\r\n")
}
func Test_GetBoundary(t *testing.T) {
bound := GetBoundary()
utils.AssertEqual(t, len(bound), 14)
}
+389
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+49
View File
@@ -0,0 +1,49 @@
package dara
import (
"reflect"
)
type Entry struct {
Key string
Value interface{}
}
// toFloat64 converts a numeric value to float64 for comparison
func Entries(m interface{}) []*Entry {
v := reflect.ValueOf(m)
if v.Kind() != reflect.Map {
panic("Entries: input must be a map")
}
entries := make([]*Entry, 0, v.Len())
for _, key := range v.MapKeys() {
// 确保 Key 是字符串类型
if key.Kind() != reflect.String {
panic("Entries: map keys must be of type string")
}
value := v.MapIndex(key)
entries = append(entries, &Entry{
Key: key.String(),
Value: value.Interface(),
})
}
return entries
}
func KeySet(m interface{}) []string {
v := reflect.ValueOf(m)
if v.Kind() != reflect.Map {
panic("KeySet: input must be a map")
}
keys := make([]string, 0, v.Len())
for _, key := range v.MapKeys() {
if key.Kind() != reflect.String {
panic("KeySet: map keys must be of type string")
}
keys = append(keys, key.String())
}
return keys
}
+59
View File
@@ -0,0 +1,59 @@
package dara
import (
"reflect"
"testing"
)
type Person struct {
Name string
Age int
}
func TestEntries(t *testing.T) {
// 定义一个包含多种类型的 map
type Person struct {
Name string
Age int
}
testMap := map[string]interface{}{
"one": 1,
"two": "two",
"three": &Person{Name: "Alice", Age: 30},
}
entries := Entries(testMap)
if len(entries) != 3 {
t.Errorf("expected %d entries, got %d", 3, len(entries))
}
for _, entry := range entries {
if !reflect.DeepEqual(entry.Value, testMap[entry.Key]) {
t.Errorf("expected entry %s to be %v, got %v", entry.Key, testMap[entry.Key], entry.Value)
}
}
}
func TestKeySet(t *testing.T) {
testMap := map[string]interface{}{
"one": 1,
"two": "two",
"three": &Person{Name: "Alice", Age: 30},
}
keys := KeySet(testMap)
str1, str2, str3 := "one", "two", "three"
expectedKeys := []*string{&str1, &str2, &str3}
if len(keys) != len(expectedKeys) {
t.Errorf("expected %d keys, got %d", len(expectedKeys), len(keys))
}
for _, key := range keys {
if !ArrContains(expectedKeys, key) {
t.Errorf("expected key %s to be in the array %v, but not", key, expectedKeys)
}
}
}
+45
View File
@@ -0,0 +1,45 @@
package dara
import (
"math"
"math/rand"
"reflect"
"time"
)
// Number is an interface that can be implemented by any numeric type
type Number interface{}
// toFloat64 converts a numeric value to float64 for comparison
func toFloat64(n Number) float64 {
v := reflect.ValueOf(n)
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float64(v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return float64(v.Uint())
case reflect.Float32, reflect.Float64:
return v.Float()
default:
panic("unsupported type")
}
}
// Floor returns the largest integer less than or equal to the input number as int
func Floor(n Number) int {
v := toFloat64(n)
floorValue := math.Floor(v)
return int(floorValue)
}
// Round returns the nearest integer to the input number as int
func Round(n Number) int {
v := toFloat64(n)
roundValue := math.Round(v)
return int(roundValue)
}
func Random() float64 {
rand.Seed(time.Now().UnixNano())
return rand.Float64()
}
+62
View File
@@ -0,0 +1,62 @@
package dara
import (
"testing"
)
// TestRandom tests the Random function to ensure it returns a value in the range [0.0, 1.0)
func TestRandom(t *testing.T) {
for i := 0; i < 100; i++ {
val := Random()
if val < 0.0 || val >= 1.0 {
t.Errorf("Random() = %v, want [0.0, 1.0)", val)
}
}
}
// TestFloor tests the Floor function with various numeric inputs
func TestFloor(t *testing.T) {
tests := []struct {
input Number
expected int
}{
{3.7, 3},
{-3.7, -4},
{0.9, 0},
{0.0, 0},
{-0.9, -1},
{int64(3), 3},
{int32(-3), -3},
}
for _, tt := range tests {
result := Floor(tt.input)
if result != tt.expected {
t.Errorf("Floor(%v) = %v, want %v", tt.input, result, tt.expected)
}
}
}
// TestRound tests the Round function with various numeric inputs
func TestRound(t *testing.T) {
tests := []struct {
input Number
expected int
}{
{3.7, 4},
{-3.7, -4},
{2.5, 3},
{2.4, 2},
{-2.5, -3},
{0.0, 0},
{int64(4), 4},
{int32(-4), -4},
}
for _, tt := range tests {
result := Round(tt.input)
if result != tt.expected {
t.Errorf("Round(%v) = %v, want %v", tt.input, result, tt.expected)
}
}
}
+280
View File
@@ -0,0 +1,280 @@
package dara
import (
"encoding/json"
"errors"
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
)
type Model interface {
Validate() error
ToMap() map[string]interface{}
copyWithouStream() Model
}
func Validate(params interface{}) error {
if params == nil {
return nil
}
requestValue := reflect.ValueOf(params)
if requestValue.IsNil() {
return nil
}
err := validate(requestValue.Elem())
return err
}
// Verify whether the parameters meet the requirements
func validate(dataValue reflect.Value) error {
if strings.HasPrefix(dataValue.Type().String(), "*") { // Determines whether the input is a structure object or a pointer object
if dataValue.IsNil() {
return nil
}
dataValue = dataValue.Elem()
}
dataType := dataValue.Type()
for i := 0; i < dataType.NumField(); i++ {
field := dataType.Field(i)
valueField := dataValue.Field(i)
for _, value := range validateParams {
err := validateParam(field, valueField, value)
if err != nil {
return err
}
}
}
return nil
}
func validateParam(field reflect.StructField, valueField reflect.Value, tagName string) error {
tag, containsTag := field.Tag.Lookup(tagName) // Take out the checked regular expression
if containsTag && tagName == "require" {
err := checkRequire(field, valueField)
if err != nil {
return err
}
}
if strings.HasPrefix(field.Type.String(), "[]") { // Verify the parameters of the array type
err := validateSlice(field, valueField, containsTag, tag, tagName)
if err != nil {
return err
}
} else if valueField.Kind() == reflect.Ptr { // Determines whether it is a pointer object
err := validatePtr(field, valueField, containsTag, tag, tagName)
if err != nil {
return err
}
}
return nil
}
func validateSlice(field reflect.StructField, valueField reflect.Value, containsregexpTag bool, tag, tagName string) error {
if valueField.IsValid() && !valueField.IsNil() { // Determines whether the parameter has a value
if containsregexpTag {
if tagName == "maxItems" {
err := checkMaxItems(field, valueField, tag)
if err != nil {
return err
}
}
if tagName == "minItems" {
err := checkMinItems(field, valueField, tag)
if err != nil {
return err
}
}
}
for m := 0; m < valueField.Len(); m++ {
elementValue := valueField.Index(m)
if elementValue.Type().Kind() == reflect.Ptr { // Determines whether the child elements of an array are of a basic type
err := validatePtr(field, elementValue, containsregexpTag, tag, tagName)
if err != nil {
return err
}
}
}
}
return nil
}
func validatePtr(field reflect.StructField, elementValue reflect.Value, containsregexpTag bool, tag, tagName string) error {
if elementValue.IsNil() {
return nil
}
if isFilterType(elementValue.Elem().Type().String(), basicTypes) {
if containsregexpTag {
if tagName == "pattern" {
err := checkPattern(field, elementValue.Elem(), tag)
if err != nil {
return err
}
}
if tagName == "maxLength" {
err := checkMaxLength(field, elementValue.Elem(), tag)
if err != nil {
return err
}
}
if tagName == "minLength" {
err := checkMinLength(field, elementValue.Elem(), tag)
if err != nil {
return err
}
}
if tagName == "maximum" {
err := checkMaximum(field, elementValue.Elem(), tag)
if err != nil {
return err
}
}
if tagName == "minimum" {
err := checkMinimum(field, elementValue.Elem(), tag)
if err != nil {
return err
}
}
}
} else {
err := validate(elementValue)
if err != nil {
return err
}
}
return nil
}
func checkRequire(field reflect.StructField, valueField reflect.Value) error {
name, _ := field.Tag.Lookup("json")
strs := strings.Split(name, ",")
name = strs[0]
if !valueField.IsNil() && valueField.IsValid() {
return nil
}
return errors.New(name + " should be setted")
}
func checkPattern(field reflect.StructField, valueField reflect.Value, tag string) error {
if valueField.IsValid() && valueField.String() != "" {
value := valueField.String()
r, _ := regexp.Compile("^" + tag + "$")
if match := r.MatchString(value); !match { // Determines whether the parameter value satisfies the regular expression or not, and throws an error
return errors.New(value + " is not matched " + tag)
}
}
return nil
}
func checkMaxItems(field reflect.StructField, valueField reflect.Value, tag string) error {
if valueField.IsValid() && valueField.String() != "" {
maxItems, err := strconv.Atoi(tag)
if err != nil {
return err
}
length := valueField.Len()
if maxItems < length {
errMsg := fmt.Sprintf("The length of %s is %d which is more than %d", field.Name, length, maxItems)
return errors.New(errMsg)
}
}
return nil
}
func checkMinItems(field reflect.StructField, valueField reflect.Value, tag string) error {
if valueField.IsValid() {
minItems, err := strconv.Atoi(tag)
if err != nil {
return err
}
length := valueField.Len()
if minItems > length {
errMsg := fmt.Sprintf("The length of %s is %d which is less than %d", field.Name, length, minItems)
return errors.New(errMsg)
}
}
return nil
}
func checkMaxLength(field reflect.StructField, valueField reflect.Value, tag string) error {
if valueField.IsValid() && valueField.String() != "" {
maxLength, err := strconv.Atoi(tag)
if err != nil {
return err
}
length := valueField.Len()
if valueField.Kind().String() == "string" {
length = strings.Count(valueField.String(), "") - 1
}
if maxLength < length {
errMsg := fmt.Sprintf("The length of %s is %d which is more than %d", field.Name, length, maxLength)
return errors.New(errMsg)
}
}
return nil
}
func checkMinLength(field reflect.StructField, valueField reflect.Value, tag string) error {
if valueField.IsValid() {
minLength, err := strconv.Atoi(tag)
if err != nil {
return err
}
length := valueField.Len()
if valueField.Kind().String() == "string" {
length = strings.Count(valueField.String(), "") - 1
}
if minLength > length {
errMsg := fmt.Sprintf("The length of %s is %d which is less than %d", field.Name, length, minLength)
return errors.New(errMsg)
}
}
return nil
}
func checkMaximum(field reflect.StructField, valueField reflect.Value, tag string) error {
if valueField.IsValid() && valueField.String() != "" {
maximum, err := strconv.ParseFloat(tag, 64)
if err != nil {
return err
}
byt, _ := json.Marshal(valueField.Interface())
num, err := strconv.ParseFloat(string(byt), 64)
if err != nil {
return err
}
if maximum < num {
errMsg := fmt.Sprintf("The size of %s is %f which is greater than %f", field.Name, num, maximum)
return errors.New(errMsg)
}
}
return nil
}
func checkMinimum(field reflect.StructField, valueField reflect.Value, tag string) error {
if valueField.IsValid() && valueField.String() != "" {
minimum, err := strconv.ParseFloat(tag, 64)
if err != nil {
return err
}
byt, _ := json.Marshal(valueField.Interface())
num, err := strconv.ParseFloat(string(byt), 64)
if err != nil {
return err
}
if minimum > num {
errMsg := fmt.Sprintf("The size of %s is %f which is less than %f", field.Name, num, minimum)
return errors.New(errMsg)
}
}
return nil
}
+373
View File
File diff suppressed because it is too large Load Diff
+619
View File
File diff suppressed because it is too large Load Diff
+138
View File
@@ -0,0 +1,138 @@
package dara
import (
"bufio"
"bytes"
"encoding/json"
"io"
"io/ioutil"
"strings"
"fmt"
)
// 定义 Event 结构体
type SSEEvent struct {
ID *string
Event *string
Data *string
Retry *int
}
// 解析单个事件
func parseEvent(lines []string) *SSEEvent {
event := &SSEEvent{}
for _, line := range lines {
if strings.HasPrefix(line, "data:") {
data := strings.TrimPrefix(line, "data:") + "\n"
if event.Data == nil {
event.Data = new(string)
}
*event.Data += data
} else if strings.HasPrefix(line, "event:") {
eventName := strings.TrimPrefix(line, "event:")
event.Event = &eventName
} else if strings.HasPrefix(line, "id:") {
id := strings.TrimPrefix(line, "id:")
event.ID = &id
} else if strings.HasPrefix(line, "retry:") {
var retry int
fmt.Sscanf(strings.TrimPrefix(line, "retry:"), "%d", &retry)
event.Retry = &retry
}
}
// Remove last newline from data
if event.Data != nil {
data := strings.TrimRight(*event.Data, "\n")
event.Data = &data
}
return event
}
func ReadAsBytes(body io.Reader) ([]byte, error) {
byt, err := ioutil.ReadAll(body)
if err != nil {
return nil, err
}
r, ok := body.(io.ReadCloser)
if ok {
r.Close()
}
return byt, nil
}
func ReadAsJSON(body io.Reader) (result interface{}, err error) {
byt, err := ioutil.ReadAll(body)
if err != nil {
return
}
if string(byt) == "" {
return
}
r, ok := body.(io.ReadCloser)
if ok {
r.Close()
}
d := json.NewDecoder(bytes.NewReader(byt))
d.UseNumber()
err = d.Decode(&result)
return
}
func ReadAsString(body io.Reader) (string, error) {
byt, err := ioutil.ReadAll(body)
if err != nil {
return "", err
}
r, ok := body.(io.ReadCloser)
if ok {
r.Close()
}
return string(byt), nil
}
func ReadAsSSE(body io.ReadCloser, eventChannel chan *SSEEvent, errorChannel chan error) {
go func() {
defer func() {
body.Close()
close(eventChannel)
}()
reader := bufio.NewReader(body)
var eventLines []string
for {
line, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
// Handle the end of the stream and possibly pending event
if len(eventLines) > 0 {
event := parseEvent(eventLines)
eventChannel <- event
}
errorChannel <- nil
return
}
errorChannel <- err
return
}
line = strings.TrimRight(line, "\n")
if line == "" {
// End of an SSE event
if len(eventLines) > 0 {
event := parseEvent(eventLines)
eventChannel <- event
eventLines = []string{} // Reset for the next event
}
continue
}
eventLines = append(eventLines, line)
}
}()
return
}
+47
View File
@@ -0,0 +1,47 @@
package dara
import (
"io/ioutil"
"strings"
"testing"
"github.com/alibabacloud-go/tea/utils"
)
func Test_ReadAsBytes(t *testing.T) {
byt, err := ReadAsBytes(strings.NewReader("common"))
utils.AssertNil(t, err)
utils.AssertEqual(t, "common", string(byt))
byt, err = ReadAsBytes(ioutil.NopCloser(strings.NewReader("common")))
utils.AssertNil(t, err)
utils.AssertEqual(t, "common", string(byt))
}
func Test_ReadAsJSON(t *testing.T) {
result, err := ReadAsJSON(strings.NewReader(`{"cleint":"test"}`))
if res, ok := result.(map[string]interface{}); ok {
utils.AssertNil(t, err)
utils.AssertEqual(t, "test", res["cleint"])
}
result, err = ReadAsJSON(strings.NewReader(""))
utils.AssertNil(t, err)
utils.AssertNil(t, result)
result, err = ReadAsJSON(ioutil.NopCloser(strings.NewReader(`{"cleint":"test"}`)))
if res, ok := result.(map[string]interface{}); ok {
utils.AssertNil(t, err)
utils.AssertEqual(t, "test", res["cleint"])
}
}
func Test_ReadAsString(t *testing.T) {
str, err := ReadAsString(strings.NewReader("common"))
utils.AssertNil(t, err)
utils.AssertEqual(t, "common", str)
str, err = ReadAsString(ioutil.NopCloser(strings.NewReader("common")))
utils.AssertNil(t, err)
utils.AssertEqual(t, "common", str)
}
+491
View File
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More