feat: Add codersdk.NullTime, change workspace build deadline (#3552)

Fixes #2015

Co-authored-by: Joe Previte <jjprevite@gmail.com>
This commit is contained in:
Mathias Fredriksson
2022-08-25 19:10:42 +03:00
committed by GitHub
co-authored by Joe Previte
parent a21a6d2f4a
commit 78a24941fe
18 changed files with 271 additions and 63 deletions
+59
View File
@@ -0,0 +1,59 @@
package codersdk
import (
"bytes"
"database/sql"
"encoding/json"
"time"
"golang.org/x/xerrors"
)
var nullBytes = []byte("null")
// NullTime represents a nullable time.Time.
// @typescript-ignore NullTime
type NullTime struct {
sql.NullTime
}
// NewNullTime returns a new NullTime with the given time.Time.
func NewNullTime(t time.Time, valid bool) NullTime {
return NullTime{
NullTime: sql.NullTime{
Time: t,
Valid: valid,
},
}
}
// MarshalJSON implements json.Marshaler.
func (t NullTime) MarshalJSON() ([]byte, error) {
if !t.Valid {
return []byte("null"), nil
}
b, err := t.Time.MarshalJSON()
if err != nil {
return nil, xerrors.Errorf("codersdk.NullTime: json encode failed: %w", err)
}
return b, nil
}
// UnmarshalJSON implements json.Unmarshaler.
func (t *NullTime) UnmarshalJSON(data []byte) error {
t.Valid = false
if bytes.Equal(data, nullBytes) {
return nil
}
err := json.Unmarshal(data, &t.Time)
if err != nil {
return xerrors.Errorf("codersdk.NullTime: json decode failed: %w", err)
}
t.Valid = true
return nil
}
// IsZero return true if the time is null or zero.
func (t NullTime) IsZero() bool {
return !t.Valid || t.Time.IsZero()
}
+156
View File
@@ -0,0 +1,156 @@
package codersdk_test
import (
"database/sql"
"encoding/json"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/coder/coder/codersdk"
)
func TestNullTime_MarshalJSON(t *testing.T) {
t.Parallel()
t1, err := time.Parse(time.RFC3339, "2022-08-18T00:00:00Z")
require.NoError(t, err)
bt1, err := json.Marshal(t1)
require.NoError(t, err)
tests := []struct {
name string
input sql.NullTime
want string
}{
{
name: "valid zero",
input: sql.NullTime{Valid: true},
want: `"0001-01-01T00:00:00Z"`,
},
{
name: "invalid zero",
input: sql.NullTime{Valid: false},
want: "null",
},
{
name: "valid time",
input: sql.NullTime{Time: t1, Valid: true},
want: string(bt1),
},
{
name: "null time",
input: sql.NullTime{Time: t1, Valid: false},
want: "null",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
tr := codersdk.NewNullTime(tt.input.Time, tt.input.Valid)
got, err := tr.MarshalJSON()
require.NoError(t, err)
require.Equal(t, tt.want, string(got))
})
}
}
func TestNullTime_UnmarshalJSON(t *testing.T) {
t.Parallel()
t1, err := time.Parse(time.RFC3339, "2022-08-18T00:00:00Z")
require.NoError(t, err)
bt1, err := json.Marshal(t1)
require.NoError(t, err)
type request struct {
Time codersdk.NullTime `json:"time"`
}
tests := []struct {
name string
data string
want codersdk.NullTime
wantErr bool
}{
{
name: "null",
data: `{"time": null}`,
want: codersdk.NullTime{},
},
{
name: "empty",
data: `{}`,
want: codersdk.NullTime{},
},
{
name: "empty string",
data: `{"time": ""}`,
wantErr: true,
},
{
name: "valid time",
data: fmt.Sprintf(`{"time": %s}`, bt1),
want: codersdk.NewNullTime(t1, true),
},
{
name: "invalid time",
data: fmt.Sprintf(`{"time": %q}`, `2022-08-18T00:00:00`),
wantErr: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
var req request
err := json.Unmarshal([]byte(tt.data), &req)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tt.want, req.Time)
})
}
}
func TestNullTime_IsZero(t *testing.T) {
t.Parallel()
tests := []struct {
name string
input sql.NullTime
want bool
}{
{
name: "zero",
input: sql.NullTime{},
want: true,
},
{
name: "not zero",
input: sql.NullTime{Time: time.Now(), Valid: true},
want: false,
},
{
name: "null is zero",
input: sql.NullTime{Time: time.Now(), Valid: false},
want: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
tr := codersdk.NullTime{NullTime: tt.input}
require.Equal(t, tt.want, tr.IsZero())
})
}
}
+1 -1
View File
@@ -50,7 +50,7 @@ type WorkspaceBuild struct {
InitiatorID uuid.UUID `json:"initiator_id"`
InitiatorUsername string `json:"initiator_name"`
Job ProvisionerJob `json:"job"`
Deadline time.Time `json:"deadline"`
Deadline NullTime `json:"deadline,omitempty"`
Reason BuildReason `db:"reason" json:"reason"`
}