Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement adding notes to timespans in the backend #81

Merged
merged 2 commits into from
Jan 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions model/timespan.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type TimeSpan struct {
OffsetUTC int
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, feel free to do the UI part as well. I'd say the UI could look like this, the notes should be defined as Markdown and then are rendered like this inside the ui.
image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The note can then be edited by clicking it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to include this in the current pr or another?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll see if I can come up with something. Currently this PR breaks the client due to the required notes input field

UserID int `gorm:"type:int REFERENCES users(id) ON DELETE CASCADE"`
Tags []TimeSpanTag
Note string
}

// TimeSpanTag is a tag for a time range
Expand Down
5 changes: 3 additions & 2 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ type RootMutation {
removeDevice(id: Int!): Device @hasRole(role: USER)
removeCurrentDevice: Device! @hasRole(role: USER)

createTimeSpan(start: Time!, end: Time, tags: [InputTimeSpanTag!]): TimeSpan @hasRole(role: USER)
updateTimeSpan(id: Int!, start: Time!, end: Time, tags: [InputTimeSpanTag!], oldStart: Time): TimeSpan @hasRole(role: USER)
createTimeSpan(start: Time!, end: Time, tags: [InputTimeSpanTag!], note: String!): TimeSpan @hasRole(role: USER)
updateTimeSpan(id: Int!, start: Time!, end: Time, tags: [InputTimeSpanTag!], oldStart: Time, note: String!): TimeSpan @hasRole(role: USER)
removeTimeSpan(id: Int!): TimeSpan @hasRole(role: USER)
replaceTimeSpanTags(from: InputTimeSpanTag!, to: InputTimeSpanTag!, opt: InputReplaceOptions!): Boolean @hasRole(role: USER)
copyTimeSpan(id: Int!, start: Time!, end: Time): TimeSpan @hasRole(role: USER)
Expand Down Expand Up @@ -155,6 +155,7 @@ type TimeSpan {
oldStart: Time
tags: [TimeSpanTag!]
# TODO not nullable ^
note: String!
}

type PagedTimeSpans {
Expand Down
4 changes: 3 additions & 1 deletion timespan/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import (
"github.com/traggo/server/model"
)

func timespanToInternal(userID int, start model.Time, end *model.Time, tags []*gqlmodel.InputTimeSpanTag) (model.TimeSpan, error) {
func timespanToInternal(userID int, start model.Time, end *model.Time, tags []*gqlmodel.InputTimeSpanTag, note string) (model.TimeSpan, error) {
_, offset := start.Time().Zone()
span := model.TimeSpan{
StartUserTime: start.OmitTimeZone(),
StartUTC: start.UTC(),
UserID: userID,
Tags: tagsToInternal(tags),
OffsetUTC: offset,
Note: note,
}

if end != nil {
Expand All @@ -39,6 +40,7 @@ func timeSpanToExternal(span model.TimeSpan) *gqlmodel.TimeSpan {
End: nil,
ID: span.ID,
Tags: tagsToExternal(span.Tags),
Note: span.Note,
}
if span.EndUTC != nil && !span.EndUTC.IsZero() {
end := *span.EndUTC
Expand Down
2 changes: 1 addition & 1 deletion timespan/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ func (r *ResolverForTimeSpan) CopyTimeSpan(ctx context.Context, id int, start mo
return nil, fmt.Errorf("time span with id %d does not exist", id)
}

return r.CreateTimeSpan(ctx, start, end, tagsToInputTag(old.Tags))
return r.CreateTimeSpan(ctx, start, end, tagsToInputTag(old.Tags), old.Note)
}
3 changes: 3 additions & 0 deletions timespan/copy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func TestCopy(t *testing.T) {
Tags: []model.TimeSpanTag{
{Key: "test", TimeSpanID: 3},
},
Note: "A special note",
})
defer db.Close()

Expand All @@ -64,6 +65,7 @@ func TestCopy(t *testing.T) {
Tags: []*gqlmodel.TimeSpanTag{
{Key: "test"},
},
Note: "A special note",
}

require.Equal(t, expected, timeSpan)
Expand All @@ -80,6 +82,7 @@ func TestCopy(t *testing.T) {
Tags: []model.TimeSpanTag{
{Key: "test", TimeSpanID: 4},
},
Note: "A special note",
})
}

Expand Down
4 changes: 2 additions & 2 deletions timespan/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
)

// CreateTimeSpan creates a time span
func (r *ResolverForTimeSpan) CreateTimeSpan(ctx context.Context, start model.Time, end *model.Time, tags []*gqlmodel.InputTimeSpanTag) (*gqlmodel.TimeSpan, error) {
timeSpan, err := timespanToInternal(auth.GetUser(ctx).ID, start, end, tags)
func (r *ResolverForTimeSpan) CreateTimeSpan(ctx context.Context, start model.Time, end *model.Time, tags []*gqlmodel.InputTimeSpanTag, note string) (*gqlmodel.TimeSpan, error) {
timeSpan, err := timespanToInternal(auth.GetUser(ctx).ID, start, end, tags, note)
if err != nil {
return nil, err
}
Expand Down
18 changes: 12 additions & 6 deletions timespan/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ func Test_Create_withoutEnd(t *testing.T) {
db.User(5)

resolver := ResolverForTimeSpan{DB: db.DB}
timeSpan, err := resolver.CreateTimeSpan(fake.User(5), test.ModelTime("2019-06-10T18:30:00+02:00"), nil, nil)
timeSpan, err := resolver.CreateTimeSpan(fake.User(5), test.ModelTime("2019-06-10T18:30:00+02:00"), nil, nil, "")

require.Nil(t, err)
expected := &gqlmodel.TimeSpan{
ID: 1,
Start: test.ModelTime("2019-06-10T18:30:00+02:00"),
Tags: []*gqlmodel.TimeSpanTag{},
Note: "",
}
require.Equal(t, expected, timeSpan)
assertTimeSpanCount(t, db, 1)
Expand All @@ -34,6 +35,7 @@ func Test_Create_withoutEnd(t *testing.T) {
StartUTC: test.Time("2019-06-10T16:30:00Z"),
OffsetUTC: 7200,
Tags: []model.TimeSpanTag{},
Note: "",
})
}

Expand All @@ -43,15 +45,17 @@ func Test_Create(t *testing.T) {
db.User(5)

resolver := ResolverForTimeSpan{DB: db.DB}
note := "A note describing the timespan"
timeSpan, err := resolver.CreateTimeSpan(fake.User(5), test.ModelTime("2019-06-10T18:30:00+02:00"),
test.ModelTimeP("2019-06-10T19:30:00+02:00"), nil)
test.ModelTimeP("2019-06-10T19:30:00+02:00"), nil, note)
log.Debug().Msg("oops")
require.Nil(t, err)
expected := &gqlmodel.TimeSpan{
ID: 1,
Start: test.ModelTime("2019-06-10T18:30:00+02:00"),
End: test.ModelTimeP("2019-06-10T19:30:00+02:00"),
Tags: []*gqlmodel.TimeSpanTag{},
Note: note,
}
require.Equal(t, expected, timeSpan)
assertTimeSpanCount(t, db, 1)
Expand All @@ -64,6 +68,7 @@ func Test_Create(t *testing.T) {
EndUTC: test.TimeP("2019-06-10T17:30:00Z"),
OffsetUTC: 7200,
Tags: []model.TimeSpanTag{},
Note: note,
})
}

