<commit_before>// Copyright © 2017 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package handler

import (
	ttnlog "github.com/TheThingsNetwork/go-utils/log"
	pb_broker "github.com/TheThingsNetwork/ttn/api/broker"
	"github.com/TheThingsNetwork/ttn/core/handler/application"
	"github.com/TheThingsNetwork/ttn/core/handler/cayennelpp"
	"github.com/TheThingsNetwork/ttn/core/handler/device"
	"github.com/TheThingsNetwork/ttn/core/handler/functions"
	"github.com/TheThingsNetwork/ttn/core/types"
	"github.com/TheThingsNetwork/ttn/utils/errors"
)

// PayloadDecoder decodes raw payload to fields
type PayloadDecoder interface {
	Decode(payload []byte, fPort uint8) (map[string]interface{}, bool, error)
}

// PayloadEncoder encodes fields to raw payload
type PayloadEncoder interface {
	Encode(fields map[string]interface{}, fPort uint8) ([]byte, error)
}

// ConvertFieldsUp converts the payload to fields using the application's payload formatter
func (h *handler) ConvertFieldsUp(ctx ttnlog.Interface, _ *pb_broker.DeduplicatedUplinkMessage, appUp *types.UplinkMessage, _ *device.Device) error {
	// Find Application
	app, err := h.applications.Get(appUp.AppID)
	if err != nil {
		return nil // Do not process if application not found
	}

	var decoder PayloadDecoder
	switch app.PayloadFormat {
	case "", application.PayloadFormatCustom:
		decoder = &CustomUplinkFunctions{
			Decoder:   app.CustomDecoder,
			Converter: app.CustomConverter,
			Validator: app.CustomValidator,
			Logger:    functions.Ignore,
		}
	case application.PayloadFormatCayenneLPP:
		decoder = &cayennelpp.Decoder{}
	default:
		return nil
	}

	fields, valid, err := decoder.Decode(appUp.PayloadRaw, appUp.FPort)
	if err != nil {
		// Emit the error
		h.mqttEvent <- &types.DeviceEvent{
			AppID: appUp.AppID,
			DevID: appUp.DevID,
			Event: types.UplinkErrorEvent,
			Data:  types.ErrorEventData{Error: err.Error()},
		}

		// Do not set fields if processing failed, but allow the handler to continue processing
		// without payload formatting
		return nil
	}

	if !valid {
		return errors.NewErrInvalidArgument("Payload", "payload validator function returned false")
	}

	appUp.PayloadFields = fields

	return nil
}

// ConvertFieldsDown converts the fields into a payload
func (h *handler) ConvertFieldsDown(ctx ttnlog.Interface, appDown *types.DownlinkMessage, ttnDown *pb_broker.DownlinkMessage, _ *device.Device) error {
	if appDown.PayloadFields == nil || len(appDown.PayloadFields) == 0 {
		return nil
	}

	if appDown.PayloadRaw != nil {
		return errors.NewErrInvalidArgument("Downlink", "Both Fields and Payload provided")
	}

	app, err := h.applications.Get(appDown.AppID)
	if err != nil {
		return err
	}

	var encoder PayloadEncoder
	switch app.PayloadFormat {
	case "", application.PayloadFormatCustom:
		encoder = &CustomDownlinkFunctions{
			Encoder: app.CustomEncoder,
			Logger:  functions.Ignore,
		}
	case application.PayloadFormatCayenneLPP:
		encoder = &cayennelpp.Encoder{}
	default:
		return nil
	}

	raw, _, err := encoder.Encode(appDown.PayloadFields, appDown.FPort)
	if err != nil {
		return err
	}

	appDown.PayloadRaw = raw<commit_msg>Do not try JavaScript when there's no payload format<commit_after>// Copyright © 2017 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package handler

import (
	ttnlog "github.com/TheThingsNetwork/go-utils/log"
	pb_broker "github.com/TheThingsNetwork/ttn/api/broker"
	"github.com/TheThingsNetwork/ttn/core/handler/application"
	"github.com/TheThingsNetwork/ttn/core/handler/cayennelpp"
	"github.com/TheThingsNetwork/ttn/core/handler/device"
	"github.com/TheThingsNetwork/ttn/core/handler/functions"
	"github.com/TheThingsNetwork/ttn/core/types"
	"github.com/TheThingsNetwork/ttn/utils/errors"
)

// PayloadDecoder decodes raw payload to fields
type PayloadDecoder interface {
	Decode(payload []byte, fPort uint8) (map[string]interface{}, bool, error)
}

// PayloadEncoder encodes fields to raw payload
type PayloadEncoder interface {
	Encode(fields map[string]interface{}, fPort uint8) ([]byte, error)
}

// ConvertFieldsUp converts the payload to fields using the application's payload formatter
func (h *handler) ConvertFieldsUp(ctx ttnlog.Interface, _ *pb_broker.DeduplicatedUplinkMessage, appUp *types.UplinkMessage, _ *device.Device) error {
	// Find Application
	app, err := h.applications.Get(appUp.AppID)
	if err != nil {
		return nil // Do not process if application not found
	}

	var decoder PayloadDecoder
	switch app.PayloadFormat {
	case application.PayloadFormatCustom:
		decoder = &CustomUplinkFunctions{
			Decoder:   app.CustomDecoder,
			Converter: app.CustomConverter,
			Validator: app.CustomValidator,
			Logger:    functions.Ignore,
		}
	case application.PayloadFormatCayenneLPP:
		decoder = &cayennelpp.Decoder{}
	default:
		return nil
	}

	fields, valid, err := decoder.Decode(appUp.PayloadRaw, appUp.FPort)
	if err != nil {
		// Emit the error
		h.mqttEvent <- &types.DeviceEvent{
			AppID: appUp.AppID,
			DevID: appUp.DevID,
			Event: types.UplinkErrorEvent,
			Data:  types.ErrorEventData{Error: err.Error()},
		}

		// Do not set fields if processing failed, but allow the handler to continue processing
		// without payload formatting
		return nil
	}

	if !valid {
		return errors.NewErrInvalidArgument("Payload", "payload validator function returned false")
	}

	appUp.PayloadFields = fields

	return nil
}

// ConvertFieldsDown converts the fields into a payload
func (h *handler) ConvertFieldsDown(ctx ttnlog.Interface, appDown *types.DownlinkMessage, ttnDown *pb_broker.DownlinkMessage, _ *device.Device) error {
	if appDown.PayloadFields == nil || len(appDown.PayloadFields) == 0 {
		return nil
	}

	if appDown.PayloadRaw != nil {
		return errors.NewErrInvalidArgument("Downlink", "Both Fields and Payload provided")
	}

	app, err := h.applications.Get(appDown.AppID)
	if err != nil {
		return err
	}

	var encoder PayloadEncoder
	switch app.PayloadFormat {
	case application.PayloadFormatCustom:
		encoder = &CustomDownlinkFunctions{
			Encoder: app.CustomEncoder,
			Logger:  functions.Ignore,
		}
	case application.PayloadFormatCayenneLPP:
		encoder = &cayennelpp.Encoder{}
	default:
		return nil
	}

	raw, _, err := encoder.Encode(appDown.PayloadFields, appDown.FPort)
	if err != nil {
		return err
	}

	appDown.PayloadRaw = raw
{'fraction_non_alphanumeric': 0.10103988825081484, 'fraction_numerical': 0.0021729008225981686, 'mean_word_length': 4.540842648323302, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4528796', 'n_tokens_mistral': 2171, 'n_tokens_neox': 2007, 'n_words': 527}