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

fix(changetracking): alter the functioning of milli and nanoseconds in timestamps v2 #1139

Closed
wants to merge 2 commits into from
Closed
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
2 changes: 1 addition & 1 deletion .tutone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ packages:
- name: ChangeTrackingDeployment
- name: ChangeTrackingDeploymentInput
- name: EpochMilliseconds
field_type_override: nrtime.EpochMilliseconds
# field_type_override: changetracking.EpochMilliseconds
skip_type_create: true
- name: EntityGuid
field_type_override: common.EntityGUID
Expand Down
100 changes: 100 additions & 0 deletions pkg/changetracking/changetracking_additional_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package changetracking

import (
"fmt"
"strconv"
"time"
)

// EpochMilliseconds - The `EpochMilliseconds` scalar represents the number of milliseconds since the Unix epoch
type EpochMilliseconds EpochTime

// EpochTime is a type used for unmarshaling timestamps represented in epoch time.
// Its underlying type is time.Time.
type EpochTime time.Time

// MarshalJSON is responsible for marshaling the EpochTime type.
func (e EpochTime) MarshalJSON() ([]byte, error) {
ret := strconv.FormatInt(time.Time(e).UTC().Unix(), 10)
milli := int64(time.Time(e).Nanosecond()) / int64(time.Millisecond)
if milli == 0 {
milliNonZero := time.Duration(1) * time.Millisecond
// the above can also be time.Duration(rand.Int63n(1000)) * time.Millisecond
milli = milliNonZero.Milliseconds()
}

nano := int64(time.Time(e).Nanosecond())

// Include milliseconds if there are some
if milli > 0 {
ret += fmt.Sprintf("%03d", milli)
} else if nano > 0 {
ret += fmt.Sprintf("%09d", nano)
}

return []byte(ret), nil
}

// UnmarshalJSON is responsible for unmarshaling the EpochTime type.
func (e *EpochTime) UnmarshalJSON(s []byte) error {
var (
err error
sec int64
milli int64
nano int64
)

// detect type of timestamp based on length
switch l := len(s); {
case string(s) == emptyTimeCase: // when we try to unmarhsal empty unix time
return nil
case l <= 10: // seconds
sec, err = strconv.ParseInt(string(s), 10, 64)
case l > 10 && l <= 16: // milliseconds
milli, err = strconv.ParseInt(string(s[0:13]), 10, 64)
if err != nil {
return err
}
nano = milli * int64(time.Millisecond)
case l > 16: // nanoseconds
sec, err = strconv.ParseInt(string(s[0:10]), 10, 64)
if err != nil {
return err
}
nano, err = strconv.ParseInt(string(s[10:l]), 10, 64)
default:
return fmt.Errorf("unable to parse EpochTime: '%s'", s)
}

if err != nil {
return err
}

// Convert and self store
*(*time.Time)(e) = time.Unix(sec, nano).UTC()

return nil
}

// Equal provides a comparator for the EpochTime type.
func (e EpochTime) Equal(u EpochTime) bool {
return time.Time(e).Equal(time.Time(u))
}

// String returns the time formatted using the format string
func (e EpochTime) String() string {
return time.Time(e).String()
}

// Unix returns the time formatted as seconds since Jan 1st, 1970
func (e EpochTime) Unix() int64 {
return time.Time(e).Unix()
}

const (
// emptyTimeCase represents result in case we marshaled empty EpochTime object to byte
// and then tried to marshal it back (we are going to get the value from []byte)
// at this moment it is emergency case, detailed you can check here https://github.com/golang/protobuf/issues/710
// and here https://github.com/newrelic/newrelic-client-go/issues/992
emptyTimeCase = "-62135596800"
)
118 changes: 117 additions & 1 deletion pkg/changetracking/changetracking_api_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
Description: "This is a test description",
EntityGUID: common.EntityGUID(testhelpers.IntegrationTestApplicationEntityGUIDNew),
GroupId: "deployment",
Timestamp: nrtime.EpochMilliseconds(time.Now()),

Check failure on line 33 in pkg/changetracking/changetracking_api_integration_test.go

View workflow job for this annotation

GitHub Actions / test-integration

cannot ***e nrtime.EpochMilliseconds(time.Now()) (value of type nrtime.EpochMilliseconds) as EpochMilliseconds value in struct literal

Check failure on line 33 in pkg/changetracking/changetracking_api_integration_test.go

View workflow job for this annotation

GitHub Actions / test-integration

