Commit 096ed098 authored by Yugesh Kumar's avatar Yugesh Kumar Committed by Timo Furrer
Browse files

feat: use Nullable[int64] for label priority

Changelog: Improvements
parent 5e4cad84
Loading
Loading
Loading
Loading
+9 −9
Original line number Diff line number Diff line
@@ -126,7 +126,7 @@ type CreateGroupLabelOptions struct {
	Name        *string         `url:"name,omitempty" json:"name,omitempty"`
	Color       *string         `url:"color,omitempty" json:"color,omitempty"`
	Description *string         `url:"description,omitempty" json:"description,omitempty"`
	Priority    *int64  `url:"priority,omitempty" json:"priority,omitempty"`
	Priority    Nullable[int64] `url:"priority,omitempty" json:"priority,omitempty"`
}

// CreateGroupLabel creates a new label for given group with given name and
@@ -199,7 +199,7 @@ type UpdateGroupLabelOptions struct {
	NewName     *string         `url:"new_name,omitempty" json:"new_name,omitempty"`
	Color       *string         `url:"color,omitempty" json:"color,omitempty"`
	Description *string         `url:"description,omitempty" json:"description,omitempty"`
	Priority    *int64  `url:"priority,omitempty" json:"priority,omitempty"`
	Priority    Nullable[int64] `url:"priority,omitempty" json:"priority,omitempty"`
}

