Thanks to visit codestin.com
Credit goes to github.com

Skip to content

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

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

Merged
merged 5 commits into from
Aug 25, 2022

Conversation

mafredri
Copy link
Member

I took a slightly different approach in this PR than what originally proposed in #2015, it's closer to my proposal in a later comment.

Currently we embedd sql.NullTime for one reason, it gives us database scanner/valuer and means we can pass nullable times to the DB by doing t.NullTime.

We can simplify this though, if it's not needed so that we have plain codersdk.NullTime{Time time.Time; Valid boold}.

Fixes #2015

@mafredri mafredri self-assigned this Aug 18, 2022
@mafredri
Copy link
Member Author

// ping @johnstcn, what do you think about this approach?

@mafredri mafredri force-pushed the mafredri/workspace-build-pointer branch from a7c0920 to 43fbbea Compare August 18, 2022 15:20
@johnstcn
Copy link
Member

// ping @johnstcn, what do you think about this approach?

:chefs-kiss:

@mafredri mafredri marked this pull request as ready for review August 18, 2022 15:39
@mafredri mafredri requested a review from a team as a code owner August 18, 2022 15:39
@mafredri mafredri requested review from johnstcn and Emyrk August 18, 2022 15:40
@@ -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"`
Copy link
Member

Choose a reason for hiding this comment

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

Based on the comment in the attached issue, it's unclear what the use-case for knowing the time is null is that a pointer wouldn't solve.

Is this related to the TypeScript generation?

Copy link
Member Author

@mafredri mafredri Aug 18, 2022

Choose a reason for hiding this comment

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

I think this is a cleaner approach from a usability perspective in Go code. That is to say, the benefit is not in the frontend talking to the backend, it's using these values in the backend or cli.

IMO it'd be nice for us to consistently use NullTime over *time.Time in the API. Things like not being able to take a pointer of a function call &time.Now() are not an issue when you can write codersdk.NewNullTime(time.Now(), true). Likewise when using the type, there's no need for the if t != nil check.

There are work-arounds for making pointer use a bit more convenient, like using coderd/util/ptr, but it's an extra step to using them vs accessing the concrete type directly.

One last point in favor of NullTime, it's safer to use. There's no risk of doing a nil pointer dereference.

Copy link
Member

Choose a reason for hiding this comment

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

FWIW working with pointers makes me sad; I much prefer the NullXXX approach.
Unless we start going down the lines of having a generic Optional[T] type?

Copy link
Member

Choose a reason for hiding this comment

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

I'm hesitant to stray from such strong language primitives. Do we imagine having a NullXXX for every type that could be nullable? This seems odd to me because embedded structs would likely be pointers with omitempty rather than Null<StructName>. Are we promoting this only for built-in types?

Copy link
Member Author

@mafredri mafredri Aug 22, 2022

Choose a reason for hiding this comment

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

I understand why you may be hesitant, but I don't think it will be a problem. If we strive to make the zero value useful in most cases, the need for nullable values will be pretty low. We can also look at codersdk (today), there we only have a couple of primitives (int, string) and time.

      7 *int64
      5 *time.Time
      3 *string
      2 *uuid.UUID

The uuid package already has uuid.NullUUID, so it would make sense to use that one instead. In fact, the occurrence of uuid.NullUUID is much larger in our codebase than that of *uuid.UUID (14 vs 3), yet we have both.

I also foresee that the need for a nullable struct will be very rare (we have none in codersdk, currently), and in those cases it would likely be a custom type for which we can simply implement a custom json marshaler/unmarshaler. It's unfortunate Go never got around to adding omitzero/omitempty to the json package, because many times that would solve the use-case for using pointer-structs (i.e. often the reason is not to detect omission, it's to create a specific type of output).

Copy link
Member

Choose a reason for hiding this comment

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

I appreciate the explanation! I agree that NullUUID is classically used, but that's typically been used for database operations, not the API.

I'll defer here though. You've spent a lot more time thinking about this than I, so I approve!

Copy link
Member Author

@mafredri mafredri Aug 25, 2022

Choose a reason for hiding this comment

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

Leaving this here for future reference, and perhaps an alternative avenue for achieving the same in a generic way. (Did some prototyping on this with @johnstcn some days ago)

type Optional[T any] struct {
	Value T
	Valid bool
}

// Of returns a new Optional of the given value.
func Of[T any](o T) Optional[T] {
	return Optional[T]{
		Value: o,
		Valid: true,
	}
}

// MarshalJSON implements json.Marshaler.
func (o Optional[T]) MarshalJSON() ([]byte, error) {
	// omitted for brevity
}

// UnmarshalJSON implements json.Unmarshaler.
func (o *Optional[T]) UnmarshalJSON(data []byte) error {
	// omitted for brevity
}

type APIRequest struct {
	Time Optional[time.Time] `json:"time"`
}

// other package
func sample() {
	r := codersdk.APIRequest{}
	r.Time = codersdk.Of(time.Now())
	
	if r.Time.Valid { // if we care about null.
		_ = r.Time.Value
	}
	// zero value is ok without null check too
	_ = r.Time.Value.IsZero()
}

The namings Optional, Of, etc are all up in the air, they could be Null and NewFrom instead.

Copy link
Contributor

@jsjoeio jsjoeio left a comment

Choose a reason for hiding this comment

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

✅ FE

I only see an update to the deadline property being made optional now. I imagine this shouldn't have any consequences assuming we have tests for code using that interface.

If there is something else you want reviewed by FE, ping me!

@mafredri mafredri force-pushed the mafredri/workspace-build-pointer branch 2 times, most recently from a77ae97 to 0c66c39 Compare August 25, 2022 12:18
@mafredri
Copy link
Member Author

mafredri commented Aug 25, 2022

✅ FE

I only see an update to the deadline property being made optional now. I imagine this shouldn't have any consequences assuming we have tests for code using that interface.

If there is something else you want reviewed by FE, ping me!

@jsjoeio Your comment made me take a look at the site code, and some updates were indeed required. Please take a look at e390cef (#3552).

I believe none of this was caught due to the function signature of dayjs which accepts basically any type, including null and undefined. Tests also use mock API objects so those would've conformed to the old format.

@mafredri mafredri force-pushed the mafredri/workspace-build-pointer branch from 3a3cb79 to e390cef Compare August 25, 2022 12:26
@mafredri mafredri force-pushed the mafredri/workspace-build-pointer branch from 89576ed to fdb657e Compare August 25, 2022 14:05
Copy link
Contributor

@jsjoeio jsjoeio left a comment

Choose a reason for hiding this comment

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

Thanks for going the extra mile to double-check any side effects on the FE! 👏🏼

@mafredri mafredri merged commit 78a2494 into main Aug 25, 2022
@mafredri mafredri deleted the mafredri/workspace-build-pointer branch August 25, 2022 16:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Codersdk: make workspace_build.deadline a pointer
4 participants