Expand All @@ -73,7 +78,7 @@ func Test_Create_fail_endBeforeStart(t *testing.T) {

resolver := ResolverForTimeSpan{DB: db.DB}
timeSpan, err := resolver.CreateTimeSpan(fake.User(5), test.ModelTime("2019-06-10T18:30:00+02:00"),
test.ModelTimeP("2019-06-10T17:30:00+02:00"), nil)
test.ModelTimeP("2019-06-10T17:30:00+02:00"), nil, "")
require.Nil(t, timeSpan)
require.EqualError(t, err, "start must be before end")
assertTimeSpanCount(t, db, 0)
Expand All @@ -85,7 +90,7 @@ func Test_Create_fail_notExistingTag(t *testing.T) {

resolver := ResolverForTimeSpan{DB: db.DB}
timeSpan, err := resolver.CreateTimeSpan(fake.User(5), test.ModelTime("2019-06-10T18:30:00+02:00"),
test.ModelTimeP("2019-06-10T18:35:00+02:00"), []*gqlmodel.InputTimeSpanTag{{Key: "test"}})
test.ModelTimeP("2019-06-10T18:35:00+02:00"), []*gqlmodel.InputTimeSpanTag{{Key: "test"}}, "")
require.Nil(t, timeSpan)
require.EqualError(t, err, "tag 'test' does not exist")
assertTimeSpanCount(t, db, 0)
Expand All @@ -99,7 +104,7 @@ func Test_Create_withTag(t *testing.T) {

resolver := ResolverForTimeSpan{DB: db.DB}
timeSpan, err := resolver.CreateTimeSpan(fake.User(5), test.ModelTime("2019-06-10T18:30:00+02:00"),
test.ModelTimeP("2019-06-10T19:30:00+02:00"), []*gqlmodel.InputTimeSpanTag{{Key: "test"}})
test.ModelTimeP("2019-06-10T19:30:00+02:00"), []*gqlmodel.InputTimeSpanTag{{Key: "test"}}, "")
require.NotNil(t, timeSpan)
require.NoError(t, err)
assertTimeSpanCount(t, db, 1)
Expand All @@ -114,6 +119,7 @@ func Test_Create_withTag(t *testing.T) {
Tags: []model.TimeSpanTag{
{Key: "test", TimeSpanID: 1},
},
Note: "",
})
}

