From fbec60365b51acc1f690afc30d697d95af122af3 Mon Sep 17 00:00:00 2001 From: xBlaz3kx Date: Thu, 6 Aug 2026 00:21:08 +0200 Subject: [PATCH] feat: add bulk message validation endpoint Validates multiple OCPP-J messages against the given OCPP version, vendor and model in one call, reading each message's own action and message type off its envelope, and returns an ordered per-message report. --- .mockery.yaml | 28 +- gen/mocks/grpc/admin_service_mock.go | 230 ++++++++ gen/mocks/grpc/schema_service_mock.go | 442 +++++++++++++++ gen/mocks/grpc/validation_service_mock.go | 175 ++++++ gen/mocks/schema/cache_mock.go | 227 ++++++++ gen/mocks/schema/repository_mock.go | 609 +++++++++++++++++++++ gen/mocks/validation/schema_getter_mock.go | 131 +++++ gen/proto/schema/v1/validation.pb.go | 238 +++++++- gen/proto/schema/v1/validation_grpc.pb.go | 36 ++ go.mod | 5 + internal/grpc/handler.go | 9 +- internal/grpc/validation_handler.go | 58 +- internal/grpc/validation_handler_test.go | 148 +++++ internal/validation/validation.go | 90 +++ internal/validation/validation_test.go | 51 ++ proto/schema/v1/validation.proto | 23 + 16 files changed, 2461 insertions(+), 39 deletions(-) create mode 100644 gen/mocks/grpc/admin_service_mock.go create mode 100644 gen/mocks/grpc/schema_service_mock.go create mode 100644 gen/mocks/grpc/validation_service_mock.go create mode 100644 gen/mocks/schema/cache_mock.go create mode 100644 gen/mocks/schema/repository_mock.go create mode 100644 gen/mocks/validation/schema_getter_mock.go create mode 100644 internal/grpc/validation_handler_test.go create mode 100644 internal/validation/validation_test.go diff --git a/.mockery.yaml b/.mockery.yaml index 939168f..5c74f42 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -1,18 +1,20 @@ -with-expecter: true -dir: gen/mocks/{{.InterfaceDirRelative}} -mockname: "Mock{{.InterfaceName}}" -outpkg: "{{.PackageName}}" -filename: "{{.InterfaceName|snakecase}}_mock.go" +dir: gen/mocks/{{trimPrefix "internal/" .InterfaceDirRelative}} +structname: Mock{{.InterfaceName}} +pkgname: '{{.SrcPackageName}}' +filename: '{{.InterfaceName | snakecase}}_mock.go' +template: testify +template-data: + unroll-variadic: true packages: - github.com/ChargePi/chargeflow-registry/internal/schema: - interfaces: - Repository: - Cache: github.com/ChargePi/chargeflow-registry/internal/grpc: interfaces: - SchemaService: - ValidationService: - AdminService: + AdminService: {} + SchemaService: {} + ValidationService: {} + github.com/ChargePi/chargeflow-registry/internal/schema: + interfaces: + Cache: {} + Repository: {} github.com/ChargePi/chargeflow-registry/internal/validation: interfaces: - SchemaGetter: + SchemaGetter: {} diff --git a/gen/mocks/grpc/admin_service_mock.go b/gen/mocks/grpc/admin_service_mock.go new file mode 100644 index 0000000..340c458 --- /dev/null +++ b/gen/mocks/grpc/admin_service_mock.go @@ -0,0 +1,230 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package grpc + +import ( + "context" + + "github.com/ChargePi/chargeflow-registry/internal/schema" + "github.com/google/uuid" + mock "github.com/stretchr/testify/mock" +) + +// NewMockAdminService creates a new instance of MockAdminService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockAdminService(t interface { + mock.TestingT + Cleanup(func()) +}) *MockAdminService { + mock := &MockAdminService{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// MockAdminService is an autogenerated mock type for the AdminService type +type MockAdminService struct { + mock.Mock +} + +type MockAdminService_Expecter struct { + mock *mock.Mock +} + +func (_m *MockAdminService) EXPECT() *MockAdminService_Expecter { + return &MockAdminService_Expecter{mock: &_m.Mock} +} + +// ChangeStatus provides a mock function for the type MockAdminService +func (_mock *MockAdminService) ChangeStatus(ctx context.Context, id uuid.UUID, status schema.Status) (*schema.Schema, error) { + ret := _mock.Called(ctx, id, status) + + if len(ret) == 0 { + panic("no return value specified for ChangeStatus") + } + + var r0 *schema.Schema + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, uuid.UUID, schema.Status) (*schema.Schema, error)); ok { + return returnFunc(ctx, id, status) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, uuid.UUID, schema.Status) *schema.Schema); ok { + r0 = returnFunc(ctx, id, status) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*schema.Schema) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, uuid.UUID, schema.Status) error); ok { + r1 = returnFunc(ctx, id, status) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockAdminService_ChangeStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChangeStatus' +type MockAdminService_ChangeStatus_Call struct { + *mock.Call +} + +// ChangeStatus is a helper method to define mock.On call +// - ctx context.Context +// - id uuid.UUID +// - status schema.Status +func (_e *MockAdminService_Expecter) ChangeStatus(ctx interface{}, id interface{}, status interface{}) *MockAdminService_ChangeStatus_Call { + return &MockAdminService_ChangeStatus_Call{Call: _e.mock.On("ChangeStatus", ctx, id, status)} +} + +func (_c *MockAdminService_ChangeStatus_Call) Run(run func(ctx context.Context, id uuid.UUID, status schema.Status)) *MockAdminService_ChangeStatus_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 uuid.UUID + if args[1] != nil { + arg1 = args[1].(uuid.UUID) + } + var arg2 schema.Status + if args[2] != nil { + arg2 = args[2].(schema.Status) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MockAdminService_ChangeStatus_Call) Return(schema1 *schema.Schema, err error) *MockAdminService_ChangeStatus_Call { + _c.Call.Return(schema1, err) + return _c +} + +func (_c *MockAdminService_ChangeStatus_Call) RunAndReturn(run func(ctx context.Context, id uuid.UUID, status schema.Status) (*schema.Schema, error)) *MockAdminService_ChangeStatus_Call { + _c.Call.Return(run) + return _c +} + +// List provides a mock function for the type MockAdminService +func (_mock *MockAdminService) List(ctx context.Context, version schema.OCPPVersion, vendor *string, model *string, action *string, msgType *schema.MessageType, status *schema.Status, limit uint32, offset uint32) ([]*schema.Schema, int64, error) { + ret := _mock.Called(ctx, version, vendor, model, action, msgType, status, limit, offset) + + if len(ret) == 0 { + panic("no return value specified for List") + } + + var r0 []*schema.Schema + var r1 int64 + var r2 error + if returnFunc, ok := ret.Get(0).(func(context.Context, schema.OCPPVersion, *string, *string, *string, *schema.MessageType, *schema.Status, uint32, uint32) ([]*schema.Schema, int64, error)); ok { + return returnFunc(ctx, version, vendor, model, action, msgType, status, limit, offset) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, schema.OCPPVersion, *string, *string, *string, *schema.MessageType, *schema.Status, uint32, uint32) []*schema.Schema); ok { + r0 = returnFunc(ctx, version, vendor, model, action, msgType, status, limit, offset) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*schema.Schema) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, schema.OCPPVersion, *string, *string, *string, *schema.MessageType, *schema.Status, uint32, uint32) int64); ok { + r1 = returnFunc(ctx, version, vendor, model, action, msgType, status, limit, offset) + } else { + r1 = ret.Get(1).(int64) + } + if returnFunc, ok := ret.Get(2).(func(context.Context, schema.OCPPVersion, *string, *string, *string, *schema.MessageType, *schema.Status, uint32, uint32) error); ok { + r2 = returnFunc(ctx, version, vendor, model, action, msgType, status, limit, offset) + } else { + r2 = ret.Error(2) + } + return r0, r1, r2 +} + +// MockAdminService_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' +type MockAdminService_List_Call struct { + *mock.Call +} + +// List is a helper method to define mock.On call +// - ctx context.Context +// - version schema.OCPPVersion +// - vendor *string +// - model *string +// - action *string +// - msgType *schema.MessageType +// - status *schema.Status +// - limit uint32 +// - offset uint32 +func (_e *MockAdminService_Expecter) List(ctx interface{}, version interface{}, vendor interface{}, model interface{}, action interface{}, msgType interface{}, status interface{}, limit interface{}, offset interface{}) *MockAdminService_List_Call { + return &MockAdminService_List_Call{Call: _e.mock.On("List", ctx, version, vendor, model, action, msgType, status, limit, offset)} +} + +func (_c *MockAdminService_List_Call) Run(run func(ctx context.Context, version schema.OCPPVersion, vendor *string, model *string, action *string, msgType *schema.MessageType, status *schema.Status, limit uint32, offset uint32)) *MockAdminService_List_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 schema.OCPPVersion + if args[1] != nil { + arg1 = args[1].(schema.OCPPVersion) + } + var arg2 *string + if args[2] != nil { + arg2 = args[2].(*string) + } + var arg3 *string + if args[3] != nil { + arg3 = args[3].(*string) + } + var arg4 *string + if args[4] != nil { + arg4 = args[4].(*string) + } + var arg5 *schema.MessageType + if args[5] != nil { + arg5 = args[5].(*schema.MessageType) + } + var arg6 *schema.Status + if args[6] != nil { + arg6 = args[6].(*schema.Status) + } + var arg7 uint32 + if args[7] != nil { + arg7 = args[7].(uint32) + } + var arg8 uint32 + if args[8] != nil { + arg8 = args[8].(uint32) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8, + ) + }) + return _c +} + +func (_c *MockAdminService_List_Call) Return(schemas []*schema.Schema, n int64, err error) *MockAdminService_List_Call { + _c.Call.Return(schemas, n, err) + return _c +} + +func (_c *MockAdminService_List_Call) RunAndReturn(run func(ctx context.Context, version schema.OCPPVersion, vendor *string, model *string, action *string, msgType *schema.MessageType, status *schema.Status, limit uint32, offset uint32) ([]*schema.Schema, int64, error)) *MockAdminService_List_Call { + _c.Call.Return(run) + return _c +} diff --git a/gen/mocks/grpc/schema_service_mock.go b/gen/mocks/grpc/schema_service_mock.go new file mode 100644 index 0000000..b494fcf --- /dev/null +++ b/gen/mocks/grpc/schema_service_mock.go @@ -0,0 +1,442 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package grpc + +import ( + "context" + + "github.com/ChargePi/chargeflow-registry/internal/schema" + mock "github.com/stretchr/testify/mock" +) + +// NewMockSchemaService creates a new instance of MockSchemaService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockSchemaService(t interface { + mock.TestingT + Cleanup(func()) +}) *MockSchemaService { + mock := &MockSchemaService{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// MockSchemaService is an autogenerated mock type for the SchemaService type +type MockSchemaService struct { + mock.Mock +} + +type MockSchemaService_Expecter struct { + mock *mock.Mock +} + +func (_m *MockSchemaService) EXPECT() *MockSchemaService_Expecter { + return &MockSchemaService_Expecter{mock: &_m.Mock} +} + +// AddPair provides a mock function for the type MockSchemaService +func (_mock *MockSchemaService) AddPair(ctx context.Context, req *schema.Schema, resp *schema.Schema) error { + ret := _mock.Called(ctx, req, resp) + + if len(ret) == 0 { + panic("no return value specified for AddPair") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, *schema.Schema, *schema.Schema) error); ok { + r0 = returnFunc(ctx, req, resp) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// MockSchemaService_AddPair_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddPair' +type MockSchemaService_AddPair_Call struct { + *mock.Call +} + +// AddPair is a helper method to define mock.On call +// - ctx context.Context +// - req *schema.Schema +// - resp *schema.Schema +func (_e *MockSchemaService_Expecter) AddPair(ctx interface{}, req interface{}, resp interface{}) *MockSchemaService_AddPair_Call { + return &MockSchemaService_AddPair_Call{Call: _e.mock.On("AddPair", ctx, req, resp)} +} + +func (_c *MockSchemaService_AddPair_Call) Run(run func(ctx context.Context, req *schema.Schema, resp *schema.Schema)) *MockSchemaService_AddPair_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 *schema.Schema + if args[1] != nil { + arg1 = args[1].(*schema.Schema) + } + var arg2 *schema.Schema + if args[2] != nil { + arg2 = args[2].(*schema.Schema) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MockSchemaService_AddPair_Call) Return(err error) *MockSchemaService_AddPair_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockSchemaService_AddPair_Call) RunAndReturn(run func(ctx context.Context, req *schema.Schema, resp *schema.Schema) error) *MockSchemaService_AddPair_Call { + _c.Call.Return(run) + return _c +} + +// Delete provides a mock function for the type MockSchemaService +func (_mock *MockSchemaService) Delete(ctx context.Context, version schema.OCPPVersion, action string, vendor *string, model *string) error { + ret := _mock.Called(ctx, version, action, vendor, model) + + if len(ret) == 0 { + panic("no return value specified for Delete") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, schema.OCPPVersion, string, *string, *string) error); ok { + r0 = returnFunc(ctx, version, action, vendor, model) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// MockSchemaService_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' +type MockSchemaService_Delete_Call struct { + *mock.Call +} + +// Delete is a helper method to define mock.On call +// - ctx context.Context +// - version schema.OCPPVersion +// - action string +// - vendor *string +// - model *string +func (_e *MockSchemaService_Expecter) Delete(ctx interface{}, version interface{}, action interface{}, vendor interface{}, model interface{}) *MockSchemaService_Delete_Call { + return &MockSchemaService_Delete_Call{Call: _e.mock.On("Delete", ctx, version, action, vendor, model)} +} + +func (_c *MockSchemaService_Delete_Call) Run(run func(ctx context.Context, version schema.OCPPVersion, action string, vendor *string, model *string)) *MockSchemaService_Delete_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 schema.OCPPVersion + if args[1] != nil { + arg1 = args[1].(schema.OCPPVersion) + } + var arg2 string + if args[2] != nil { + arg2 = args[2].(string) + } + var arg3 *string + if args[3] != nil { + arg3 = args[3].(*string) + } + var arg4 *string + if args[4] != nil { + arg4 = args[4].(*string) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + ) + }) + return _c +} + +func (_c *MockSchemaService_Delete_Call) Return(err error) *MockSchemaService_Delete_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockSchemaService_Delete_Call) RunAndReturn(run func(ctx context.Context, version schema.OCPPVersion, action string, vendor *string, model *string) error) *MockSchemaService_Delete_Call { + _c.Call.Return(run) + return _c +} + +// List provides a mock function for the type MockSchemaService +func (_mock *MockSchemaService) List(ctx context.Context, version schema.OCPPVersion, vendor *string, model *string, action *string, msgType *schema.MessageType, status *schema.Status, limit uint32, offset uint32) ([]*schema.Schema, int64, error) { + ret := _mock.Called(ctx, version, vendor, model, action, msgType, status, limit, offset) + + if len(ret) == 0 { + panic("no return value specified for List") + } + + var r0 []*schema.Schema + var r1 int64 + var r2 error + if returnFunc, ok := ret.Get(0).(func(context.Context, schema.OCPPVersion, *string, *string, *string, *schema.MessageType, *schema.Status, uint32, uint32) ([]*schema.Schema, int64, error)); ok { + return returnFunc(ctx, version, vendor, model, action, msgType, status, limit, offset) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, schema.OCPPVersion, *string, *string, *string, *schema.MessageType, *schema.Status, uint32, uint32) []*schema.Schema); ok { + r0 = returnFunc(ctx, version, vendor, model, action, msgType, status, limit, offset) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*schema.Schema) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, schema.OCPPVersion, *string, *string, *string, *schema.MessageType, *schema.Status, uint32, uint32) int64); ok { + r1 = returnFunc(ctx, version, vendor, model, action, msgType, status, limit, offset) + } else { + r1 = ret.Get(1).(int64) + } + if returnFunc, ok := ret.Get(2).(func(context.Context, schema.OCPPVersion, *string, *string, *string, *schema.MessageType, *schema.Status, uint32, uint32) error); ok { + r2 = returnFunc(ctx, version, vendor, model, action, msgType, status, limit, offset) + } else { + r2 = ret.Error(2) + } + return r0, r1, r2 +} + +// MockSchemaService_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' +type MockSchemaService_List_Call struct { + *mock.Call +} + +// List is a helper method to define mock.On call +// - ctx context.Context +// - version schema.OCPPVersion +// - vendor *string +// - model *string +// - action *string +// - msgType *schema.MessageType +// - status *schema.Status +// - limit uint32 +// - offset uint32 +func (_e *MockSchemaService_Expecter) List(ctx interface{}, version interface{}, vendor interface{}, model interface{}, action interface{}, msgType interface{}, status interface{}, limit interface{}, offset interface{}) *MockSchemaService_List_Call { + return &MockSchemaService_List_Call{Call: _e.mock.On("List", ctx, version, vendor, model, action, msgType, status, limit, offset)} +} + +func (_c *MockSchemaService_List_Call) Run(run func(ctx context.Context, version schema.OCPPVersion, vendor *string, model *string, action *string, msgType *schema.MessageType, status *schema.Status, limit uint32, offset uint32)) *MockSchemaService_List_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 schema.OCPPVersion + if args[1] != nil { + arg1 = args[1].(schema.OCPPVersion) + } + var arg2 *string + if args[2] != nil { + arg2 = args[2].(*string) + } + var arg3 *string + if args[3] != nil { + arg3 = args[3].(*string) + } + var arg4 *string + if args[4] != nil { + arg4 = args[4].(*string) + } + var arg5 *schema.MessageType + if args[5] != nil { + arg5 = args[5].(*schema.MessageType) + } + var arg6 *schema.Status + if args[6] != nil { + arg6 = args[6].(*schema.Status) + } + var arg7 uint32 + if args[7] != nil { + arg7 = args[7].(uint32) + } + var arg8 uint32 + if args[8] != nil { + arg8 = args[8].(uint32) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8, + ) + }) + return _c +} + +func (_c *MockSchemaService_List_Call) Return(schemas []*schema.Schema, n int64, err error) *MockSchemaService_List_Call { + _c.Call.Return(schemas, n, err) + return _c +} + +func (_c *MockSchemaService_List_Call) RunAndReturn(run func(ctx context.Context, version schema.OCPPVersion, vendor *string, model *string, action *string, msgType *schema.MessageType, status *schema.Status, limit uint32, offset uint32) ([]*schema.Schema, int64, error)) *MockSchemaService_List_Call { + _c.Call.Return(run) + return _c +} + +// ListVendorModels provides a mock function for the type MockSchemaService +func (_mock *MockSchemaService) ListVendorModels(ctx context.Context, vendor string, models []string, limit uint32, offset uint32) ([]*schema.VendorModel, int64, error) { + ret := _mock.Called(ctx, vendor, models, limit, offset) + + if len(ret) == 0 { + panic("no return value specified for ListVendorModels") + } + + var r0 []*schema.VendorModel + var r1 int64 + var r2 error + if returnFunc, ok := ret.Get(0).(func(context.Context, string, []string, uint32, uint32) ([]*schema.VendorModel, int64, error)); ok { + return returnFunc(ctx, vendor, models, limit, offset) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, string, []string, uint32, uint32) []*schema.VendorModel); ok { + r0 = returnFunc(ctx, vendor, models, limit, offset) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*schema.VendorModel) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, string, []string, uint32, uint32) int64); ok { + r1 = returnFunc(ctx, vendor, models, limit, offset) + } else { + r1 = ret.Get(1).(int64) + } + if returnFunc, ok := ret.Get(2).(func(context.Context, string, []string, uint32, uint32) error); ok { + r2 = returnFunc(ctx, vendor, models, limit, offset) + } else { + r2 = ret.Error(2) + } + return r0, r1, r2 +} + +// MockSchemaService_ListVendorModels_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListVendorModels' +type MockSchemaService_ListVendorModels_Call struct { + *mock.Call +} + +// ListVendorModels is a helper method to define mock.On call +// - ctx context.Context +// - vendor string +// - models []string +// - limit uint32 +// - offset uint32 +func (_e *MockSchemaService_Expecter) ListVendorModels(ctx interface{}, vendor interface{}, models interface{}, limit interface{}, offset interface{}) *MockSchemaService_ListVendorModels_Call { + return &MockSchemaService_ListVendorModels_Call{Call: _e.mock.On("ListVendorModels", ctx, vendor, models, limit, offset)} +} + +func (_c *MockSchemaService_ListVendorModels_Call) Run(run func(ctx context.Context, vendor string, models []string, limit uint32, offset uint32)) *MockSchemaService_ListVendorModels_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 string + if args[1] != nil { + arg1 = args[1].(string) + } + var arg2 []string + if args[2] != nil { + arg2 = args[2].([]string) + } + var arg3 uint32 + if args[3] != nil { + arg3 = args[3].(uint32) + } + var arg4 uint32 + if args[4] != nil { + arg4 = args[4].(uint32) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + ) + }) + return _c +} + +func (_c *MockSchemaService_ListVendorModels_Call) Return(vendorModels []*schema.VendorModel, n int64, err error) *MockSchemaService_ListVendorModels_Call { + _c.Call.Return(vendorModels, n, err) + return _c +} + +func (_c *MockSchemaService_ListVendorModels_Call) RunAndReturn(run func(ctx context.Context, vendor string, models []string, limit uint32, offset uint32) ([]*schema.VendorModel, int64, error)) *MockSchemaService_ListVendorModels_Call { + _c.Call.Return(run) + return _c +} + +// Upsert provides a mock function for the type MockSchemaService +func (_mock *MockSchemaService) Upsert(ctx context.Context, s *schema.Schema) error { + ret := _mock.Called(ctx, s) + + if len(ret) == 0 { + panic("no return value specified for Upsert") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, *schema.Schema) error); ok { + r0 = returnFunc(ctx, s) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// MockSchemaService_Upsert_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Upsert' +type MockSchemaService_Upsert_Call struct { + *mock.Call +} + +// Upsert is a helper method to define mock.On call +// - ctx context.Context +// - s *schema.Schema +func (_e *MockSchemaService_Expecter) Upsert(ctx interface{}, s interface{}) *MockSchemaService_Upsert_Call { + return &MockSchemaService_Upsert_Call{Call: _e.mock.On("Upsert", ctx, s)} +} + +func (_c *MockSchemaService_Upsert_Call) Run(run func(ctx context.Context, s *schema.Schema)) *MockSchemaService_Upsert_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 *schema.Schema + if args[1] != nil { + arg1 = args[1].(*schema.Schema) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *MockSchemaService_Upsert_Call) Return(err error) *MockSchemaService_Upsert_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockSchemaService_Upsert_Call) RunAndReturn(run func(ctx context.Context, s *schema.Schema) error) *MockSchemaService_Upsert_Call { + _c.Call.Return(run) + return _c +} diff --git a/gen/mocks/grpc/validation_service_mock.go b/gen/mocks/grpc/validation_service_mock.go new file mode 100644 index 0000000..efc4689 --- /dev/null +++ b/gen/mocks/grpc/validation_service_mock.go @@ -0,0 +1,175 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package grpc + +import ( + "context" + + "github.com/ChargePi/chargeflow-registry/internal/validation" + mock "github.com/stretchr/testify/mock" +) + +// NewMockValidationService creates a new instance of MockValidationService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockValidationService(t interface { + mock.TestingT + Cleanup(func()) +}) *MockValidationService { + mock := &MockValidationService{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// MockValidationService is an autogenerated mock type for the ValidationService type +type MockValidationService struct { + mock.Mock +} + +type MockValidationService_Expecter struct { + mock *mock.Mock +} + +func (_m *MockValidationService) EXPECT() *MockValidationService_Expecter { + return &MockValidationService_Expecter{mock: &_m.Mock} +} + +// ValidateMessage provides a mock function for the type MockValidationService +func (_mock *MockValidationService) ValidateMessage(ctx context.Context, req validation.Request) (*validation.Result, error) { + ret := _mock.Called(ctx, req) + + if len(ret) == 0 { + panic("no return value specified for ValidateMessage") + } + + var r0 *validation.Result + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, validation.Request) (*validation.Result, error)); ok { + return returnFunc(ctx, req) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, validation.Request) *validation.Result); ok { + r0 = returnFunc(ctx, req) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*validation.Result) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, validation.Request) error); ok { + r1 = returnFunc(ctx, req) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockValidationService_ValidateMessage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ValidateMessage' +type MockValidationService_ValidateMessage_Call struct { + *mock.Call +} + +// ValidateMessage is a helper method to define mock.On call +// - ctx context.Context +// - req validation.Request +func (_e *MockValidationService_Expecter) ValidateMessage(ctx interface{}, req interface{}) *MockValidationService_ValidateMessage_Call { + return &MockValidationService_ValidateMessage_Call{Call: _e.mock.On("ValidateMessage", ctx, req)} +} + +func (_c *MockValidationService_ValidateMessage_Call) Run(run func(ctx context.Context, req validation.Request)) *MockValidationService_ValidateMessage_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 validation.Request + if args[1] != nil { + arg1 = args[1].(validation.Request) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *MockValidationService_ValidateMessage_Call) Return(result *validation.Result, err error) *MockValidationService_ValidateMessage_Call { + _c.Call.Return(result, err) + return _c +} + +func (_c *MockValidationService_ValidateMessage_Call) RunAndReturn(run func(ctx context.Context, req validation.Request) (*validation.Result, error)) *MockValidationService_ValidateMessage_Call { + _c.Call.Return(run) + return _c +} + +// ValidateMessages provides a mock function for the type MockValidationService +func (_mock *MockValidationService) ValidateMessages(ctx context.Context, req validation.BulkRequest) ([]validation.ReportEntry, error) { + ret := _mock.Called(ctx, req) + + if len(ret) == 0 { + panic("no return value specified for ValidateMessages") + } + + var r0 []validation.ReportEntry + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, validation.BulkRequest) ([]validation.ReportEntry, error)); ok { + return returnFunc(ctx, req) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, validation.BulkRequest) []validation.ReportEntry); ok { + r0 = returnFunc(ctx, req) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]validation.ReportEntry) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, validation.BulkRequest) error); ok { + r1 = returnFunc(ctx, req) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockValidationService_ValidateMessages_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ValidateMessages' +type MockValidationService_ValidateMessages_Call struct { + *mock.Call +} + +// ValidateMessages is a helper method to define mock.On call +// - ctx context.Context +// - req validation.BulkRequest +func (_e *MockValidationService_Expecter) ValidateMessages(ctx interface{}, req interface{}) *MockValidationService_ValidateMessages_Call { + return &MockValidationService_ValidateMessages_Call{Call: _e.mock.On("ValidateMessages", ctx, req)} +} + +func (_c *MockValidationService_ValidateMessages_Call) Run(run func(ctx context.Context, req validation.BulkRequest)) *MockValidationService_ValidateMessages_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 validation.BulkRequest + if args[1] != nil { + arg1 = args[1].(validation.BulkRequest) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *MockValidationService_ValidateMessages_Call) Return(reportEntrys []validation.ReportEntry, err error) *MockValidationService_ValidateMessages_Call { + _c.Call.Return(reportEntrys, err) + return _c +} + +func (_c *MockValidationService_ValidateMessages_Call) RunAndReturn(run func(ctx context.Context, req validation.BulkRequest) ([]validation.ReportEntry, error)) *MockValidationService_ValidateMessages_Call { + _c.Call.Return(run) + return _c +} diff --git a/gen/mocks/schema/cache_mock.go b/gen/mocks/schema/cache_mock.go new file mode 100644 index 0000000..aba0315 --- /dev/null +++ b/gen/mocks/schema/cache_mock.go @@ -0,0 +1,227 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package schema + +import ( + "context" + + "github.com/ChargePi/chargeflow-registry/internal/schema" + mock "github.com/stretchr/testify/mock" +) + +// NewMockCache creates a new instance of MockCache. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockCache(t interface { + mock.TestingT + Cleanup(func()) +}) *MockCache { + mock := &MockCache{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// MockCache is an autogenerated mock type for the Cache type +type MockCache struct { + mock.Mock +} + +type MockCache_Expecter struct { + mock *mock.Mock +} + +func (_m *MockCache) EXPECT() *MockCache_Expecter { + return &MockCache_Expecter{mock: &_m.Mock} +} + +// Delete provides a mock function for the type MockCache +func (_mock *MockCache) Delete(ctx context.Context, key string) error { + ret := _mock.Called(ctx, key) + + if len(ret) == 0 { + panic("no return value specified for Delete") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = returnFunc(ctx, key) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// MockCache_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' +type MockCache_Delete_Call struct { + *mock.Call +} + +// Delete is a helper method to define mock.On call +// - ctx context.Context +// - key string +func (_e *MockCache_Expecter) Delete(ctx interface{}, key interface{}) *MockCache_Delete_Call { + return &MockCache_Delete_Call{Call: _e.mock.On("Delete", ctx, key)} +} + +func (_c *MockCache_Delete_Call) Run(run func(ctx context.Context, key string)) *MockCache_Delete_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 string + if args[1] != nil { + arg1 = args[1].(string) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *MockCache_Delete_Call) Return(err error) *MockCache_Delete_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockCache_Delete_Call) RunAndReturn(run func(ctx context.Context, key string) error) *MockCache_Delete_Call { + _c.Call.Return(run) + return _c +} + +// Get provides a mock function for the type MockCache +func (_mock *MockCache) Get(ctx context.Context, key string) (*schema.Schema, error) { + ret := _mock.Called(ctx, key) + + if len(ret) == 0 { + panic("no return value specified for Get") + } + + var r0 *schema.Schema + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, string) (*schema.Schema, error)); ok { + return returnFunc(ctx, key) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, string) *schema.Schema); ok { + r0 = returnFunc(ctx, key) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*schema.Schema) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = returnFunc(ctx, key) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockCache_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' +type MockCache_Get_Call struct { + *mock.Call +} + +// Get is a helper method to define mock.On call +// - ctx context.Context +// - key string +func (_e *MockCache_Expecter) Get(ctx interface{}, key interface{}) *MockCache_Get_Call { + return &MockCache_Get_Call{Call: _e.mock.On("Get", ctx, key)} +} + +func (_c *MockCache_Get_Call) Run(run func(ctx context.Context, key string)) *MockCache_Get_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 string + if args[1] != nil { + arg1 = args[1].(string) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *MockCache_Get_Call) Return(schema1 *schema.Schema, err error) *MockCache_Get_Call { + _c.Call.Return(schema1, err) + return _c +} + +func (_c *MockCache_Get_Call) RunAndReturn(run func(ctx context.Context, key string) (*schema.Schema, error)) *MockCache_Get_Call { + _c.Call.Return(run) + return _c +} + +// Set provides a mock function for the type MockCache +func (_mock *MockCache) Set(ctx context.Context, key string, schema1 *schema.Schema) error { + ret := _mock.Called(ctx, key, schema1) + + if len(ret) == 0 { + panic("no return value specified for Set") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, string, *schema.Schema) error); ok { + r0 = returnFunc(ctx, key, schema1) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// MockCache_Set_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Set' +type MockCache_Set_Call struct { + *mock.Call +} + +// Set is a helper method to define mock.On call +// - ctx context.Context +// - key string +// - schema1 *schema.Schema +func (_e *MockCache_Expecter) Set(ctx interface{}, key interface{}, schema1 interface{}) *MockCache_Set_Call { + return &MockCache_Set_Call{Call: _e.mock.On("Set", ctx, key, schema1)} +} + +func (_c *MockCache_Set_Call) Run(run func(ctx context.Context, key string, schema1 *schema.Schema)) *MockCache_Set_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 string + if args[1] != nil { + arg1 = args[1].(string) + } + var arg2 *schema.Schema + if args[2] != nil { + arg2 = args[2].(*schema.Schema) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MockCache_Set_Call) Return(err error) *MockCache_Set_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockCache_Set_Call) RunAndReturn(run func(ctx context.Context, key string, schema1 *schema.Schema) error) *MockCache_Set_Call { + _c.Call.Return(run) + return _c +} diff --git a/gen/mocks/schema/repository_mock.go b/gen/mocks/schema/repository_mock.go new file mode 100644 index 0000000..864c630 --- /dev/null +++ b/gen/mocks/schema/repository_mock.go @@ -0,0 +1,609 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package schema + +import ( + "context" + + "github.com/ChargePi/chargeflow-registry/internal/schema" + "github.com/google/uuid" + mock "github.com/stretchr/testify/mock" +) + +// NewMockRepository creates a new instance of MockRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockRepository(t interface { + mock.TestingT + Cleanup(func()) +}) *MockRepository { + mock := &MockRepository{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// MockRepository is an autogenerated mock type for the Repository type +type MockRepository struct { + mock.Mock +} + +type MockRepository_Expecter struct { + mock *mock.Mock +} + +func (_m *MockRepository) EXPECT() *MockRepository_Expecter { + return &MockRepository_Expecter{mock: &_m.Mock} +} + +// Add provides a mock function for the type MockRepository +func (_mock *MockRepository) Add(ctx context.Context, schema1 *schema.Schema) error { + ret := _mock.Called(ctx, schema1) + + if len(ret) == 0 { + panic("no return value specified for Add") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, *schema.Schema) error); ok { + r0 = returnFunc(ctx, schema1) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// MockRepository_Add_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Add' +type MockRepository_Add_Call struct { + *mock.Call +} + +// Add is a helper method to define mock.On call +// - ctx context.Context +// - schema1 *schema.Schema +func (_e *MockRepository_Expecter) Add(ctx interface{}, schema1 interface{}) *MockRepository_Add_Call { + return &MockRepository_Add_Call{Call: _e.mock.On("Add", ctx, schema1)} +} + +func (_c *MockRepository_Add_Call) Run(run func(ctx context.Context, schema1 *schema.Schema)) *MockRepository_Add_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 *schema.Schema + if args[1] != nil { + arg1 = args[1].(*schema.Schema) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *MockRepository_Add_Call) Return(err error) *MockRepository_Add_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockRepository_Add_Call) RunAndReturn(run func(ctx context.Context, schema1 *schema.Schema) error) *MockRepository_Add_Call { + _c.Call.Return(run) + return _c +} + +// Delete provides a mock function for the type MockRepository +func (_mock *MockRepository) Delete(ctx context.Context, version schema.OCPPVersion, action string, msgType schema.MessageType, vendor *string, model *string) error { + ret := _mock.Called(ctx, version, action, msgType, vendor, model) + + if len(ret) == 0 { + panic("no return value specified for Delete") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, schema.OCPPVersion, string, schema.MessageType, *string, *string) error); ok { + r0 = returnFunc(ctx, version, action, msgType, vendor, model) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// MockRepository_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' +type MockRepository_Delete_Call struct { + *mock.Call +} + +// Delete is a helper method to define mock.On call +// - ctx context.Context +// - version schema.OCPPVersion +// - action string +// - msgType schema.MessageType +// - vendor *string +// - model *string +func (_e *MockRepository_Expecter) Delete(ctx interface{}, version interface{}, action interface{}, msgType interface{}, vendor interface{}, model interface{}) *MockRepository_Delete_Call { + return &MockRepository_Delete_Call{Call: _e.mock.On("Delete", ctx, version, action, msgType, vendor, model)} +} + +func (_c *MockRepository_Delete_Call) Run(run func(ctx context.Context, version schema.OCPPVersion, action string, msgType schema.MessageType, vendor *string, model *string)) *MockRepository_Delete_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 schema.OCPPVersion + if args[1] != nil { + arg1 = args[1].(schema.OCPPVersion) + } + var arg2 string + if args[2] != nil { + arg2 = args[2].(string) + } + var arg3 schema.MessageType + if args[3] != nil { + arg3 = args[3].(schema.MessageType) + } + var arg4 *string + if args[4] != nil { + arg4 = args[4].(*string) + } + var arg5 *string + if args[5] != nil { + arg5 = args[5].(*string) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + ) + }) + return _c +} + +func (_c *MockRepository_Delete_Call) Return(err error) *MockRepository_Delete_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockRepository_Delete_Call) RunAndReturn(run func(ctx context.Context, version schema.OCPPVersion, action string, msgType schema.MessageType, vendor *string, model *string) error) *MockRepository_Delete_Call { + _c.Call.Return(run) + return _c +} + +// Get provides a mock function for the type MockRepository +func (_mock *MockRepository) Get(ctx context.Context, version schema.OCPPVersion, action string, msgType schema.MessageType, vendor *string, model *string) (*schema.Schema, error) { + ret := _mock.Called(ctx, version, action, msgType, vendor, model) + + if len(ret) == 0 { + panic("no return value specified for Get") + } + + var r0 *schema.Schema + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, schema.OCPPVersion, string, schema.MessageType, *string, *string) (*schema.Schema, error)); ok { + return returnFunc(ctx, version, action, msgType, vendor, model) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, schema.OCPPVersion, string, schema.MessageType, *string, *string) *schema.Schema); ok { + r0 = returnFunc(ctx, version, action, msgType, vendor, model) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*schema.Schema) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, schema.OCPPVersion, string, schema.MessageType, *string, *string) error); ok { + r1 = returnFunc(ctx, version, action, msgType, vendor, model) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockRepository_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' +type MockRepository_Get_Call struct { + *mock.Call +} + +// Get is a helper method to define mock.On call +// - ctx context.Context +// - version schema.OCPPVersion +// - action string +// - msgType schema.MessageType +// - vendor *string +// - model *string +func (_e *MockRepository_Expecter) Get(ctx interface{}, version interface{}, action interface{}, msgType interface{}, vendor interface{}, model interface{}) *MockRepository_Get_Call { + return &MockRepository_Get_Call{Call: _e.mock.On("Get", ctx, version, action, msgType, vendor, model)} +} + +func (_c *MockRepository_Get_Call) Run(run func(ctx context.Context, version schema.OCPPVersion, action string, msgType schema.MessageType, vendor *string, model *string)) *MockRepository_Get_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 schema.OCPPVersion + if args[1] != nil { + arg1 = args[1].(schema.OCPPVersion) + } + var arg2 string + if args[2] != nil { + arg2 = args[2].(string) + } + var arg3 schema.MessageType + if args[3] != nil { + arg3 = args[3].(schema.MessageType) + } + var arg4 *string + if args[4] != nil { + arg4 = args[4].(*string) + } + var arg5 *string + if args[5] != nil { + arg5 = args[5].(*string) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + ) + }) + return _c +} + +func (_c *MockRepository_Get_Call) Return(schema1 *schema.Schema, err error) *MockRepository_Get_Call { + _c.Call.Return(schema1, err) + return _c +} + +func (_c *MockRepository_Get_Call) RunAndReturn(run func(ctx context.Context, version schema.OCPPVersion, action string, msgType schema.MessageType, vendor *string, model *string) (*schema.Schema, error)) *MockRepository_Get_Call { + _c.Call.Return(run) + return _c +} + +// List provides a mock function for the type MockRepository +func (_mock *MockRepository) List(ctx context.Context, version schema.OCPPVersion, vendor *string, model *string, action *string, msgType *schema.MessageType, status *schema.Status, limit uint32, offset uint32) ([]*schema.Schema, int64, error) { + ret := _mock.Called(ctx, version, vendor, model, action, msgType, status, limit, offset) + + if len(ret) == 0 { + panic("no return value specified for List") + } + + var r0 []*schema.Schema + var r1 int64 + var r2 error + if returnFunc, ok := ret.Get(0).(func(context.Context, schema.OCPPVersion, *string, *string, *string, *schema.MessageType, *schema.Status, uint32, uint32) ([]*schema.Schema, int64, error)); ok { + return returnFunc(ctx, version, vendor, model, action, msgType, status, limit, offset) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, schema.OCPPVersion, *string, *string, *string, *schema.MessageType, *schema.Status, uint32, uint32) []*schema.Schema); ok { + r0 = returnFunc(ctx, version, vendor, model, action, msgType, status, limit, offset) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*schema.Schema) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, schema.OCPPVersion, *string, *string, *string, *schema.MessageType, *schema.Status, uint32, uint32) int64); ok { + r1 = returnFunc(ctx, version, vendor, model, action, msgType, status, limit, offset) + } else { + r1 = ret.Get(1).(int64) + } + if returnFunc, ok := ret.Get(2).(func(context.Context, schema.OCPPVersion, *string, *string, *string, *schema.MessageType, *schema.Status, uint32, uint32) error); ok { + r2 = returnFunc(ctx, version, vendor, model, action, msgType, status, limit, offset) + } else { + r2 = ret.Error(2) + } + return r0, r1, r2 +} + +// MockRepository_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' +type MockRepository_List_Call struct { + *mock.Call +} + +// List is a helper method to define mock.On call +// - ctx context.Context +// - version schema.OCPPVersion +// - vendor *string +// - model *string +// - action *string +// - msgType *schema.MessageType +// - status *schema.Status +// - limit uint32 +// - offset uint32 +func (_e *MockRepository_Expecter) List(ctx interface{}, version interface{}, vendor interface{}, model interface{}, action interface{}, msgType interface{}, status interface{}, limit interface{}, offset interface{}) *MockRepository_List_Call { + return &MockRepository_List_Call{Call: _e.mock.On("List", ctx, version, vendor, model, action, msgType, status, limit, offset)} +} + +func (_c *MockRepository_List_Call) Run(run func(ctx context.Context, version schema.OCPPVersion, vendor *string, model *string, action *string, msgType *schema.MessageType, status *schema.Status, limit uint32, offset uint32)) *MockRepository_List_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 schema.OCPPVersion + if args[1] != nil { + arg1 = args[1].(schema.OCPPVersion) + } + var arg2 *string + if args[2] != nil { + arg2 = args[2].(*string) + } + var arg3 *string + if args[3] != nil { + arg3 = args[3].(*string) + } + var arg4 *string + if args[4] != nil { + arg4 = args[4].(*string) + } + var arg5 *schema.MessageType + if args[5] != nil { + arg5 = args[5].(*schema.MessageType) + } + var arg6 *schema.Status + if args[6] != nil { + arg6 = args[6].(*schema.Status) + } + var arg7 uint32 + if args[7] != nil { + arg7 = args[7].(uint32) + } + var arg8 uint32 + if args[8] != nil { + arg8 = args[8].(uint32) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8, + ) + }) + return _c +} + +func (_c *MockRepository_List_Call) Return(schemas []*schema.Schema, n int64, err error) *MockRepository_List_Call { + _c.Call.Return(schemas, n, err) + return _c +} + +func (_c *MockRepository_List_Call) RunAndReturn(run func(ctx context.Context, version schema.OCPPVersion, vendor *string, model *string, action *string, msgType *schema.MessageType, status *schema.Status, limit uint32, offset uint32) ([]*schema.Schema, int64, error)) *MockRepository_List_Call { + _c.Call.Return(run) + return _c +} + +// ListVendorModels provides a mock function for the type MockRepository +func (_mock *MockRepository) ListVendorModels(ctx context.Context, vendor string, models []string, limit uint32, offset uint32) ([]*schema.VendorModel, int64, error) { + ret := _mock.Called(ctx, vendor, models, limit, offset) + + if len(ret) == 0 { + panic("no return value specified for ListVendorModels") + } + + var r0 []*schema.VendorModel + var r1 int64 + var r2 error + if returnFunc, ok := ret.Get(0).(func(context.Context, string, []string, uint32, uint32) ([]*schema.VendorModel, int64, error)); ok { + return returnFunc(ctx, vendor, models, limit, offset) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, string, []string, uint32, uint32) []*schema.VendorModel); ok { + r0 = returnFunc(ctx, vendor, models, limit, offset) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*schema.VendorModel) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, string, []string, uint32, uint32) int64); ok { + r1 = returnFunc(ctx, vendor, models, limit, offset) + } else { + r1 = ret.Get(1).(int64) + } + if returnFunc, ok := ret.Get(2).(func(context.Context, string, []string, uint32, uint32) error); ok { + r2 = returnFunc(ctx, vendor, models, limit, offset) + } else { + r2 = ret.Error(2) + } + return r0, r1, r2 +} + +// MockRepository_ListVendorModels_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListVendorModels' +type MockRepository_ListVendorModels_Call struct { + *mock.Call +} + +// ListVendorModels is a helper method to define mock.On call +// - ctx context.Context +// - vendor string +// - models []string +// - limit uint32 +// - offset uint32 +func (_e *MockRepository_Expecter) ListVendorModels(ctx interface{}, vendor interface{}, models interface{}, limit interface{}, offset interface{}) *MockRepository_ListVendorModels_Call { + return &MockRepository_ListVendorModels_Call{Call: _e.mock.On("ListVendorModels", ctx, vendor, models, limit, offset)} +} + +func (_c *MockRepository_ListVendorModels_Call) Run(run func(ctx context.Context, vendor string, models []string, limit uint32, offset uint32)) *MockRepository_ListVendorModels_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 string + if args[1] != nil { + arg1 = args[1].(string) + } + var arg2 []string + if args[2] != nil { + arg2 = args[2].([]string) + } + var arg3 uint32 + if args[3] != nil { + arg3 = args[3].(uint32) + } + var arg4 uint32 + if args[4] != nil { + arg4 = args[4].(uint32) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + ) + }) + return _c +} + +func (_c *MockRepository_ListVendorModels_Call) Return(vendorModels []*schema.VendorModel, n int64, err error) *MockRepository_ListVendorModels_Call { + _c.Call.Return(vendorModels, n, err) + return _c +} + +func (_c *MockRepository_ListVendorModels_Call) RunAndReturn(run func(ctx context.Context, vendor string, models []string, limit uint32, offset uint32) ([]*schema.VendorModel, int64, error)) *MockRepository_ListVendorModels_Call { + _c.Call.Return(run) + return _c +} + +// UpdateStatus provides a mock function for the type MockRepository +func (_mock *MockRepository) UpdateStatus(ctx context.Context, id uuid.UUID, status schema.Status) (*schema.Schema, error) { + ret := _mock.Called(ctx, id, status) + + if len(ret) == 0 { + panic("no return value specified for UpdateStatus") + } + + var r0 *schema.Schema + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, uuid.UUID, schema.Status) (*schema.Schema, error)); ok { + return returnFunc(ctx, id, status) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, uuid.UUID, schema.Status) *schema.Schema); ok { + r0 = returnFunc(ctx, id, status) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*schema.Schema) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, uuid.UUID, schema.Status) error); ok { + r1 = returnFunc(ctx, id, status) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockRepository_UpdateStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateStatus' +type MockRepository_UpdateStatus_Call struct { + *mock.Call +} + +// UpdateStatus is a helper method to define mock.On call +// - ctx context.Context +// - id uuid.UUID +// - status schema.Status +func (_e *MockRepository_Expecter) UpdateStatus(ctx interface{}, id interface{}, status interface{}) *MockRepository_UpdateStatus_Call { + return &MockRepository_UpdateStatus_Call{Call: _e.mock.On("UpdateStatus", ctx, id, status)} +} + +func (_c *MockRepository_UpdateStatus_Call) Run(run func(ctx context.Context, id uuid.UUID, status schema.Status)) *MockRepository_UpdateStatus_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 uuid.UUID + if args[1] != nil { + arg1 = args[1].(uuid.UUID) + } + var arg2 schema.Status + if args[2] != nil { + arg2 = args[2].(schema.Status) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MockRepository_UpdateStatus_Call) Return(schema1 *schema.Schema, err error) *MockRepository_UpdateStatus_Call { + _c.Call.Return(schema1, err) + return _c +} + +func (_c *MockRepository_UpdateStatus_Call) RunAndReturn(run func(ctx context.Context, id uuid.UUID, status schema.Status) (*schema.Schema, error)) *MockRepository_UpdateStatus_Call { + _c.Call.Return(run) + return _c +} + +// Upsert provides a mock function for the type MockRepository +func (_mock *MockRepository) Upsert(ctx context.Context, schema1 *schema.Schema) error { + ret := _mock.Called(ctx, schema1) + + if len(ret) == 0 { + panic("no return value specified for Upsert") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, *schema.Schema) error); ok { + r0 = returnFunc(ctx, schema1) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// MockRepository_Upsert_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Upsert' +type MockRepository_Upsert_Call struct { + *mock.Call +} + +// Upsert is a helper method to define mock.On call +// - ctx context.Context +// - schema1 *schema.Schema +func (_e *MockRepository_Expecter) Upsert(ctx interface{}, schema1 interface{}) *MockRepository_Upsert_Call { + return &MockRepository_Upsert_Call{Call: _e.mock.On("Upsert", ctx, schema1)} +} + +func (_c *MockRepository_Upsert_Call) Run(run func(ctx context.Context, schema1 *schema.Schema)) *MockRepository_Upsert_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 *schema.Schema + if args[1] != nil { + arg1 = args[1].(*schema.Schema) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *MockRepository_Upsert_Call) Return(err error) *MockRepository_Upsert_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockRepository_Upsert_Call) RunAndReturn(run func(ctx context.Context, schema1 *schema.Schema) error) *MockRepository_Upsert_Call { + _c.Call.Return(run) + return _c +} diff --git a/gen/mocks/validation/schema_getter_mock.go b/gen/mocks/validation/schema_getter_mock.go new file mode 100644 index 0000000..7291045 --- /dev/null +++ b/gen/mocks/validation/schema_getter_mock.go @@ -0,0 +1,131 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package validation + +import ( + "context" + + "github.com/ChargePi/chargeflow-registry/internal/schema" + mock "github.com/stretchr/testify/mock" +) + +// NewMockSchemaGetter creates a new instance of MockSchemaGetter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockSchemaGetter(t interface { + mock.TestingT + Cleanup(func()) +}) *MockSchemaGetter { + mock := &MockSchemaGetter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// MockSchemaGetter is an autogenerated mock type for the SchemaGetter type +type MockSchemaGetter struct { + mock.Mock +} + +type MockSchemaGetter_Expecter struct { + mock *mock.Mock +} + +func (_m *MockSchemaGetter) EXPECT() *MockSchemaGetter_Expecter { + return &MockSchemaGetter_Expecter{mock: &_m.Mock} +} + +// Get provides a mock function for the type MockSchemaGetter +func (_mock *MockSchemaGetter) Get(ctx context.Context, version schema.OCPPVersion, action string, msgType schema.MessageType, vendor *string, model *string) (*schema.Schema, error) { + ret := _mock.Called(ctx, version, action, msgType, vendor, model) + + if len(ret) == 0 { + panic("no return value specified for Get") + } + + var r0 *schema.Schema + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, schema.OCPPVersion, string, schema.MessageType, *string, *string) (*schema.Schema, error)); ok { + return returnFunc(ctx, version, action, msgType, vendor, model) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, schema.OCPPVersion, string, schema.MessageType, *string, *string) *schema.Schema); ok { + r0 = returnFunc(ctx, version, action, msgType, vendor, model) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*schema.Schema) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, schema.OCPPVersion, string, schema.MessageType, *string, *string) error); ok { + r1 = returnFunc(ctx, version, action, msgType, vendor, model) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockSchemaGetter_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' +type MockSchemaGetter_Get_Call struct { + *mock.Call +} + +// Get is a helper method to define mock.On call +// - ctx context.Context +// - version schema.OCPPVersion +// - action string +// - msgType schema.MessageType +// - vendor *string +// - model *string +func (_e *MockSchemaGetter_Expecter) Get(ctx interface{}, version interface{}, action interface{}, msgType interface{}, vendor interface{}, model interface{}) *MockSchemaGetter_Get_Call { + return &MockSchemaGetter_Get_Call{Call: _e.mock.On("Get", ctx, version, action, msgType, vendor, model)} +} + +func (_c *MockSchemaGetter_Get_Call) Run(run func(ctx context.Context, version schema.OCPPVersion, action string, msgType schema.MessageType, vendor *string, model *string)) *MockSchemaGetter_Get_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 schema.OCPPVersion + if args[1] != nil { + arg1 = args[1].(schema.OCPPVersion) + } + var arg2 string + if args[2] != nil { + arg2 = args[2].(string) + } + var arg3 schema.MessageType + if args[3] != nil { + arg3 = args[3].(schema.MessageType) + } + var arg4 *string + if args[4] != nil { + arg4 = args[4].(*string) + } + var arg5 *string + if args[5] != nil { + arg5 = args[5].(*string) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + ) + }) + return _c +} + +func (_c *MockSchemaGetter_Get_Call) Return(schema1 *schema.Schema, err error) *MockSchemaGetter_Get_Call { + _c.Call.Return(schema1, err) + return _c +} + +func (_c *MockSchemaGetter_Get_Call) RunAndReturn(run func(ctx context.Context, version schema.OCPPVersion, action string, msgType schema.MessageType, vendor *string, model *string) (*schema.Schema, error)) *MockSchemaGetter_Get_Call { + _c.Call.Return(run) + return _c +} diff --git a/gen/proto/schema/v1/validation.pb.go b/gen/proto/schema/v1/validation.pb.go index 0093f2d..37b30fc 100644 --- a/gen/proto/schema/v1/validation.pb.go +++ b/gen/proto/schema/v1/validation.pb.go @@ -209,6 +209,183 @@ func (x *ValidateMessageResponse) GetViolations() []*ValidationViolation { return nil } +type BulkValidateMessagesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + OcppVersion OcppVersion `protobuf:"varint,1,opt,name=ocpp_version,json=ocppVersion,proto3,enum=schema.v1.OcppVersion" json:"ocpp_version,omitempty"` + Vendor *string `protobuf:"bytes,2,opt,name=vendor,proto3,oneof" json:"vendor,omitempty"` + Model *string `protobuf:"bytes,3,opt,name=model,proto3,oneof" json:"model,omitempty"` + // OCPP-J messages as JSON objects ({"messageTypeId", "uniqueId", "action", + // "payload"}); action and message type are read from each message rather + // than supplied once for the whole batch. Validated against the schema + // they identify and returned in the same order as supplied here. + Messages []string `protobuf:"bytes,4,rep,name=messages,proto3" json:"messages,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BulkValidateMessagesRequest) Reset() { + *x = BulkValidateMessagesRequest{} + mi := &file_schema_v1_validation_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BulkValidateMessagesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BulkValidateMessagesRequest) ProtoMessage() {} + +func (x *BulkValidateMessagesRequest) ProtoReflect() protoreflect.Message { + mi := &file_schema_v1_validation_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BulkValidateMessagesRequest.ProtoReflect.Descriptor instead. +func (*BulkValidateMessagesRequest) Descriptor() ([]byte, []int) { + return file_schema_v1_validation_proto_rawDescGZIP(), []int{3} +} + +func (x *BulkValidateMessagesRequest) GetOcppVersion() OcppVersion { + if x != nil { + return x.OcppVersion + } + return OcppVersion_OCPP_VERSION_UNSPECIFIED +} + +func (x *BulkValidateMessagesRequest) GetVendor() string { + if x != nil && x.Vendor != nil { + return *x.Vendor + } + return "" +} + +func (x *BulkValidateMessagesRequest) GetModel() string { + if x != nil && x.Model != nil { + return *x.Model + } + return "" +} + +func (x *BulkValidateMessagesRequest) GetMessages() []string { + if x != nil { + return x.Messages + } + return nil +} + +type ValidationReportEntry struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Position of the corresponding message in the request's messages list. + Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` + Valid bool `protobuf:"varint,2,opt,name=valid,proto3" json:"valid,omitempty"` + Violations []*ValidationViolation `protobuf:"bytes,3,rep,name=violations,proto3" json:"violations,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ValidationReportEntry) Reset() { + *x = ValidationReportEntry{} + mi := &file_schema_v1_validation_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ValidationReportEntry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValidationReportEntry) ProtoMessage() {} + +func (x *ValidationReportEntry) ProtoReflect() protoreflect.Message { + mi := &file_schema_v1_validation_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValidationReportEntry.ProtoReflect.Descriptor instead. +func (*ValidationReportEntry) Descriptor() ([]byte, []int) { + return file_schema_v1_validation_proto_rawDescGZIP(), []int{4} +} + +func (x *ValidationReportEntry) GetIndex() int32 { + if x != nil { + return x.Index + } + return 0 +} + +func (x *ValidationReportEntry) GetValid() bool { + if x != nil { + return x.Valid + } + return false +} + +func (x *ValidationReportEntry) GetViolations() []*ValidationViolation { + if x != nil { + return x.Violations + } + return nil +} + +type BulkValidateMessagesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Results []*ValidationReportEntry `protobuf:"bytes,1,rep,name=results,proto3" json:"results,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BulkValidateMessagesResponse) Reset() { + *x = BulkValidateMessagesResponse{} + mi := &file_schema_v1_validation_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BulkValidateMessagesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BulkValidateMessagesResponse) ProtoMessage() {} + +func (x *BulkValidateMessagesResponse) ProtoReflect() protoreflect.Message { + mi := &file_schema_v1_validation_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BulkValidateMessagesResponse.ProtoReflect.Descriptor instead. +func (*BulkValidateMessagesResponse) Descriptor() ([]byte, []int) { + return file_schema_v1_validation_proto_rawDescGZIP(), []int{5} +} + +func (x *BulkValidateMessagesResponse) GetResults() []*ValidationReportEntry { + if x != nil { + return x.Results + } + return nil +} + var File_schema_v1_validation_proto protoreflect.FileDescriptor const file_schema_v1_validation_proto_rawDesc = "" + @@ -230,9 +407,25 @@ const file_schema_v1_validation_proto_rawDesc = "" + "\x05valid\x18\x01 \x01(\bR\x05valid\x12>\n" + "\n" + "violations\x18\x02 \x03(\v2\x1e.schema.v1.ValidationViolationR\n" + - "violations2s\n" + + "violations\"\xc1\x01\n" + + "\x1bBulkValidateMessagesRequest\x129\n" + + "\focpp_version\x18\x01 \x01(\x0e2\x16.schema.v1.OcppVersionR\vocppVersion\x12\x1b\n" + + "\x06vendor\x18\x02 \x01(\tH\x00R\x06vendor\x88\x01\x01\x12\x19\n" + + "\x05model\x18\x03 \x01(\tH\x01R\x05model\x88\x01\x01\x12\x1a\n" + + "\bmessages\x18\x04 \x03(\tR\bmessagesB\t\n" + + "\a_vendorB\b\n" + + "\x06_model\"\x83\x01\n" + + "\x15ValidationReportEntry\x12\x14\n" + + "\x05index\x18\x01 \x01(\x05R\x05index\x12\x14\n" + + "\x05valid\x18\x02 \x01(\bR\x05valid\x12>\n" + + "\n" + + "violations\x18\x03 \x03(\v2\x1e.schema.v1.ValidationViolationR\n" + + "violations\"Z\n" + + "\x1cBulkValidateMessagesResponse\x12:\n" + + "\aresults\x18\x01 \x03(\v2 .schema.v1.ValidationReportEntryR\aresults2\xdc\x01\n" + "\x17SchemaValidationService\x12X\n" + - "\x0fValidateMessage\x12!.schema.v1.ValidateMessageRequest\x1a\".schema.v1.ValidateMessageResponseB\xab\x01\n" + + "\x0fValidateMessage\x12!.schema.v1.ValidateMessageRequest\x1a\".schema.v1.ValidateMessageResponse\x12g\n" + + "\x14BulkValidateMessages\x12&.schema.v1.BulkValidateMessagesRequest\x1a'.schema.v1.BulkValidateMessagesResponseB\xab\x01\n" + "\rcom.schema.v1B\x0fValidationProtoP\x01ZDgithub.com/ChargePi/chargeflow-registry/gen/proto/schema/v1;schemav1\xa2\x02\x03SXX\xaa\x02\tSchema.V1\xca\x02\tSchema\\V1\xe2\x02\x15Schema\\V1\\GPBMetadata\xea\x02\n" + "Schema::V1b\x06proto3" @@ -248,25 +441,33 @@ func file_schema_v1_validation_proto_rawDescGZIP() []byte { return file_schema_v1_validation_proto_rawDescData } -var file_schema_v1_validation_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_schema_v1_validation_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_schema_v1_validation_proto_goTypes = []any{ - (*ValidationViolation)(nil), // 0: schema.v1.ValidationViolation - (*ValidateMessageRequest)(nil), // 1: schema.v1.ValidateMessageRequest - (*ValidateMessageResponse)(nil), // 2: schema.v1.ValidateMessageResponse - (OcppVersion)(0), // 3: schema.v1.OcppVersion - (MessageType)(0), // 4: schema.v1.MessageType + (*ValidationViolation)(nil), // 0: schema.v1.ValidationViolation + (*ValidateMessageRequest)(nil), // 1: schema.v1.ValidateMessageRequest + (*ValidateMessageResponse)(nil), // 2: schema.v1.ValidateMessageResponse + (*BulkValidateMessagesRequest)(nil), // 3: schema.v1.BulkValidateMessagesRequest + (*ValidationReportEntry)(nil), // 4: schema.v1.ValidationReportEntry + (*BulkValidateMessagesResponse)(nil), // 5: schema.v1.BulkValidateMessagesResponse + (OcppVersion)(0), // 6: schema.v1.OcppVersion + (MessageType)(0), // 7: schema.v1.MessageType } var file_schema_v1_validation_proto_depIdxs = []int32{ - 3, // 0: schema.v1.ValidateMessageRequest.ocpp_version:type_name -> schema.v1.OcppVersion - 4, // 1: schema.v1.ValidateMessageRequest.message_type:type_name -> schema.v1.MessageType + 6, // 0: schema.v1.ValidateMessageRequest.ocpp_version:type_name -> schema.v1.OcppVersion + 7, // 1: schema.v1.ValidateMessageRequest.message_type:type_name -> schema.v1.MessageType 0, // 2: schema.v1.ValidateMessageResponse.violations:type_name -> schema.v1.ValidationViolation - 1, // 3: schema.v1.SchemaValidationService.ValidateMessage:input_type -> schema.v1.ValidateMessageRequest - 2, // 4: schema.v1.SchemaValidationService.ValidateMessage:output_type -> schema.v1.ValidateMessageResponse - 4, // [4:5] is the sub-list for method output_type - 3, // [3:4] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 6, // 3: schema.v1.BulkValidateMessagesRequest.ocpp_version:type_name -> schema.v1.OcppVersion + 0, // 4: schema.v1.ValidationReportEntry.violations:type_name -> schema.v1.ValidationViolation + 4, // 5: schema.v1.BulkValidateMessagesResponse.results:type_name -> schema.v1.ValidationReportEntry + 1, // 6: schema.v1.SchemaValidationService.ValidateMessage:input_type -> schema.v1.ValidateMessageRequest + 3, // 7: schema.v1.SchemaValidationService.BulkValidateMessages:input_type -> schema.v1.BulkValidateMessagesRequest + 2, // 8: schema.v1.SchemaValidationService.ValidateMessage:output_type -> schema.v1.ValidateMessageResponse + 5, // 9: schema.v1.SchemaValidationService.BulkValidateMessages:output_type -> schema.v1.BulkValidateMessagesResponse + 8, // [8:10] is the sub-list for method output_type + 6, // [6:8] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_schema_v1_validation_proto_init() } @@ -276,13 +477,14 @@ func file_schema_v1_validation_proto_init() { } file_schema_v1_schema_proto_init() file_schema_v1_validation_proto_msgTypes[1].OneofWrappers = []any{} + file_schema_v1_validation_proto_msgTypes[3].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_schema_v1_validation_proto_rawDesc), len(file_schema_v1_validation_proto_rawDesc)), NumEnums: 0, - NumMessages: 3, + NumMessages: 6, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/proto/schema/v1/validation_grpc.pb.go b/gen/proto/schema/v1/validation_grpc.pb.go index c3fdcce..0f0ac7f 100644 --- a/gen/proto/schema/v1/validation_grpc.pb.go +++ b/gen/proto/schema/v1/validation_grpc.pb.go @@ -19,6 +19,7 @@ const _ = grpc.SupportPackageIsVersion7 // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type SchemaValidationServiceClient interface { ValidateMessage(ctx context.Context, in *ValidateMessageRequest, opts ...grpc.CallOption) (*ValidateMessageResponse, error) + BulkValidateMessages(ctx context.Context, in *BulkValidateMessagesRequest, opts ...grpc.CallOption) (*BulkValidateMessagesResponse, error) } type schemaValidationServiceClient struct { @@ -38,11 +39,21 @@ func (c *schemaValidationServiceClient) ValidateMessage(ctx context.Context, in return out, nil } +func (c *schemaValidationServiceClient) BulkValidateMessages(ctx context.Context, in *BulkValidateMessagesRequest, opts ...grpc.CallOption) (*BulkValidateMessagesResponse, error) { + out := new(BulkValidateMessagesResponse) + err := c.cc.Invoke(ctx, "/schema.v1.SchemaValidationService/BulkValidateMessages", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // SchemaValidationServiceServer is the server API for SchemaValidationService service. // All implementations must embed UnimplementedSchemaValidationServiceServer // for forward compatibility type SchemaValidationServiceServer interface { ValidateMessage(context.Context, *ValidateMessageRequest) (*ValidateMessageResponse, error) + BulkValidateMessages(context.Context, *BulkValidateMessagesRequest) (*BulkValidateMessagesResponse, error) mustEmbedUnimplementedSchemaValidationServiceServer() } @@ -53,6 +64,9 @@ type UnimplementedSchemaValidationServiceServer struct { func (UnimplementedSchemaValidationServiceServer) ValidateMessage(context.Context, *ValidateMessageRequest) (*ValidateMessageResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ValidateMessage not implemented") } +func (UnimplementedSchemaValidationServiceServer) BulkValidateMessages(context.Context, *BulkValidateMessagesRequest) (*BulkValidateMessagesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BulkValidateMessages not implemented") +} func (UnimplementedSchemaValidationServiceServer) mustEmbedUnimplementedSchemaValidationServiceServer() { } @@ -85,6 +99,24 @@ func _SchemaValidationService_ValidateMessage_Handler(srv interface{}, ctx conte return interceptor(ctx, in, info, handler) } +func _SchemaValidationService_BulkValidateMessages_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(BulkValidateMessagesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SchemaValidationServiceServer).BulkValidateMessages(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/schema.v1.SchemaValidationService/BulkValidateMessages", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SchemaValidationServiceServer).BulkValidateMessages(ctx, req.(*BulkValidateMessagesRequest)) + } + return interceptor(ctx, in, info, handler) +} + // SchemaValidationService_ServiceDesc is the grpc.ServiceDesc for SchemaValidationService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -96,6 +128,10 @@ var SchemaValidationService_ServiceDesc = grpc.ServiceDesc{ MethodName: "ValidateMessage", Handler: _SchemaValidationService_ValidateMessage_Handler, }, + { + MethodName: "BulkValidateMessages", + Handler: _SchemaValidationService_BulkValidateMessages_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "schema/v1/validation.proto", diff --git a/go.mod b/go.mod index 87c23e2..b72bdc1 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/samber/lo v1.53.0 github.com/spf13/cobra v1.9.1 github.com/spf13/viper v1.21.0 + github.com/stretchr/testify v1.11.1 github.com/uptrace/opentelemetry-go-extra/otelgorm v0.3.2 github.com/xBlaz3kx/DevX v0.5.0 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 @@ -47,6 +48,7 @@ require ( github.com/cloudwego/base64x v0.1.6 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.1.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect @@ -105,6 +107,7 @@ require ( github.com/nats-io/nuid v1.0.1 // indirect github.com/pelletier/go-toml/v2 v2.3.0 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.59.0 // indirect github.com/sagikazarmark/crypt v0.31.0 // indirect @@ -116,6 +119,7 @@ require ( github.com/spf13/cast v1.10.0 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/spf13/viper/remote v1.21.0 // indirect + github.com/stretchr/objx v0.5.3 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.1 // indirect @@ -159,4 +163,5 @@ require ( google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260720211330-0afa2a65878a // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/internal/grpc/handler.go b/internal/grpc/handler.go index 5690c87..06231e2 100644 --- a/internal/grpc/handler.go +++ b/internal/grpc/handler.go @@ -37,13 +37,20 @@ func validateVendorModel(vendor, model *string) error { return nil } -func validateActionKey(version schemav1.OcppVersion, action string) error { +func validateOcppVersion(version schemav1.OcppVersion) error { if version == schemav1.OcppVersion_OCPP_VERSION_UNSPECIFIED { return status.Error(codes.InvalidArgument, "ocpp_version is required") } if _, ok := schemav1.OcppVersion_name[int32(version)]; !ok { return status.Error(codes.InvalidArgument, "unknown ocpp_version") } + return nil +} + +func validateActionKey(version schemav1.OcppVersion, action string) error { + if err := validateOcppVersion(version); err != nil { + return err + } if action == "" { return status.Error(codes.InvalidArgument, "action is required") } diff --git a/internal/grpc/validation_handler.go b/internal/grpc/validation_handler.go index ebae52a..fa58287 100644 --- a/internal/grpc/validation_handler.go +++ b/internal/grpc/validation_handler.go @@ -13,6 +13,7 @@ import ( type ValidationService interface { ValidateMessage(ctx context.Context, req validation.Request) (*validation.Result, error) + ValidateMessages(ctx context.Context, req validation.BulkRequest) ([]validation.ReportEntry, error) } type ValidationHandler struct { @@ -50,13 +51,56 @@ func (h *ValidationHandler) ValidateMessage(ctx context.Context, req *schemav1.V return nil, status.Error(codes.Internal, err.Error()) } - resp := &schemav1.ValidateMessageResponse{Valid: result.Valid} - for _, v := range result.Violations { - resp.Violations = append(resp.Violations, &schemav1.ValidationViolation{ - Field: v.Field, - Message: v.Message, - }) + return &schemav1.ValidateMessageResponse{Valid: result.Valid, Violations: violationsToProto(result.Violations)}, nil +} + +// BulkValidateMessages validates a list of OCPP-J messages against the given +// OCPP version, vendor and model, reading each message's own action and +// message type off it, and returns one report entry per message, in the +// same order they were supplied. +func (h *ValidationHandler) BulkValidateMessages(ctx context.Context, req *schemav1.BulkValidateMessagesRequest) (*schemav1.BulkValidateMessagesResponse, error) { + if err := validateOcppVersion(req.OcppVersion); err != nil { + return nil, err + } + if err := validateVendorModel(req.Vendor, req.Model); err != nil { + return nil, err + } + if len(req.Messages) == 0 { + return nil, status.Error(codes.InvalidArgument, "at least one message is required") + } + if len(req.Messages) > validation.MaxBulkMessages { + return nil, status.Errorf(codes.InvalidArgument, "too many messages: max %d per request", validation.MaxBulkMessages) + } + + entries, err := h.service.ValidateMessages(ctx, validation.BulkRequest{ + Version: ocppVersionToDomain(req.OcppVersion), + Vendor: req.Vendor, + Model: req.Model, + Messages: req.Messages, + }) + if err != nil { + if errors.Is(err, schema.ErrNotFound) { + return nil, status.Error(codes.NotFound, "no schema found for the given parameters") + } + return nil, status.Error(codes.Internal, err.Error()) } - return resp, nil + results := make([]*schemav1.ValidationReportEntry, len(entries)) + for i, e := range entries { + results[i] = &schemav1.ValidationReportEntry{ + Index: int32(e.Index), + Valid: e.Valid, + Violations: violationsToProto(e.Violations), + } + } + + return &schemav1.BulkValidateMessagesResponse{Results: results}, nil +} + +func violationsToProto(violations []validation.Violation) []*schemav1.ValidationViolation { + out := make([]*schemav1.ValidationViolation, len(violations)) + for i, v := range violations { + out[i] = &schemav1.ValidationViolation{Field: v.Field, Message: v.Message} + } + return out } diff --git a/internal/grpc/validation_handler_test.go b/internal/grpc/validation_handler_test.go new file mode 100644 index 0000000..eaffb74 --- /dev/null +++ b/internal/grpc/validation_handler_test.go @@ -0,0 +1,148 @@ +package grpc + +import ( + "context" + "errors" + "testing" + + grpcmocks "github.com/ChargePi/chargeflow-registry/gen/mocks/grpc" + schemav1 "github.com/ChargePi/chargeflow-registry/gen/proto/schema/v1" + "github.com/ChargePi/chargeflow-registry/internal/schema" + "github.com/ChargePi/chargeflow-registry/internal/validation" + "github.com/stretchr/testify/mock" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func TestBulkValidateMessages_ValidationErrors(t *testing.T) { + model := "Terra54" + + tests := []struct { + name string + req *schemav1.BulkValidateMessagesRequest + code codes.Code + }{ + { + name: "missing ocpp_version", + req: &schemav1.BulkValidateMessagesRequest{ + Messages: []string{"{}"}, + }, + code: codes.InvalidArgument, + }, + { + name: "model without vendor", + req: &schemav1.BulkValidateMessagesRequest{ + OcppVersion: schemav1.OcppVersion_OCPP_VERSION_16, + Model: &model, + Messages: []string{"{}"}, + }, + code: codes.InvalidArgument, + }, + { + name: "no messages", + req: &schemav1.BulkValidateMessagesRequest{ + OcppVersion: schemav1.OcppVersion_OCPP_VERSION_16, + Messages: nil, + }, + code: codes.InvalidArgument, + }, + { + name: "too many messages", + req: &schemav1.BulkValidateMessagesRequest{ + OcppVersion: schemav1.OcppVersion_OCPP_VERSION_16, + Messages: make([]string, validation.MaxBulkMessages+1), + }, + code: codes.InvalidArgument, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := NewValidationHandler(grpcmocks.NewMockValidationService(t)) + + _, err := h.BulkValidateMessages(context.Background(), tt.req) + if err == nil { + t.Fatal("expected an error, got nil") + } + if got := status.Code(err); got != tt.code { + t.Errorf("status code = %v, want %v", got, tt.code) + } + }) + } +} + +func TestBulkValidateMessages_OrderedReport(t *testing.T) { + svc := grpcmocks.NewMockValidationService(t) + h := NewValidationHandler(svc) + + req := &schemav1.BulkValidateMessagesRequest{ + OcppVersion: schemav1.OcppVersion_OCPP_VERSION_16, + Messages: []string{ + `{"messageTypeId":2,"uniqueId":"1","action":"Authorize","payload":{}}`, + `{"messageTypeId":2,"uniqueId":"2","action":"Authorize","payload":{}}`, + `{"messageTypeId":3,"uniqueId":"1","action":"Authorize","payload":{}}`, + }, + } + + want := []validation.ReportEntry{ + {Index: 0, Valid: true}, + {Index: 1, Valid: false, Violations: []validation.Violation{{Field: "/idTag", Message: "required"}}}, + {Index: 2, Valid: true}, + } + + svc.EXPECT(). + ValidateMessages(context.Background(), validation.BulkRequest{ + Version: schema.OCPPVersion16, + Messages: req.Messages, + }). + Return(want, nil) + + resp, err := h.BulkValidateMessages(context.Background(), req) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(resp.Results) != len(want) { + t.Fatalf("got %d results, want %d", len(resp.Results), len(want)) + } + for i, entry := range resp.Results { + if entry.Index != int32(want[i].Index) { + t.Errorf("result[%d].Index = %d, want %d", i, entry.Index, want[i].Index) + } + if entry.Valid != want[i].Valid { + t.Errorf("result[%d].Valid = %v, want %v", i, entry.Valid, want[i].Valid) + } + if len(entry.Violations) != len(want[i].Violations) { + t.Errorf("result[%d].Violations count = %d, want %d", i, len(entry.Violations), len(want[i].Violations)) + } + } +} + +func TestBulkValidateMessages_ServiceErrors(t *testing.T) { + tests := []struct { + name string + err error + code codes.Code + }{ + {name: "not found", err: schema.ErrNotFound, code: codes.NotFound}, + {name: "internal", err: errors.New("boom"), code: codes.Internal}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + svc := grpcmocks.NewMockValidationService(t) + h := NewValidationHandler(svc) + + svc.EXPECT(). + ValidateMessages(mock.Anything, mock.Anything). + Return(nil, tt.err) + + _, err := h.BulkValidateMessages(context.Background(), &schemav1.BulkValidateMessagesRequest{ + OcppVersion: schemav1.OcppVersion_OCPP_VERSION_16, + Messages: []string{"{}"}, + }) + if got := status.Code(err); got != tt.code { + t.Errorf("status code = %v, want %v", got, tt.code) + } + }) + } +} diff --git a/internal/validation/validation.go b/internal/validation/validation.go index f9458eb..a06cbe8 100644 --- a/internal/validation/validation.go +++ b/internal/validation/validation.go @@ -36,6 +36,28 @@ type Request struct { Message []byte } +// MaxBulkMessages caps the number of messages accepted by a single +// BulkValidateMessages call. +const MaxBulkMessages = 200 + +// BulkRequest validates multiple OCPP-J messages against the given OCPP +// version, vendor and model. Each message carries its own action and +// message type, so unlike Request these aren't supplied once for the batch. +type BulkRequest struct { + Version schema.OCPPVersion + Vendor *string + Model *string + Messages []string +} + +// ReportEntry is one message's validation outcome within a bulk report, +// tagged with its position in the request's Messages list. +type ReportEntry struct { + Index int + Valid bool + Violations []Violation +} + type SchemaGetter interface { Get(ctx context.Context, version schema.OCPPVersion, action string, msgType schema.MessageType, vendor, model *string) (*schema.Schema, error) } @@ -95,6 +117,74 @@ func (s *Service) ValidateMessage(ctx context.Context, req Request) (*Result, er return &Result{Valid: result.IsValid(), Violations: violations}, nil } +// bulkEnvelope is the JSON shape of one message in a BulkRequest: an OCPP-J +// frame with messageTypeId, action and payload as named object fields +// (rather than the positional array used on the wire), so each message's +// action and message type can be read off the message itself. +type bulkEnvelope struct { + MessageTypeId ocpp.MessageType `json:"messageTypeId"` + Action string `json:"action"` + Payload json.RawMessage `json:"payload"` +} + +// ValidateMessages validates each of req.Messages, deriving the action and +// message type from the message itself, and returns one report entry per +// message in input order. +func (s *Service) ValidateMessages(ctx context.Context, req BulkRequest) ([]ReportEntry, error) { + ctx, span := tracer.Start(ctx, "validation.ValidateMessages", trace.WithAttributes( + attribute.String("ocpp.version", string(req.Version)), + attribute.Int("ocpp.message_count", len(req.Messages)), + )) + defer span.End() + + entries := make([]ReportEntry, len(req.Messages)) + for i, msg := range req.Messages { + var env bulkEnvelope + if err := json.Unmarshal([]byte(msg), &env); err != nil { + entries[i] = ReportEntry{Index: i, Violations: []Violation{{Field: "/", Message: "invalid JSON: " + err.Error()}}} + continue + } + + msgType, ok := messageTypeFromOCPP(env.MessageTypeId) + if !ok { + entries[i] = ReportEntry{Index: i, Violations: []Violation{{Field: "/messageTypeId", Message: fmt.Sprintf("unsupported message type: %d", env.MessageTypeId)}}} + continue + } + if env.Action == "" { + entries[i] = ReportEntry{Index: i, Violations: []Violation{{Field: "/action", Message: "action is required"}}} + continue + } + + result, err := s.ValidateMessage(ctx, Request{ + Version: req.Version, + Action: env.Action, + MessageType: msgType, + Vendor: req.Vendor, + Model: req.Model, + Message: env.Payload, + }) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return nil, fmt.Errorf("validate message %d: %w", i, err) + } + entries[i] = ReportEntry{Index: i, Valid: result.Valid, Violations: result.Violations} + } + + return entries, nil +} + +func messageTypeFromOCPP(id ocpp.MessageType) (schema.MessageType, bool) { + switch id { + case ocpp.CALL: + return schema.MessageTypeRequest, true + case ocpp.CALL_RESULT: + return schema.MessageTypeResponse, true + default: + return "", false + } +} + func buildOCPPMessage(action string, msgType schema.MessageType, payload any) (ocpp.Message, error) { switch msgType { case schema.MessageTypeRequest: diff --git a/internal/validation/validation_test.go b/internal/validation/validation_test.go new file mode 100644 index 0000000..e6f5cc7 --- /dev/null +++ b/internal/validation/validation_test.go @@ -0,0 +1,51 @@ +package validation + +import ( + "context" + "testing" + + validationmocks "github.com/ChargePi/chargeflow-registry/gen/mocks/validation" + "github.com/ChargePi/chargeflow-registry/internal/schema" + "go.uber.org/zap" +) + +func TestValidateMessages_DerivesActionAndTypeFromMessage(t *testing.T) { + getter := validationmocks.NewMockSchemaGetter(t) + getter.EXPECT(). + Get(context.Background(), schema.OCPPVersion16, "Authorize", schema.MessageTypeRequest, (*string)(nil), (*string)(nil)). + Return(&schema.Schema{Schema: []byte(`{"type":"object","required":["idTag"],"properties":{"idTag":{"type":"string"}}}`)}, nil) + + svc := NewService(getter, zap.NewNop()) + + entries, err := svc.ValidateMessages(context.Background(), BulkRequest{ + Version: schema.OCPPVersion16, + Messages: []string{ + `{"messageTypeId":2,"uniqueId":"1","action":"Authorize","payload":{"idTag":"ABC"}}`, + `not json`, + `{"messageTypeId":4,"uniqueId":"2","action":"Authorize","payload":{}}`, + `{"messageTypeId":2,"uniqueId":"3","payload":{}}`, + }, + }) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(entries) != 4 { + t.Fatalf("got %d entries, want 4", len(entries)) + } + + if !entries[0].Valid { + t.Errorf("entry 0: want valid, got violations %+v", entries[0].Violations) + } + + if entries[1].Valid || len(entries[1].Violations) == 0 { + t.Errorf("entry 1 (invalid JSON): want invalid with a violation, got %+v", entries[1]) + } + + if entries[2].Valid || len(entries[2].Violations) == 0 { + t.Errorf("entry 2 (unsupported message type): want invalid with a violation, got %+v", entries[2]) + } + + if entries[3].Valid || len(entries[3].Violations) == 0 { + t.Errorf("entry 3 (missing action): want invalid with a violation, got %+v", entries[3]) + } +} diff --git a/proto/schema/v1/validation.proto b/proto/schema/v1/validation.proto index 0a79282..88796fa 100644 --- a/proto/schema/v1/validation.proto +++ b/proto/schema/v1/validation.proto @@ -23,6 +23,29 @@ message ValidateMessageResponse { repeated ValidationViolation violations = 2; } +message BulkValidateMessagesRequest { + OcppVersion ocpp_version = 1; + optional string vendor = 2; + optional string model = 3; + // OCPP-J messages as JSON objects ({"messageTypeId", "uniqueId", "action", + // "payload"}); action and message type are read from each message rather + // than supplied once for the whole batch. Validated against the schema + // they identify and returned in the same order as supplied here. + repeated string messages = 4; +} + +message ValidationReportEntry { + // Position of the corresponding message in the request's messages list. + int32 index = 1; + bool valid = 2; + repeated ValidationViolation violations = 3; +} + +message BulkValidateMessagesResponse { + repeated ValidationReportEntry results = 1; +} + service SchemaValidationService { rpc ValidateMessage(ValidateMessageRequest) returns (ValidateMessageResponse); + rpc BulkValidateMessages(BulkValidateMessagesRequest) returns (BulkValidateMessagesResponse); } \ No newline at end of file