// UpdateGroupLabel updates an existing label with new name or now color. At least
+3 −3
Original line number Diff line number Diff line
@@ -118,7 +118,7 @@ func TestSubscribeToGroupLabel(t *testing.T) {
	if err != nil {
		t.Fatal(err)
	}
	want := &GroupLabel{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}
	want := &GroupLabel{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true, Priority: NewNullNullable[int64]()}
	if !reflect.DeepEqual(want, label) {
		t.Errorf("GroupLabels.SubscribeToGroupLabel returned %+v, want %+v", label, want)
	}
@@ -157,7 +157,7 @@ func TestListGroupLabels(t *testing.T) {
	if err != nil {
		t.Log(err.Error() == "invalid ID type 1.1, the ID must be an int64 or a string")
	}
	want := []*GroupLabel{{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}}
	want := []*GroupLabel{{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true, Priority: NewNullNullable[int64]()}}
	if !reflect.DeepEqual(want, label) {
		t.Errorf("GroupLabels.ListGroupLabels returned %+v, want %+v", label, want)
	}
@@ -177,7 +177,7 @@ func TestGetGroupLabel(t *testing.T) {
		t.Log(err)
	}

	want := &GroupLabel{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}
	want := &GroupLabel{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true, Priority: NewNullNullable[int64]()}
	if !reflect.DeepEqual(want, label) {
		t.Errorf("GroupLabels.GetGroupLabel returned %+v, want %+v", label, want)
	}
+20 −20
Original line number Diff line number Diff line
@@ -58,7 +58,7 @@ type Label struct {
	ClosedIssuesCount      int64           `json:"closed_issues_count"`
	OpenMergeRequestsCount int64           `json:"open_merge_requests_count"`
	Subscribed             bool            `json:"subscribed"`
	Priority               int64  `json:"priority"`
	Priority               Nullable[int64] `json:"priority"`
	IsProjectLabel         bool            `json:"is_project_label"`
}

@@ -155,7 +155,7 @@ type CreateLabelOptions struct {
	Name        *string         `url:"name,omitempty" json:"name,omitempty"`
	Color       *string         `url:"color,omitempty" json:"color,omitempty"`
	Description *string         `url:"description,omitempty" json:"description,omitempty"`
	Priority    *int64  `url:"priority,omitempty" json:"priority,omitempty"`
	Priority    Nullable[int64] `url:"priority,omitempty" json:"priority,omitempty"`
}

// CreateLabel creates a new label for given repository with given name and
@@ -224,7 +224,7 @@ type UpdateLabelOptions struct {
	NewName     *string         `url:"new_name,omitempty" json:"new_name,omitempty"`
	Color       *string         `url:"color,omitempty" json:"color,omitempty"`
	Description *string         `url:"description,omitempty" json:"description,omitempty"`
	Priority    *int64  `url:"priority,omitempty" json:"priority,omitempty"`
	Priority    Nullable[int64] `url:"priority,omitempty" json:"priority,omitempty"`
}

// UpdateLabel updates an existing label with new name or now color. At least
+7 −7
Original line number Diff line number Diff line
@@ -36,13 +36,13 @@ func TestCreateLabel(t *testing.T) {
	l := &CreateLabelOptions{
		Name:     Ptr("MyLabel"),
		Color:    Ptr("#11FF22"),
		Priority: Ptr(int64(2)),
		Priority: NewNullableWithValue(int64(2)),
	}
	label, _, err := client.Labels.CreateLabel("1", l)
	if err != nil {
		t.Fatal(err)
	}
	want := &Label{ID: 1, Name: "MyLabel", Color: "#11FF22", Priority: 2}
	want := &Label{ID: 1, Name: "MyLabel", Color: "#11FF22", Priority: NewNullableWithValue(int64(2))}
	if !reflect.DeepEqual(want, label) {
		t.Errorf("Labels.CreateLabel returned %+v, want %+v", label, want)
	}
@@ -96,7 +96,7 @@ func TestUpdateLabel(t *testing.T) {
		NewName:     Ptr("New Label"),
		Color:       Ptr("#11FF23"),
		Description: Ptr("This is updated label"),
		Priority:    Ptr(int64(42)),
		Priority:    NewNullableWithValue(int64(42)),
	}

	label, resp, err := client.Labels.UpdateLabel("1", "MyLabel", l)
@@ -108,7 +108,7 @@ func TestUpdateLabel(t *testing.T) {
		t.Fatal(err)
	}

	want := &Label{ID: 1, Name: "New Label", Color: "#11FF23", Description: "This is updated label", Priority: 42}
	want := &Label{ID: 1, Name: "New Label", Color: "#11FF23", Description: "This is updated label", Priority: NewNullableWithValue(int64(42))}

	if !reflect.DeepEqual(want, label) {
		t.Errorf("Labels.UpdateLabel returned %+v, want %+v", label, want)
@@ -128,7 +128,7 @@ func TestSubscribeToLabel(t *testing.T) {
	if err != nil {
		t.Fatal(err)
	}
	want := &Label{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}
	want := &Label{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true, Priority: NewNullNullable[int64]()}
	if !reflect.DeepEqual(want, label) {
		t.Errorf("Labels.SubscribeToLabel returned %+v, want %+v", label, want)
	}
@@ -167,7 +167,7 @@ func TestListLabels(t *testing.T) {
	if err != nil {
		t.Log(err.Error() == "invalid ID type 1.1, the ID must be an int or a string")
	}
	want := []*Label{{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}}
	want := []*Label{{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true, Priority: NewNullNullable[int64]()}}
	if !reflect.DeepEqual(want, label) {
		t.Errorf("Labels.ListLabels returned %+v, want %+v", label, want)
	}
@@ -186,7 +186,7 @@ func TestGetLabel(t *testing.T) {
	if err != nil {
		t.Log(err)
	}
	want := &Label{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}
	want := &Label{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true, Priority: NewNullNullable[int64]()}
	if !reflect.DeepEqual(want, label) {
		t.Errorf("Labels.GetLabel returned %+v, want %+v", label, want)
	}

nullable.go

0 → 100644
+135 −0
Original line number Diff line number Diff line
//
// Copyright 2024 oapi-codegen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// This code was adapted from https://github.com/oapi-codegen/nullable
//

package gitlab

import (
	"bytes"
	"encoding/json"
	"errors"
)

// Nullable is a generic type, which implements a field that can be one of three states:
//
// - field is not set in the request
// - field is explicitly set to `null` in the request
// - field is explicitly set to a valid value in the request
//
// Nullable is intended to be used with JSON marshaling and unmarshaling.
//
// Internal implementation details:
//
// - map[true]T means a value was provided
// - map[false]T means an explicit null was provided
// - nil or zero map means the field was not provided
//
// If the field is expected to be optional, add the `omitempty` JSON tags. Do NOT use `*Nullable`!
//
// Adapted from https://github.com/golang/go/issues/64515#issuecomment-1841057182
type Nullable[T any] map[bool]T

// NewNullableWithValue is a convenience helper to allow constructing a `Nullable` with a given value, for instance to construct a field inside a struct, without introducing an intermediate variable
func NewNullableWithValue[T any](t T) Nullable[T] {
	var n Nullable[T]
	n.Set(t)
	return n
}

// NewNullNullable is a convenience helper to allow constructing a `Nullable` with an explicit `null`, for instance to construct a field inside a struct, without introducing an intermediate variable
func NewNullNullable[T any]() Nullable[T] {
	var n Nullable[T]
	n.SetNull()
	return n
}

// Get retrieves the underlying value, if present, and returns an error if the value was not present
func (t Nullable[T]) Get() (T, error) {
	var empty T
	if t.IsNull() {
		return empty, errors.New("value is null")
	}
	if !t.IsSpecified() {
		return empty, errors.New("value is not specified")
	}
	return t[true], nil
}

// MustGet retrieves the underlying value, if present, and panics if the value was not present
func (t Nullable[T]) MustGet() T {
	v, err := t.Get()
	if err != nil {
		panic(err)
	}
	return v
}

// Set sets the underlying value to a given value
func (t *Nullable[T]) Set(value T) {
	*t = map[bool]T{true: value}
}

// IsNull indicate whether the field was sent, and had a value of `null`
func (t Nullable[T]) IsNull() bool {
	_, foundNull := t[false]
	return foundNull
}

// SetNull indicate that the field was sent, and had a value of `null`
func (t *Nullable[T]) SetNull() {
	var empty T
	*t = map[bool]T{false: empty}
}

// IsSpecified indicates whether the field was sent
func (t Nullable[T]) IsSpecified() bool {
	return len(t) != 0
}

// SetUnspecified indicate whether the field was sent
func (t *Nullable[T]) SetUnspecified() {
	*t = map[bool]T{}
}

func (t Nullable[T]) MarshalJSON() ([]byte, error) {
	// if field was specified, and `null`, marshal it
	if t.IsNull() {
		return []byte("null"), nil
	}

	// if field was unspecified, and `omitempty` is set on the field's tags, `json.Marshal` will omit this field

	// otherwise: we have a value, so marshal it
	return json.Marshal(t[true])
}

func (t *Nullable[T]) UnmarshalJSON(data []byte) error {
	// if field is unspecified, UnmarshalJSON won't be called

	// if field is specified, and `null`
	if bytes.Equal(data, []byte("null")) {
		t.SetNull()
		return nil
	}
	// otherwise, we have an actual value, so parse it
	var v T
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}
	t.Set(v)
	return nil
}
Loading