Expand All @@ -125,7 +131,7 @@ func Test_Create_fail_tagAddedMultipleTimes(t *testing.T) {

resolver := ResolverForTimeSpan{DB: db.DB}
timeSpan, err := resolver.CreateTimeSpan(fake.User(5), test.ModelTime("2019-06-10T18:30:00+02:00"),
test.ModelTimeP("2019-06-10T18:35:00+02:00"), []*gqlmodel.InputTimeSpanTag{{Key: "test"}, {Key: "test"}})
test.ModelTimeP("2019-06-10T18:35:00+02:00"), []*gqlmodel.InputTimeSpanTag{{Key: "test"}, {Key: "test"}}, "")
require.Nil(t, timeSpan)
require.EqualError(t, err, "tag 'test' is present multiple times")
assertTimeSpanCount(t, db, 0)
Expand Down
4 changes: 2 additions & 2 deletions timespan/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
)

// UpdateTimeSpan update a time span
func (r *ResolverForTimeSpan) UpdateTimeSpan(ctx context.Context, id int, start model.Time, end *model.Time, tags []*gqlmodel.InputTimeSpanTag, oldStart *model.Time) (*gqlmodel.TimeSpan, error) {
timeSpan, err := timespanToInternal(auth.GetUser(ctx).ID, start, end, tags)
func (r *ResolverForTimeSpan) UpdateTimeSpan(ctx context.Context, id int, start model.Time, end *model.Time, tags []*gqlmodel.InputTimeSpanTag, oldStart *model.Time, note string) (*gqlmodel.TimeSpan, error) {
timeSpan, err := timespanToInternal(auth.GetUser(ctx).ID, start, end, tags, note)
if err != nil {
return nil, err
}
Expand Down
25 changes: 17 additions & 8 deletions timespan/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,27 @@ func Test_Update_withoutEnd(t *testing.T) {
db := test.InMemoryDB(t)
defer db.Close()
db.User(3)
note := "A note"
db.Create(&model.TimeSpan{
ID: 1,
UserID: 3,
StartUserTime: test.Time("2019-06-10T18:30:00Z"),
StartUTC: test.Time("2019-06-10T16:30:00Z"),
OffsetUTC: 7200,
Tags: []model.TimeSpanTag{},
Note: note,
})

resolver := ResolverForTimeSpan{DB: db.DB}
timeSpan, err := resolver.UpdateTimeSpan(fake.User(3), 1, test.ModelTime("2019-06-10T19:30:00+02:00"), nil, nil, nil)
timeSpan, err := resolver.UpdateTimeSpan(fake.User(3), 1, test.ModelTime("2019-06-10T19:30:00+02:00"), nil, nil, nil, "")

require.Nil(t, err)
expected := &gqlmodel.TimeSpan{
ID: 1,
Start: test.ModelTime("2019-06-10T19:30:00+02:00"),
OldStart: test.ModelTimeP("2019-06-10T18:30:00+02:00"),
Tags: []*gqlmodel.TimeSpanTag{},
Note: "",
}
require.Equal(t, expected, timeSpan)
assertTimeSpanCount(t, db, 1)
Expand All @@ -43,25 +46,29 @@ func Test_Update_withoutEnd(t *testing.T) {
StartUTC: test.Time("2019-06-10T17:30:00Z"),
OffsetUTC: 7200,
Tags: []model.TimeSpanTag{},
Note: "",
})
}

func Test_Update(t *testing.T) {
db := test.InMemoryDB(t)
defer db.Close()
db.User(5)
note := "A note"
db.Create(&model.TimeSpan{
ID: 3,
UserID: 5,
StartUserTime: test.Time("2019-06-10T18:30:00Z"),
StartUTC: test.Time("2019-06-10T16:30:00Z"),
OffsetUTC: 7200,
Tags: []model.TimeSpanTag{},
Note: note,
})

note = "An upated note"
resolver := ResolverForTimeSpan{DB: db.DB}
timeSpan, err := resolver.UpdateTimeSpan(fake.User(5), 3, test.ModelTime("2019-06-10T18:30:00+02:00"),
test.ModelTimeP("2019-06-10T20:30:00+02:00"), nil, nil)
test.ModelTimeP("2019-06-10T20:30:00+02:00"), nil, nil, note)
log.Debug().Msg("oops")
require.Nil(t, err)
expected := &gqlmodel.TimeSpan{
Expand All @@ -70,6 +77,7 @@ func Test_Update(t *testing.T) {
OldStart: test.ModelTimeP("2019-06-10T18:30:00+02:00"),
End: test.ModelTimeP("2019-06-10T20:30:00+02:00"),
Tags: []*gqlmodel.TimeSpanTag{},
Note: note,
}
require.Equal(t, expected, timeSpan)
assertTimeSpanCount(t, db, 1)
Expand All @@ -82,6 +90,7 @@ func Test_Update(t *testing.T) {
EndUTC: test.TimeP("2019-06-10T18:30:00Z"),
OffsetUTC: 7200,
Tags: []model.TimeSpanTag{},
Note: note,
})
}

