Repository: deepmap/oapi-codegen
Pull request #285 (2021-01-21 13:14:47)

Implement marshalling of Date and aliased time parameters
Currently, marshalling of date parameters fails with an error similar to `error formatting 'date-from': unsupported type time.Time`.

To resolve this, I added marshalling of types.Date to `marshalTimeValue`, similar to what was already done for time.Time. Furthermore, I added support for aliasing of both types, which required some reflection magic.


Patch:
diff --git a/pkg/runtime/styleparam.go b/pkg/runtime/styleparam.go
index 84f2f31..30c285a 100644
--- a/pkg/runtime/styleparam.go
+++ b/pkg/runtime/styleparam.go
@@ -23,6 +23,8 @@ import (
 	"time"
 
 	"github.com/pkg/errors"
+
+	"github.com/deepmap/oapi-codegen/pkg/types"
 )
 
 // Given an input value, such as a primitive type, array or object, turn it
@@ -134,22 +136,29 @@ func sortedKeys(strMap map[string]string) []string {
 	return keys
 }
 
-// This is a special case. The struct may be a time, in which case, marshal
-// it in RFC3339 format.
-func marshalTimeValue(value interface{}) (string, bool) {
-	if timeVal, ok := value.(*time.Time); ok {
+// This is a special case. The struct may be a date or time, in
+// which case, marshal it in correct format.
+func marshalDateTimeValue(value interface{}) (string, bool) {
+	v := reflect.Indirect(reflect.ValueOf(value))
+	t := v.Type()
+
+	if t.ConvertibleTo(reflect.TypeOf(time.Time{})) {
+		tt := v.Convert(reflect.TypeOf(time.Time{}))
+		timeVal := tt.Interface().(time.Time)
 		return timeVal.Format(time.RFC3339Nano), true
 	}
 
-	if timeVal, ok := value.(time.Time); ok {
-		return timeVal.Format(time.RFC3339Nano), true
+	if t.ConvertibleTo(reflect.TypeOf(types.Date{})) {
+		d := v.Convert(reflect.TypeOf(types.Date{}))
+		dateVal := d.Interface().(types.Date)
+		return dateVal.Format(types.DateFormat), true
 	}
 
 	return "", false
 }
 
 func styleStruct(style string, explode bool, paramName string, value interface{}) (string, error) {
-	if timeVal, ok := marshalTimeValue(value); ok {
+	if timeVal, ok := marshalDateTimeValue(value); ok {
 		styledVal, err := stylePrimitive(style, explode, paramName, url.QueryEscape(timeVal))
 		if err != nil {
 			return "", errors.Wrap(err, "failed to style time")


Test patch:
diff --git a/pkg/runtime/styleparam_test.go b/pkg/runtime/styleparam_test.go
index fcf4eef..de3791a 100644
--- a/pkg/runtime/styleparam_test.go
+++ b/pkg/runtime/styleparam_test.go
@@ -18,6 +18,8 @@ import (
 	"time"
 
 	"github.com/stretchr/testify/assert"
+
+	"github.com/deepmap/oapi-codegen/pkg/types"
 )
 
 func TestStyleParam(t *testing.T) {
@@ -34,7 +36,13 @@ func TestStyleParam(t *testing.T) {
 	dict := map[string]interface{}{}
 	dict["firstName"] = "Alex"
 	dict["role"] = "admin"
-	timestamp, _ := time.Parse(time.RFC3339, "2020-01-01T22:00:00+02:00")
+
+	type AliasedTime time.Time
+	ti, _ := time.Parse(time.RFC3339, "2020-01-01T22:00:00+02:00")
+	timestamp := AliasedTime(ti)
+
+	type AliasedDate types.Date
+	date := AliasedDate{time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)}
 
 	// ---------------------------- Simple Style -------------------------------
 
@@ -86,6 +94,22 @@ func TestStyleParam(t *testing.T) {
 	assert.NoError(t, err)
 	assert.EqualValues(t, "2020-01-01T22%3A00%3A00%2B02%3A00", result)
 
+	result, err = StyleParam("simple", false, "id", date)
+	assert.NoError(t, err)
+	assert.EqualValues(t, "2020-01-01", result)
+
+	result, err = StyleParam("simple", true, "id", date)
+	assert.NoError(t, err)
+	assert.EqualValues(t, "2020-01-01", result)
+
+	result, err = StyleParam("simple", false, "id", &date)
+	assert.NoError(t, err)
+	assert.EqualValues(t, "2020-01-01", result)
+
+	result, err = StyleParam("simple", true, "id", &date)
+	assert.NoError(t, err)
+	assert.EqualValues(t, "2020-01-01", result)
+
 	// ----------------------------- Label Style -------------------------------
 
 	result, err = StyleParam("label", false, "id", primitive)
@@ -136,6 +160,22 @@ func TestStyleParam(t *testing.T) {
 	assert.NoError(t, err)
 	assert.EqualValues(t, ".2020-01-01T22%3A00%3A00%2B02%3A00", result)
 
+	result, err = StyleParam("label", false, "id", date)
+	assert.NoError(t, err)
+	assert.EqualValues(t, ".2020-01-01", result)
+
+	result, err = StyleParam("label", true, "id", date)
+	assert.NoError(t, err)
+	assert.EqualValues(t, ".2020-01-01", result)
+
+	result, err = StyleParam("label", false, "id", &date)
+	assert.NoError(t, err)
+	assert.EqualValues(t, ".2020-01-01", result)
+
+	result, err = StyleParam("label", true, "id", &date)
+	assert.NoError(t, err)
+	assert.EqualValues(t, ".2020-01-01", result)
+
 	// ----------------------------- Matrix Style ------------------------------
 
 	result, err = StyleParam("matrix", false, "id", primitive)
@@ -186,6 +226,22 @@ func TestStyleParam(t *testing.T) {
 	assert.NoError(t, err)
 	assert.EqualValues(t, ";id=2020-01-01T22%3A00%3A00%2B02%3A00", result)
 
+	result, err = StyleParam("matrix", false, "id", date)
+	assert.NoError(t, err)
+	assert.EqualValues(t, ";id=2020-01-01", result)
+
+	result, err = StyleParam("matrix", true, "id", date)
+	assert.NoError(t, err)
+	assert.EqualValues(t, ";id=2020-01-01", result)
+
+	result, err = StyleParam("matrix", false, "id", &date)
+	assert.NoError(t, err)
+	assert.EqualValues(t, ";id=2020-01-01", result)
+
+	result, err = StyleParam("matrix", true, "id", &date)
+	assert.NoError(t, err)
+	assert.EqualValues(t, ";id=2020-01-01", result)
+
 	// ------------------------------ Form Style -------------------------------
 	result, err = StyleParam("form", false, "id", primitive)
 	assert.NoError(t, err)
@@ -235,6 +291,22 @@ func TestStyleParam(t *testing.T) {
 	assert.NoError(t, err)
 	assert.EqualValues(t, "id=2020-01-01T22%3A00%3A00%2B02%3A00", result)
 
+	result, err = StyleParam("form", false, "id", date)
+	assert.NoError(t, err)
+	assert.EqualValues(t, "id=2020-01-01", result)
+
+	result, err = StyleParam("form", true, "id", date)
+	assert.NoError(t, err)
+	assert.EqualValues(t, "id=2020-01-01", result)
+
+	result, err = StyleParam("form", false, "id", &date)
+	assert.NoError(t, err)
+	assert.EqualValues(t, "id=2020-01-01", result)
+
+	result, err = StyleParam("form", true, "id", &date)
+	assert.NoError(t, err)
+	assert.EqualValues(t, "id=2020-01-01", result)
+
 	// ------------------------  spaceDelimited Style --------------------------
 
 	result, err = StyleParam("spaceDelimited", false, "id", primitive)
@@ -275,6 +347,18 @@ func TestStyleParam(t *testing.T) {
 	result, err = StyleParam("spaceDelimited", true, "id", &timestamp)
 	assert.Error(t, err)
 
+	result, err = StyleParam("spaceDelimited", false, "id", date)
+	assert.Error(t, err)
+
+	result, err = StyleParam("spaceDelimited", true, "id", date)
+	assert.Error(t, err)
+
+	result, err = StyleParam("spaceDelimited", false, "id", &date)
+	assert.Error(t, err)
+
+	result, err = StyleParam("spaceDelimited", true, "id", &date)
+	assert.Error(t, err)
+
 	// -------------------------  pipeDelimited Style --------------------------
 
 	result, err = StyleParam("pipeDelimited", false, "id", primitive)
@@ -315,6 +399,18 @@ func TestStyleParam(t *testing.T) {
 	result, err = StyleParam("pipeDelimited", true, "id", &timestamp)
 	assert.Error(t, err)
 
+	result, err = StyleParam("pipeDelimited", false, "id", date)
+	assert.Error(t, err)
+
+	result, err = StyleParam("pipeDelimited", true, "id", date)
+	assert.Error(t, err)
+
+	result, err = StyleParam("pipeDelimited", false, "id", &date)
+	assert.Error(t, err)
+
+	result, err = StyleParam("pipeDelimited", true, "id", &date)
+	assert.Error(t, err)
+
 	// ---------------------------  deepObject Style ---------------------------
 	result, err = StyleParam("deepObject", false, "id", primitive)
 	assert.Error(t, err)
@@ -352,6 +448,18 @@ func TestStyleParam(t *testing.T) {
 	result, err = StyleParam("deepObject", true, "id", &timestamp)
 	assert.Error(t, err)
 
+	result, err = StyleParam("deepObject", false, "id", date)
+	assert.Error(t, err)
+
+	result, err = StyleParam("deepObject", true, "id", date)
+	assert.Error(t, err)
+
+	result, err = StyleParam("deepObject", false, "id", &date)
+	assert.Error(t, err)
+
+	result, err = StyleParam("deepObject", true, "id", &date)
+	assert.Error(t, err)
+
 	// Misc tests
 	// Test type aliases
 	type StrType string