Repository: caicloud/nirvana
Pull request #352 (2020-08-25 06:36:23)

feat(time): support convert to time
<!--  Thanks for sending a pull request! See below for tips! -->

**What this PR does / why we need it**:

Add your description

**Which issue(s) this PR is related to** *(optional, link to 3rd issue(s))*:

Fixes #

Reference to #
<!-- 填在 Fixes，PR 合并就会关 issue。填在 Reference to 会关联 issue，不会联动关闭。-->

**Special notes for your reviewer**:

/cc @your-reviewer

<!-- Please answer the following questions during the code freeze, and delete this line.
**Code freeze questions**

1. What causes this PR to not be merged before code freeze?
2. Why this PR is absolutely necessary for this version? Paste a screenshot of smoke testing docs if you could.
3. What's the effects after merging it?
4. Is there anyway we can skip this to not affect the overall process?
-->

**Release note**:
<!--  Write your release note:
1. Enter your extended release note in the below block. If the PR requires additional action from users switching to the new release, include the string "action required".
2. If no release note is required, just write "NONE".
-->

```release-note
NONE
```

<!--  Thanks for sending a pull request! Here are some tips:

1. https://github.com/caicloud/engineering/blob/master/guidelines/review_conventions.md      <-- what is the review process looks like
2. https://github.com/caicloud/engineering/blob/master/guidelines/git_commit_conventions.md  <-- how to structure your git commit
3. https://github.com/caicloud/engineering/blob/master/guidelines/caicloud_bot.md            <-- how to work with caicloud bot

Other tips:

If this is your first contribution, read our Getting Started guide https://github.com/caicloud/engineering/blob/master/guidelines/README.md
-->

caicloud-bot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: 
To fully approve this pull request, please assign additional approvers.
We suggest the following additional approver: **kdada**

Assign the PR to them by writing `/assign @kdada` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://github.com/caicloud/engineering/blob/master/guidelines/caicloud_bot.md#bot-commands).

The pull request process is described [here](https://github.com/caicloud/engineering/blob/master/guidelines/caicloud_bot.md#typical-workflow)

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/caicloud/nirvana/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["kdada"]} -->
whalecold: @iawia002 
hezhizhen: /lgtm
whalecold: /cherrypick release-0.2
caicloud-bot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/caicloud/nirvana/pull/352#issuecomment-679834880" title="LGTM">hezhizhen</a>*
To fully approve this pull request, please assign additional approvers.
We suggest the following additional approver: **kdada**

Assign the PR to them by writing `/assign @kdada` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://github.com/caicloud/engineering/blob/master/guidelines/caicloud_bot.md#bot-commands).

The pull request process is described [here](https://github.com/caicloud/engineering/blob/master/guidelines/caicloud_bot.md#typical-workflow)

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/caicloud/nirvana/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["kdada"]} -->
caicloud-bot: @whalecold: once the present PR merges, I will cherry-pick it on top of release-0.2 in a new PR and assign it to you.

<details>