Expand All @@ -102,7 +111,7 @@ func Test_Update_fail_endBeforeStart(t *testing.T) {

resolver := ResolverForTimeSpan{DB: db.DB}
timeSpan, err := resolver.UpdateTimeSpan(fake.User(5), 3, test.ModelTime("2019-06-10T18:30:00+02:00"),
test.ModelTimeP("2019-06-10T17:30:00+02:00"), nil, nil)
test.ModelTimeP("2019-06-10T17:30:00+02:00"), nil, nil, "")
require.Nil(t, timeSpan)
require.EqualError(t, err, "start must be before end")
assertTimeSpanCount(t, db, 1)
Expand All @@ -125,7 +134,7 @@ func Test_Update_fail_notExistingTag(t *testing.T) {

resolver := ResolverForTimeSpan{DB: db.DB}
timeSpan, err := resolver.UpdateTimeSpan(fake.User(5), 3, test.ModelTime("2019-06-10T18:30:00+02:00"),
test.ModelTimeP("2019-06-10T18:35:00+02:00"), []*gqlmodel.InputTimeSpanTag{{Key: "test"}}, nil)
test.ModelTimeP("2019-06-10T18:35:00+02:00"), []*gqlmodel.InputTimeSpanTag{{Key: "test"}}, nil, "nil")
require.Nil(t, timeSpan)
require.EqualError(t, err, "tag 'test' does not exist")
assertTimeSpanCount(t, db, 1)
Expand Down Expand Up @@ -159,7 +168,7 @@ func Test_Update_withTag(t *testing.T) {

resolver := ResolverForTimeSpan{DB: db.DB}
timeSpan, err := resolver.UpdateTimeSpan(fake.User(5), 3, test.ModelTime("2019-06-10T18:30:00+02:00"),
test.ModelTimeP("2019-06-10T19:30:00+02:00"), []*gqlmodel.InputTimeSpanTag{{Key: "test"}}, nil)
test.ModelTimeP("2019-06-10T19:30:00+02:00"), []*gqlmodel.InputTimeSpanTag{{Key: "test"}}, nil, "")
require.NotNil(t, timeSpan)
require.NoError(t, err)
assertTimeSpanCount(t, db, 1)
Expand Down Expand Up @@ -195,7 +204,7 @@ func Test_Update_fail_tagAddedMultipleTimes(t *testing.T) {

resolver := ResolverForTimeSpan{DB: db.DB}
timeSpan, err := resolver.UpdateTimeSpan(fake.User(5), 3, test.ModelTime("2019-06-10T18:30:00+02:00"),
test.ModelTimeP("2019-06-10T18:35:00+02:00"), []*gqlmodel.InputTimeSpanTag{{Key: "test"}, {Key: "test"}}, nil)
test.ModelTimeP("2019-06-10T18:35:00+02:00"), []*gqlmodel.InputTimeSpanTag{{Key: "test"}, {Key: "test"}}, nil, "")
require.Nil(t, timeSpan)
require.EqualError(t, err, "tag 'test' is present multiple times")
assertTimeSpanCount(t, db, 1)
Expand All @@ -209,7 +218,7 @@ func Test_Update_fail_notExisting(t *testing.T) {

resolver := ResolverForTimeSpan{DB: db.DB}
timeSpan, err := resolver.UpdateTimeSpan(fake.User(5), 3, test.ModelTime("2019-06-10T18:30:00+02:00"),
test.ModelTimeP("2019-06-10T18:35:00+02:00"), []*gqlmodel.InputTimeSpanTag{{Key: "test"}, {Key: "test"}}, nil)
test.ModelTimeP("2019-06-10T18:35:00+02:00"), []*gqlmodel.InputTimeSpanTag{{Key: "test"}, {Key: "test"}}, nil, "")
require.Nil(t, timeSpan)
require.EqualError(t, err, "time span with id 3 does not exist")
assertTimeSpanCount(t, db, 0)
Expand All @@ -235,7 +244,7 @@ func Test_Update_fail_noPermission(t *testing.T) {

resolver := ResolverForTimeSpan{DB: db.DB}
timeSpan, err := resolver.UpdateTimeSpan(fake.User(2), 3, test.ModelTime("2019-06-10T18:30:00+02:00"),
test.ModelTimeP("2019-06-10T18:35:00+02:00"), []*gqlmodel.InputTimeSpanTag{{Key: "test"}, {Key: "test"}}, nil)
test.ModelTimeP("2019-06-10T18:35:00+02:00"), []*gqlmodel.InputTimeSpanTag{{Key: "test"}, {Key: "test"}}, nil, "")
require.Nil(t, timeSpan)
require.EqualError(t, err, "time span with id 3 does not exist")
assertTimeSpanCount(t, db, 1)
Expand Down
Loading