Skip to content

Commit

Permalink
Added methods for builder, added some tests, updated payload validation
Browse files Browse the repository at this point in the history
  • Loading branch information
xBlaz3kx committed Sep 29, 2024
1 parent b07473c commit dad4910
Show file tree
Hide file tree
Showing 4 changed files with 269 additions and 57 deletions.
120 changes: 66 additions & 54 deletions builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package ocmf_go

import (
"crypto"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"

Expand All @@ -14,32 +12,34 @@ type BuilderOption func(*Builder)

func WithSignatureAlgorithm(algorithm SignatureAlgorithm) BuilderOption {
return func(b *Builder) {
b.signature.Algorithm = algorithm
if isValidSignatureAlgorithm(algorithm) {
b.signature.Algorithm = algorithm
}
}
}

func WithSignatureEncoding(encoding SignatureEncoding) BuilderOption {
return func(b *Builder) {
b.signature.Encoding = encoding
if isValidSignatureEncoding(encoding) {
b.signature.Encoding = encoding
}
}
}

type Builder struct {
payload PayloadSection
signature Signature
payload PayloadSection
signature Signature
privateKey crypto.PrivateKey
}

func NewBuilder(opts ...BuilderOption) *Builder {
func NewBuilder(privateKey crypto.PrivateKey, opts ...BuilderOption) *Builder {
builder := &Builder{
payload: PayloadSection{
FormatVersion: "0.4",
FormatVersion: OcmfVersion,
},
// Set default signature parameters
signature: Signature{
Algorithm: SignatureAlgorithmECDSAsecp256r1SHA256,
Encoding: SignatureEncodingHex,
MimeType: SignatureMimeTypeDer,
},
signature: *NewDefaultSignature(),
privateKey: privateKey,
}

// Apply builder options
Expand Down Expand Up @@ -75,74 +75,86 @@ func (b *Builder) AddReading(reading Reading) *Builder {
return b
}

func (b *Builder) AddFlag(flag string) *Builder {
func (b *Builder) AddIdentificationFlag(flag string) *Builder {
b.payload.IdentificationFlags = append(b.payload.IdentificationFlags, flag)
return b
}

func (b *Builder) AddLossCompensation(lossCompensation LossCompensation) *Builder {
b.payload.LossCompensation = lossCompensation
func (b *Builder) WithMeterSerial(serial string) *Builder {
b.payload.MeterSerial = serial
return b
}

func (b *Builder) ClearPayloadSection() *Builder {
b.payload = PayloadSection{
FormatVersion: "0.4",
}
func (b *Builder) WithIdentificationStatus(status bool) *Builder {
b.payload.IdentificationStatus = status
return b
}

// Sign payload
func (b *Builder) signPayload(privateKey crypto.PrivateKey) {
var signedData string
func (b *Builder) WithIdentificationLevel(level string) *Builder {
b.payload.IdentificationLevel = level
return b
}

switch b.signature.Algorithm {
case SignatureAlgorithmECDSAsecp192k1SHA256:
// TODO
case SignatureAlgorithmECDSAsecp256k1SHA256:
// TODO
case SignatureAlgorithmECDSAsecp384r1SHA256:
// TODO
case SignatureAlgorithmECDSAbrainpool256r11SHA256:
// TODO
case SignatureAlgorithmECDSAsecp256r1SHA256:
// TODO
default:
func (b *Builder) WithIdentificationType(idType string) *Builder {
b.payload.IdentificationType = idType
return b
}

}
func (b *Builder) WithIdentificationData(data string) *Builder {
b.payload.IdentificationData = data
return b
}

// Encode signed data
switch b.signature.Encoding {
case SignatureEncodingBase64:
signedData = base64.StdEncoding.EncodeToString([]byte(signedData))
default:
signedData = hex.EncodeToString([]byte(signedData))
}
func (b *Builder) WithTariffText(text string) *Builder {
b.payload.TariffText = text
return b
}

b.signature.Data = signedData
func (b *Builder) WithChargeControllerVersion(version string) *Builder {
b.payload.ChargeControllerVersion = version
return b
}

// Sign payload
func (b *Builder) validatePayload() error {
return b.payload.Validate()
func (b *Builder) WithChargePointIdentificationType(serial string) *Builder {
b.payload.ChargePointIdentificationType = serial
return b
}

func (b *Builder) Build(privateKey crypto.PrivateKey) (*string, error) {
err := b.validatePayload()
func (b *Builder) WithChargePointIdentification(serial string) *Builder {
b.payload.ChargePointIdentification = serial
return b
}

func (b *Builder) AddLossCompensation(lossCompensation LossCompensation) *Builder {
b.payload.LossCompensation = lossCompensation
return b
}

func (b *Builder) ClearPayloadSection() *Builder {
b.payload = PayloadSection{
FormatVersion: OcmfVersion,
}
return b
}

func (b *Builder) Build() (*string, error) {
// Validate payload
err := b.payload.Validate()
if err != nil {
return nil, err
}

// Build payload
payload, err := json.Marshal(b.payload)
// Sign payload with private key
err = b.signature.Sign(b.privateKey)
if err != nil {
return nil, err
}

// Sign payload
b.signPayload(privateKey)
payload, err := json.Marshal(b.payload)
if err != nil {
return nil, err
}

// Build signature
signature, err := json.Marshal(b.signature)
if err != nil {
return nil, err
Expand Down
200 changes: 200 additions & 0 deletions builder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
package ocmf_go

import (
"testing"

"github.com/stretchr/testify/suite"
)

type builderTestSuite struct {
suite.Suite
}

func (s *builderTestSuite) SetupTest() {
}

func (s *builderTestSuite) TearDownSuite() {
}

func (s *builderTestSuite) TestNewBuilder() {
tests := []struct {
name string
opts []BuilderOption
}{
{},
}

for _, tt := range tests {
s.T().Run(tt.name, func(t *testing.T) {

})
}
}

func (s *builderTestSuite) TestBuilder_Valid() {
privateKey := "" // todo
builder := NewBuilder(privateKey).
WithPagination("1").
WithMeterSerial("exampleSerial123").
WithIdentificationStatus(true).
WithIdentificationType(string(RfidNone)).
AddReading(Reading{
Time: "2018-07-24T13:22:04,000+0200",
ReadingValue: 123,
ReadingUnit: string(UnitskWh),
Status: string(MeterOk),
})

s.Equal("1", builder.payload.Pagination)
s.Equal("exampleSerial123", builder.payload.MeterSerial)
s.Equal(true, builder.payload.IdentificationStatus)
s.Equal(string(RfidNone), builder.payload.IdentificationType)
s.Len(builder.payload.Readings, 1)
s.Equal("2018-07-24T13:22:04,000+0200", builder.payload.Readings[0].Time)
s.Equal(123, builder.payload.Readings[0].ReadingValue)
s.Equal(string(UnitskWh), builder.payload.Readings[0].ReadingUnit)
s.Equal(string(MeterOk), builder.payload.Readings[0].Status)

payload, err := builder.Build()
s.NoError(err)
s.NotNil(payload)
}

func (s *builderTestSuite) TestBuilder_MissingAttributes() {
builder := NewBuilder("privateKey").
// WithPagination("1").
WithMeterSerial("exampleSerial123").
WithIdentificationStatus(true).
WithIdentificationType(string(RfidNone)).
AddReading(Reading{
Time: "2021-01-01T00:00:00Z",
ReadingValue: 123,
ReadingUnit: string(UnitskWh),
Status: string(MeterOk),
})

payload, err := builder.Build()
s.Error(err)
s.Nil(payload)
}

func (s *builderTestSuite) TestBuilder_CantSign() {
builder := NewBuilder("privateKey").
WithPagination("1").
WithMeterSerial("exampleSerial123").
WithIdentificationStatus(true).
WithIdentificationType(string(RfidNone)).
AddReading(Reading{
Time: "2021-01-01T00:00:00Z",
ReadingValue: 123,
ReadingUnit: string(UnitskWh),
Status: string(MeterOk),
})

builder.privateKey = ""

payload, err := builder.Build()
s.Error(err)
s.Nil(payload)
}

func (s *builderTestSuite) TestWithSignatureAlgorithm() {
tests := []struct {
name string
algorithm SignatureAlgorithm
want bool
}{
{
name: "ECDSA-secp192k1-SHA256",
algorithm: SignatureAlgorithmECDSAsecp192k1SHA256,
want: true,
},
{
name: "ECDSA-secp256k1-SHA256",
algorithm: SignatureAlgorithmECDSAsecp256k1SHA256,
want: true,
},
{
name: "ECDSA-secp384r1-SHA256",
algorithm: SignatureAlgorithmECDSAsecp384r1SHA256,
want: true,
},
{
name: "ECDSA-brainpool256r1-SHA256",
algorithm: SignatureAlgorithmECDSAbrainpool256r11SHA256,
want: true,
},
{
name: "ECDSA-secp256r1-SHA256",
algorithm: SignatureAlgorithmECDSAsecp256r1SHA256,
want: true,
},
{
name: "ECDSA-secp192r1-SHA256",
algorithm: SignatureAlgorithmECDSAsecp192r1SHA256,
want: true,
},
{
name: "Unknown algorithm",
algorithm: SignatureAlgorithm("ABCD"),
want: false,
},
{
name: "Empty algorithm",
algorithm: SignatureAlgorithm(""),
want: false,
},
}

for _, tt := range tests {
s.T().Run(tt.name, func(t *testing.T) {
builder := NewBuilder("privateKey", WithSignatureAlgorithm(tt.algorithm))

if tt.name == "Unknown algorithm" || tt.name == "Empty algorithm" {
s.NotEqual(tt.algorithm, builder.signature.Algorithm)
} else {
s.Equal(tt.algorithm, builder.signature.Algorithm)
}
})
}
}

func (s *builderTestSuite) TestWithWithSignatureEncoding() {
tests := []struct {
name string
encoding SignatureEncoding
}{
{
name: "Base64 encoding",
encoding: SignatureEncodingBase64,
},
{
name: "Hex encoding",
encoding: SignatureEncodingHex,
},
{
name: "Empty encoding",
encoding: SignatureEncoding(""),
},
{
name: "Unknown encoding",
encoding: SignatureEncoding("ABDD"),
},
}

for _, tt := range tests {
s.T().Run(tt.name, func(t *testing.T) {
builder := NewBuilder("privateKey", WithSignatureEncoding(tt.encoding))

if tt.encoding == SignatureEncodingBase64 || tt.encoding == SignatureEncodingHex {
s.Equal(tt.encoding, builder.signature.Encoding)
} else {
s.NotEqual(tt.encoding, builder.signature.Encoding)
}
})
}
}

func TestBuilder(t *testing.T) {
suite.Run(t, new(builderTestSuite))
}
4 changes: 2 additions & 2 deletions payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,11 @@ type PayloadSection struct {
MeterSerial string `json:"MS" validate:"required"`
MeterFirmware string `json:"MF,omitempty"`
// User assignment
IdentificationStatus bool `json:"IS" validate:"required,"`
IdentificationStatus bool `json:"IS" validate:"required"`
IdentificationLevel string `json:"IL,omitempty" validate:"omitempty,userAssignmentState"`
IdentificationFlags []string `json:"IF" validate:"omitempty,max=4"`
IdentificationType string `json:"IT" validate:"required,rfidState"`
IdentificationData string `json:"ID,omitempty" validate:"omitempty,hex"`
IdentificationData string `json:"ID,omitempty" validate:"omitempty,hexadecimal"`
TariffText string `json:"TT,omitempty" validate:"omitempty,max=250"`
// EVSE metrologic parameters
LossCompensation LossCompensation `json:"LC,omitempty"`
Expand Down
Loading

0 comments on commit dad4910

Please sign in to comment.