cannot ***e nrtime.EpochMilliseconds(time.Now()) (value of type nrtime.EpochMilliseconds) as EpochMilliseconds value in struct literal
User: "newrelic-go-client",
Version: "0.0.1",
}
Expand Down Expand Up @@ -66,9 +66,9 @@
DeepLink: "newrelic-client-go",
DeploymentType: ChangeTrackingDeploymentTypeTypes.BASIC,
Description: "This is a test description",
EntityGUID: common.EntityGUID(testhelpers.IntegrationTestApplicationEntityGUID),
EntityGUID: common.EntityGUID(testhelpers.IntegrationTestApplicationEntityGUIDNew),
GroupId: "deployment",
Timestamp: nrtime.EpochMilliseconds(time.Now()),

Check failure on line 71 in pkg/changetracking/changetracking_api_integration_test.go

View workflow job for this annotation

GitHub Actions / test-integration

cannot ***e nrtime.EpochMilliseconds(time.Now()) (value of type nrtime.EpochMilliseconds) as EpochMilliseconds value in struct literal

Check failure on line 71 in pkg/changetracking/changetracking_api_integration_test.go

View workflow job for this annotation

GitHub Actions / test-integration

cannot ***e nrtime.EpochMilliseconds(time.Now()) (value of type nrtime.EpochMilliseconds) as EpochMilliseconds value in struct literal
User: "newrelic-go-client",
Version: "0.0.1",
}
Expand Down Expand Up @@ -96,7 +96,7 @@
Description: "This is a test description",
EntityGUID: common.EntityGUID(testhelpers.IntegrationTestApplicationEntityGUID),
GroupId: "deployment",
Timestamp: nrtime.EpochMilliseconds(time.UnixMilli(0)),

Check failure on line 99 in pkg/changetracking/changetracking_api_integration_test.go

View workflow job for this annotation

GitHub Actions / test-integration

cannot ***e nrtime.EpochMilliseconds(time.UnixMilli(0)) (value of type nrtime.EpochMilliseconds) as EpochMilliseconds value in struct literal

Check failure on line 99 in pkg/changetracking/changetracking_api_integration_test.go

View workflow job for this annotation

GitHub Actions / test-integration

cannot ***e nrtime.EpochMilliseconds(time.UnixMilli(0)) (value of type nrtime.EpochMilliseconds) as EpochMilliseconds value in struct literal
User: "newrelic-go-client",
Version: "0.0.1",
}
Expand All @@ -109,6 +109,122 @@
require.Nil(t, res)
}

func TestChangeTrackingCreateDeployment_OldTimestampError(t *testing.T) {
t.Parallel()
now := time.Now()

a := newIntegrationTestClient(t)

input := ChangeTrackingDeploymentInput{
Changelog: "test",
Commit: "12345a",
DeepLink: "newrelic-client-go",
DeploymentType: ChangeTrackingDeploymentTypeTypes.BASIC,
Description: "This is a test description",
EntityGUID: common.EntityGUID(testhelpers.IntegrationTestApplicationEntityGUID),
GroupId: "deployment",
Timestamp: nrtime.EpochMilliseconds(

Check failure on line 126 in pkg/changetracking/changetracking_api_integration_test.go

View workflow job for this annotation

GitHub Actions / test-integration

cannot ***e nrtime.EpochMilliseconds(time.Date(now.Year(), now.Month(), now.Day() - 2, now.Hour() - 3, now.Minute() - 30, 0, 0, time.Local)) (value of type nrtime.EpochMilliseconds) as EpochMilliseconds value in struct literal

Check failure on line 126 in pkg/changetracking/changetracking_api_integration_test.go

View workflow job for this annotation

GitHub Actions / test-integration

cannot ***e nrtime.EpochMilliseconds(time.Date(now.Year(), now.Month(), now.Day() - 2, now.Hour() - 3, now.Minute() - 30, 0, 0, time.Local)) (value of type nrtime.EpochMilliseconds) as EpochMilliseconds value in struct literal
time.Date(
now.Year(),
now.Month(),
now.Day()-2,
now.Hour()-3,
now.Minute()-30,
0,
0,
time.Local,
),
),
User: "newrelic-go-client",
Version: "0.0.1",
}

res, err := a.ChangeTrackingCreateDeployment(
ChangeTrackingDataHandlingRules{ValidationFlags: []ChangeTrackingValidationFlag{ChangeTrackingValidationFlagTypes.FAIL_ON_FIELD_LENGTH}},
input,
)
require.Error(t, err)
require.Nil(t, res)
}

