From 684bcdb8be8ffb16b9bc38fef6ecb5dc5262ffd6 Mon Sep 17 00:00:00 2001 From: Vlad Vitan <23100181+vlasebian@users.noreply.github.com> Date: Wed, 15 Jan 2025 13:04:08 +0200 Subject: [PATCH] util: Add recvTime to uplink decoder input --- .../javascript/javascript.go | 45 ++++++++++++------- .../javascript/javascript_test.go | 27 +++++++++++ 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/pkg/messageprocessors/javascript/javascript.go b/pkg/messageprocessors/javascript/javascript.go index 81219b288f..8b7c76fb1f 100644 --- a/pkg/messageprocessors/javascript/javascript.go +++ b/pkg/messageprocessors/javascript/javascript.go @@ -21,6 +21,7 @@ import ( "reflect" "runtime/trace" "strings" + "time" "go.thethings.network/lorawan-stack/v3/pkg/errors" "go.thethings.network/lorawan-stack/v3/pkg/goproto" @@ -101,8 +102,8 @@ func (h *host) CompileDownlinkEncoder( return func( ctx context.Context, - ids *ttnpb.EndDeviceIdentifiers, - version *ttnpb.EndDeviceVersionIdentifiers, + _ *ttnpb.EndDeviceIdentifiers, + _ *ttnpb.EndDeviceVersionIdentifiers, msg *ttnpb.ApplicationDownlink, ) error { return h.encodeDownlink(ctx, msg, run) @@ -138,7 +139,7 @@ func (*host) encodeDownlink( if err != nil { return errInput.WithCause(err) } - fPort := uint8(msg.FPort) + fPort := uint8(msg.FPort) // nolint:gosec input := encodeDownlinkInput{ Data: data, FPort: &fPort, @@ -170,8 +171,9 @@ func (*host) encodeDownlink( } type decodeUplinkInput struct { - Bytes []uint8 `json:"bytes"` - FPort uint8 `json:"fPort"` + Bytes []uint8 `json:"bytes"` + FPort uint8 `json:"fPort"` + RecvTime int64 `json:"recvTime"` // UnixNano } type decodeUplinkOutput struct { @@ -200,9 +202,13 @@ func wrapUplinkDecoderScript(script string) string { function main(input) { const bytes = input.bytes.slice(); - const { fPort } = input; + const { fPort, recvTime } = input; + + // Convert UnixNano to JavaScript Date. + const jsDate = new Date(Number(BigInt(recvTime) / 1000000n)); + if (typeof decodeUplink === 'function') { - const decoded = decodeUplink({ bytes, fPort }); + const decoded = decodeUplink({ bytes, fPort, recvTime: jsDate }); let normalized; const { data, errors } = decoded; if ((!errors || !errors.length) && data && typeof normalizeUplink === 'function') { @@ -240,8 +246,8 @@ func (h *host) CompileUplinkDecoder( return func( ctx context.Context, - ids *ttnpb.EndDeviceIdentifiers, - version *ttnpb.EndDeviceVersionIdentifiers, + _ *ttnpb.EndDeviceIdentifiers, + _ *ttnpb.EndDeviceVersionIdentifiers, msg *ttnpb.ApplicationUplink, ) error { return h.decodeUplink(ctx, msg, run) @@ -280,7 +286,7 @@ func appendValidationErrors(dst []string, measurements []normalizedpayload.Parse return dst } -func (*host) decodeUplink( +func (*host) decodeUplink( // nolint: gocyclo ctx context.Context, msg *ttnpb.ApplicationUplink, run func(context.Context, string, ...any) (func(any) error, error), @@ -288,8 +294,9 @@ func (*host) decodeUplink( defer trace.StartRegion(ctx, "decode uplink message").End() input := decodeUplinkInput{ - Bytes: msg.FrmPayload, - FPort: uint8(msg.FPort), + Bytes: msg.FrmPayload, + FPort: uint8(msg.FPort), // nolint:gosec + RecvTime: msg.ReceivedAt.AsTime().UnixNano(), } valueAs, err := run(ctx, "main", input) @@ -306,6 +313,14 @@ func (*host) decodeUplink( if errs := output.Decoded.Errors; len(errs) > 0 { return errOutputErrors.WithAttributes("errors", strings.Join(errs, ", ")) } + + // goproto.Struct does not support time.Time, use UnixNano instead. + for key, item := range output.Decoded.Data { + if t, ok := item.(time.Time); ok { + output.Decoded.Data[key] = t.UnixNano() + } + } + decodedPayload, err := goproto.Struct(output.Decoded.Data) if err != nil { return errOutput.WithCause(err) @@ -432,8 +447,8 @@ func (h *host) CompileDownlinkDecoder( return func( ctx context.Context, - ids *ttnpb.EndDeviceIdentifiers, - version *ttnpb.EndDeviceVersionIdentifiers, + _ *ttnpb.EndDeviceIdentifiers, + _ *ttnpb.EndDeviceVersionIdentifiers, msg *ttnpb.ApplicationDownlink, ) error { return h.decodeDownlink(ctx, msg, run) @@ -463,7 +478,7 @@ func (*host) decodeDownlink( input := decodeDownlinkInput{ Bytes: msg.FrmPayload, - FPort: uint8(msg.FPort), + FPort: uint8(msg.FPort), // nolint:gosec } valueAs, err := run(ctx, "main", input) diff --git a/pkg/messageprocessors/javascript/javascript_test.go b/pkg/messageprocessors/javascript/javascript_test.go index 6684742e6b..fddc66f16a 100644 --- a/pkg/messageprocessors/javascript/javascript_test.go +++ b/pkg/messageprocessors/javascript/javascript_test.go @@ -814,6 +814,33 @@ func TestDecodeUplink(t *testing.T) { err := host.DecodeUplink(ctx, ids, nil, message, script) a.So(err, should.BeNil) } + + // Check recvTime. + { + script := ` + function decodeUplink(input) { + if (input.recvTime === undefined) { + throw new Error('recvTime is undefined'); + } + + if (input.recvTime === null) { + throw new Error('recvTime is null'); + } + + if (!(input.recvTime instanceof Date)) { + throw new Error('recvTime is not a date object, got ' + typeof input.recvTime); + } + + return { + data: { + recvTime: input.recvTime + } + } + } + ` + err := host.DecodeUplink(ctx, ids, nil, message, script) + a.So(err, should.BeNil) + } } func TestDecodeDownlink(t *testing.T) {