In response to [this](https://github.com/caicloud/nirvana/pull/352#issuecomment-679834970):

>/cherrypick release-0.2


Instructions for interacting with me using PR comments are available [here](https://github.com/caicloud/engineering/blob/master/docs/caicloud_bot.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
caicloud-bot: New changes are detected. LGTM label has been removed.
iawia002: /lgtm
caicloud-bot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/caicloud/nirvana/pull/352#issuecomment-679834880" title="LGTM">hezhizhen</a>*, *<a href="https://github.com/caicloud/nirvana/pull/352#issuecomment-679839585" title="LGTM">iawia002</a>*

The full list of commands accepted by this bot can be found [here](https://github.com/caicloud/engineering/blob/master/guidelines/caicloud_bot.md#bot-commands).

The pull request process is described [here](https://github.com/caicloud/engineering/blob/master/guidelines/caicloud_bot.md#typical-workflow)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/caicloud/nirvana/blob/master/OWNERS)~~ [iawia002]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/service/content.go b/service/content.go
index 9c80e49..5b47344 100644
--- a/service/content.go
+++ b/service/content.go
@@ -25,6 +25,7 @@ import (
 	"io/ioutil"
 	"reflect"
 	"strconv"
+	"time"
 
 	"github.com/caicloud/nirvana/definition"
 )
@@ -370,38 +371,40 @@ func (p *ContextPrefab) Make(ctx context.Context) (interface{}, error) {
 type Converter func(ctx context.Context, data []string) (interface{}, error)
 
 var converters = map[reflect.Type]Converter{
-	reflect.TypeOf(bool(false)):  ConvertToBool,
-	reflect.TypeOf(int(0)):       ConvertToInt,
-	reflect.TypeOf(int8(0)):      ConvertToInt8,
-	reflect.TypeOf(int16(0)):     ConvertToInt16,
-	reflect.TypeOf(int32(0)):     ConvertToInt32,
-	reflect.TypeOf(int64(0)):     ConvertToInt64,
-	reflect.TypeOf(uint(0)):      ConvertToUint,
-	reflect.TypeOf(uint8(0)):     ConvertToUint8,
-	reflect.TypeOf(uint16(0)):    ConvertToUint16,
-	reflect.TypeOf(uint32(0)):    ConvertToUint32,
-	reflect.TypeOf(uint64(0)):    ConvertToUint64,
-	reflect.TypeOf(float32(0)):   ConvertToFloat32,
-	reflect.TypeOf(float64(0)):   ConvertToFloat64,
-	reflect.TypeOf(string("")):   ConvertToString,
-	reflect.TypeOf(new(bool)):    ConvertToBoolP,
-	reflect.TypeOf(new(int)):     ConvertToIntP,
-	reflect.TypeOf(new(int8)):    ConvertToInt8P,
-	reflect.TypeOf(new(int16)):   ConvertToInt16P,
-	reflect.TypeOf(new(int32)):   ConvertToInt32P,
-	reflect.TypeOf(new(int64)):   ConvertToInt64P,
-	reflect.TypeOf(new(uint)):    ConvertToUintP,
-	reflect.TypeOf(new(uint8)):   ConvertToUint8P,
-	reflect.TypeOf(new(uint16)):  ConvertToUint16P,
-	reflect.TypeOf(new(uint32)):  ConvertToUint32P,
-	reflect.TypeOf(new(uint64)):  ConvertToUint64P,
-	reflect.TypeOf(new(float32)): ConvertToFloat32P,
-	reflect.TypeOf(new(float64)): ConvertToFloat64P,
-	reflect.TypeOf(new(string)):  ConvertToStringP,
-	reflect.TypeOf([]bool{}):     ConvertToBoolSlice,
-	reflect.TypeOf([]int{}):      ConvertToIntSlice,
-	reflect.TypeOf([]float64{}):  ConvertToFloat64Slice,
-	reflect.TypeOf([]string{}):   ConvertToStringSlice,
+	reflect.TypeOf(false):          ConvertToBool,
+	reflect.TypeOf(0):              ConvertToInt,
+	reflect.TypeOf(int8(0)):        ConvertToInt8,
+	reflect.TypeOf(int16(0)):       ConvertToInt16,
+	reflect.TypeOf(int32(0)):       ConvertToInt32,
+	reflect.TypeOf(int64(0)):       ConvertToInt64,
+	reflect.TypeOf(uint(0)):        ConvertToUint,
+	reflect.TypeOf(uint8(0)):       ConvertToUint8,
+	reflect.TypeOf(uint16(0)):      ConvertToUint16,
+	reflect.TypeOf(uint32(0)):      ConvertToUint32,
+	reflect.TypeOf(uint64(0)):      ConvertToUint64,
+	reflect.TypeOf(float32(0)):     ConvertToFloat32,
+	reflect.TypeOf(float64(0)):     ConvertToFloat64,
+	reflect.TypeOf(""):             ConvertToString,
+	reflect.TypeOf(time.Time{}):    ConvertToTime,
+	reflect.TypeOf(new(bool)):      ConvertToBoolP,
+	reflect.TypeOf(new(int)):       ConvertToIntP,
+	reflect.TypeOf(new(int8)):      ConvertToInt8P,
+	reflect.TypeOf(new(int16)):     ConvertToInt16P,
+	reflect.TypeOf(new(int32)):     ConvertToInt32P,
+	reflect.TypeOf(new(int64)):     ConvertToInt64P,
+	reflect.TypeOf(new(uint)):      ConvertToUintP,
+	reflect.TypeOf(new(uint8)):     ConvertToUint8P,
+	reflect.TypeOf(new(uint16)):    ConvertToUint16P,
+	reflect.TypeOf(new(uint32)):    ConvertToUint32P,
+	reflect.TypeOf(new(uint64)):    ConvertToUint64P,
+	reflect.TypeOf(new(float32)):   ConvertToFloat32P,
+	reflect.TypeOf(new(float64)):   ConvertToFloat64P,
+	reflect.TypeOf(new(string)):    ConvertToStringP,
+	reflect.TypeOf(new(time.Time)): ConvertToTimeP,
+	reflect.TypeOf([]bool{}):       ConvertToBoolSlice,
+	reflect.TypeOf([]int{}):        ConvertToIntSlice,
+	reflect.TypeOf([]float64{}):    ConvertToFloat64Slice,
+	reflect.TypeOf([]string{}):     ConvertToStringSlice,
 }
 
 // ConverterFor gets converter for specified type.
@@ -665,6 +668,16 @@ func ConvertToFloat64(ctx context.Context, data []string) (interface{}, error) {
 	return target, nil
 }
 
+// ConvertToTime converts []string to time.Time. Only the first data is used.
+func ConvertToTime(ctx context.Context, data []string) (interface{}, error) {
+	origin := data[0]
+	target, err := time.Parse(time.RFC3339, origin)
+	if err != nil {
+		return nil, invalidConversion.Error(origin, "time")
+	}
+	return target, nil
+}
+
 // ConvertToFloat64P converts []string to *float64. Only the first data is used.
 func ConvertToFloat64P(ctx context.Context, data []string) (interface{}, error) {
 	ret, err := ConvertToFloat64(ctx, data)
@@ -685,6 +698,16 @@ func ConvertToStringP(ctx context.Context, data []string) (interface{}, error) {
 	return &data[0], nil
 }
 
+// ConvertToTimeP return the first element's pointer in []string.
+func ConvertToTimeP(ctx context.Context, data []string) (interface{}, error) {
+	ret, err := ConvertToTime(ctx, data)
+	if err != nil {
+		return nil, err
+	}
+	value := ret.(time.Time)
+	return &value, nil
+}
+
 // ConvertToBoolSlice converts all elements in data to bool, and return []bool
 func ConvertToBoolSlice(ctx context.Context, data []string) (interface{}, error) {
 	ret := make([]bool, len(data))
diff --git a/utils/generators/swagger/generator.go b/utils/generators/swagger/generator.go
index e0e77d3..a6296f2 100644
--- a/utils/generators/swagger/generator.go
+++ b/utils/generators/swagger/generator.go
@@ -498,38 +498,40 @@ func (g *Generator) generateAutoParameter(typ api.TypeName) []spec.Parameter {
 }
 
 var converters = map[string]service.Converter{
-	"bool":      service.ConvertToBool,
-	"int":       service.ConvertToInt,
-	"int8":      service.ConvertToInt8,
-	"int16":     service.ConvertToInt16,
-	"int32":     service.ConvertToInt32,
-	"int64":     service.ConvertToInt64,
-	"uint":      service.ConvertToUint,
-	"uint8":     service.ConvertToUint8,
-	"uint16":    service.ConvertToUint16,
-	"uint32":    service.ConvertToUint32,
-	"uint64":    service.ConvertToUint64,
-	"float32":   service.ConvertToFloat32,
-	"float64":   service.ConvertToFloat64,
-	"string":    service.ConvertToString,
-	"*bool":     service.ConvertToBoolP,
-	"*int":      service.ConvertToIntP,
-	"*int8":     service.ConvertToInt8P,
-	"*int16":    service.ConvertToInt16P,
-	"*int32":    service.ConvertToInt32P,
-	"*int64":    service.ConvertToInt64P,
-	"*uint":     service.ConvertToUintP,
-	"*uint8":    service.ConvertToUint8P,
-	"*uint16":   service.ConvertToUint16P,
-	"*uint32":   service.ConvertToUint32P,
-	"*uint64":   service.ConvertToUint64P,
-	"*float32":  service.ConvertToFloat32P,
-	"*float64":  service.ConvertToFloat64P,
-	"*string":   service.ConvertToStringP,
-	"[]bool":    service.ConvertToBoolSlice,
-	"[]int":     service.ConvertToIntSlice,
-	"[]float64": service.ConvertToFloat64Slice,
-	"[]string":  service.ConvertToStringSlice,
+	"bool":       service.ConvertToBool,
+	"int":        service.ConvertToInt,
+	"int8":       service.ConvertToInt8,
+	"int16":      service.ConvertToInt16,
+	"int32":      service.ConvertToInt32,
+	"int64":      service.ConvertToInt64,
+	"uint":       service.ConvertToUint,
+	"uint8":      service.ConvertToUint8,
+	"uint16":     service.ConvertToUint16,
+	"uint32":     service.ConvertToUint32,
+	"uint64":     service.ConvertToUint64,
+	"float32":    service.ConvertToFloat32,
+	"float64":    service.ConvertToFloat64,
+	"string":     service.ConvertToString,
+	"time.Time":  service.ConvertToTime,
+	"*bool":      service.ConvertToBoolP,
+	"*int":       service.ConvertToIntP,
+	"*int8":      service.ConvertToInt8P,
+	"*int16":     service.ConvertToInt16P,
+	"*int32":     service.ConvertToInt32P,
+	"*int64":     service.ConvertToInt64P,
+	"*uint":      service.ConvertToUintP,
+	"*uint8":     service.ConvertToUint8P,
+	"*uint16":    service.ConvertToUint16P,
+	"*uint32":    service.ConvertToUint32P,
+	"*uint64":    service.ConvertToUint64P,
+	"*float32":   service.ConvertToFloat32P,
+	"*float64":   service.ConvertToFloat64P,
+	"*string":    service.ConvertToStringP,
+	"*time.Time": service.ConvertToTimeP,
+	"[]bool":     service.ConvertToBoolSlice,
+	"[]int":      service.ConvertToIntSlice,
+	"[]float64":  service.ConvertToFloat64Slice,
+	"[]string":   service.ConvertToStringSlice,
 }
 
 func (g *Generator) enum(typ *api.Type) []spec.Parameter {


Test patch:
diff --git a/service/content_test.go b/service/content_test.go
index a906da1..4167f66 100644
--- a/service/content_test.go
+++ b/service/content_test.go
@@ -22,6 +22,7 @@ import (
 	"io"
 	"reflect"
 	"testing"
+	"time"
 
 	"github.com/caicloud/nirvana/definition"
 )
@@ -119,6 +120,7 @@ func TestProducer(t *testing.T) {
 }
 
 func TestConverterFor(t *testing.T) {
+	wantTime, _ := time.Parse(time.RFC3339, "2020-08-25T05:12:18Z")
 	tests := []struct {
 		tpy     reflect.Type
 		data    []string
@@ -126,13 +128,13 @@ func TestConverterFor(t *testing.T) {
 		pointer bool
 	}{
 		{
-			reflect.TypeOf(bool(false)),
+			reflect.TypeOf(false),
 			[]string{"true", "false"},
 			true,
 			false,
 		},
 		{
-			reflect.TypeOf(int(0)),
+			reflect.TypeOf(0),
 			[]string{"1", "2"},
 			1,
 			false,
@@ -192,11 +194,17 @@ func TestConverterFor(t *testing.T) {
 			false,
 		},
 		{
-			reflect.TypeOf(string("")),
+			reflect.TypeOf(""),
 			[]string{"1", "2"},
 			"1",
 			false,
 		},
+		{
+			reflect.TypeOf(time.Time{}),
+			[]string{"2020-08-25T05:12:18Z", "2020-08-25T05:12:18Z"},
+			wantTime,
+			false,
+		},
 		{
 			reflect.TypeOf(new(bool)),
 			[]string{"true", "2"},
@@ -284,6 +292,12 @@ func TestConverterFor(t *testing.T) {
 			"1.2",
 			true,
 		},
+		{
+			reflect.TypeOf(new(time.Time)),
+			[]string{"2020-08-25T05:12:18Z", "2020-08-25T05:12:18Z"},
+			wantTime,
+			true,
+		},
 		{
 			reflect.TypeOf([]bool{}),
 			[]string{"true", "false"},