func TestChangeTrackingCreateDeployment_TimestampZeroNanosecondsTest(t *testing.T) {
t.Parallel()

a := newIntegrationTestClient(t)
now := time.Now()

input := ChangeTrackingDeploymentInput{
Changelog: "test",
Commit: "12345a",
DeepLink: "newrelic-client-go",
DeploymentType: ChangeTrackingDeploymentTypeTypes.BASIC,
Description: "This is a test description",
EntityGUID: common.EntityGUID(testhelpers.IntegrationTestApplicationEntityGUIDNew),
GroupId: "deployment",
Timestamp: nrtime.EpochMilliseconds(

Check failure on line 164 in pkg/changetracking/changetracking_api_integration_test.go

View workflow job for this annotation

GitHub Actions / test-integration

cannot ***e nrtime.EpochMilliseconds(time.Date(now.Year(), now.Month(), now.Day(), now.Hour() - 3, now.Minute() - 30, 0, 0, time.Local)) (value of type nrtime.EpochMilliseconds) as EpochMilliseconds value in struct literal
time.Date(
now.Year(),
now.Month(),
now.Day(),
now.Hour()-3,
now.Minute()-30,
0,
0,
time.Local,
),
),
User: "newrelic-go-client",
Version: "0.0.1",
}

res, err := a.ChangeTrackingCreateDeployment(
ChangeTrackingDataHandlingRules{ValidationFlags: []ChangeTrackingValidationFlag{ChangeTrackingValidationFlagTypes.FAIL_ON_FIELD_LENGTH}},
input,
)
require.NoError(t, err)
require.NotNil(t, res.EntityGUID)
require.Equal(t, res.EntityGUID, input.EntityGUID)
}

func TestChangeTrackingCreateDeployment_TimestampNonZeroNanosecondsTest(t *testing.T) {
t.Parallel()

a := newIntegrationTestClient(t)
now := time.Now()

input := ChangeTrackingDeploymentInput{
Changelog: "test",
Commit: "12345a",
DeepLink: "newrelic-client-go",
DeploymentType: ChangeTrackingDeploymentTypeTypes.BASIC,
Description: "This is a test description",
EntityGUID: common.EntityGUID(testhelpers.IntegrationTestApplicationEntityGUIDNew),
GroupId: "deployment",
Timestamp: nrtime.EpochMilliseconds(

Check failure on line 203 in pkg/changetracking/changetracking_api_integration_test.go

View workflow job for this annotation

GitHub Actions / test-integration

cannot ***e nrtime.EpochMilliseconds(time.Date(now.Year(), now.Month(), now.Day(), now.Hour() - 6, now.Minute() - 30, 0, 231567, time.Local)) (value of type nrtime.EpochMilliseconds) as EpochMilliseconds value in struct literal
time.Date(
now.Year(),
now.Month(),
now.Day(),
now.Hour()-6,
now.Minute()-30,
0,
231567,
time.Local,
),
),
User: "newrelic-go-client",
Version: "0.0.1",
}

res, err := a.ChangeTrackingCreateDeployment(
ChangeTrackingDataHandlingRules{ValidationFlags: []ChangeTrackingValidationFlag{ChangeTrackingValidationFlagTypes.FAIL_ON_FIELD_LENGTH}},
input,
)
require.NoError(t, err)
require.NotNil(t, res.EntityGUID)
require.Equal(t, res.EntityGUID, input.EntityGUID)
}

func newIntegrationTestClient(t *testing.T) Changetracking {
tc := testhelpers.NewIntegrationTestConfig(t)

Expand Down
9 changes: 3 additions & 6 deletions pkg/changetracking/types.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
// Code generated by tutone: DO NOT EDIT
package changetracking

import (
"github.com/newrelic/newrelic-client-go/v2/pkg/common"
"github.com/newrelic/newrelic-client-go/v2/pkg/nrtime"
)
import "github.com/newrelic/newrelic-client-go/v2/pkg/common"

// ChangeTrackingDeploymentType - Type of deployment.
type ChangeTrackingDeploymentType string
Expand Down Expand Up @@ -79,7 +76,7 @@ type ChangeTrackingDeployment struct {
// An identifier used to correlate two or more events.
GroupId string `json:"groupId,omitempty"`
// The start time of the deployment as the number of milliseconds since the Unix epoch.
Timestamp nrtime.EpochMilliseconds `json:"timestamp"`
Timestamp EpochMilliseconds `json:"timestamp"`
// The username of the deployer or bot.
User string `json:"user,omitempty"`
// The version of the deployed software, for example, something like v1.1.
Expand Down Expand Up @@ -116,7 +113,7 @@ type ChangeTrackingDeploymentInput struct {
// An identifier used to correlate two or more events.
GroupId string `json:"groupId,omitempty"`
// The start time of the deployment as the number of milliseconds since the Unix epoch. Should be within the boundary of the past or future 24 hours. Defaults to now.
Timestamp nrtime.EpochMilliseconds `json:"timestamp,omitempty"`
Timestamp EpochMilliseconds `json:"timestamp,omitempty"`
// The username of the deployer or bot.
User string `json:"user,omitempty"`
// The version of the deployed software, for example, something like v1.1
Expand Down
Loading