diff --git a/.gitignore b/.gitignore index b546895..12018c1 100644 --- a/.gitignore +++ b/.gitignore @@ -129,3 +129,13 @@ dist .yarn/build-state.yml .yarn/install-state.gz .pnp.* + +# Ignore compiled shared object files +*.so + +# Ignore coverage output files +coverage.out +coverage.html + +# Ignore the schema directory used for testing +/schemas/ \ No newline at end of file diff --git a/go.mod b/go.mod index c4129fc..91f393a 100644 --- a/go.mod +++ b/go.mod @@ -1,47 +1,15 @@ module github.com/beckn/beckn-onix -go 1.23.0 +go 1.24.1 -toolchain go1.23.7 - -require golang.org/x/crypto v0.36.0 +require ( + github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 + golang.org/x/crypto v0.36.0 +) require ( github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03 - cloud.google.com/go v0.119.0 // indirect - cloud.google.com/go/auth v0.15.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect - cloud.google.com/go/compute/metadata v0.6.0 // indirect - cloud.google.com/go/iam v1.4.1 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-logr/logr v1.4.2 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/google/s2a-go v0.1.9 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.5 // indirect - github.com/googleapis/gax-go/v2 v2.14.1 // indirect - go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect - go.opentelemetry.io/otel v1.34.0 // indirect - go.opentelemetry.io/otel/metric v1.34.0 // indirect - go.opentelemetry.io/otel/trace v1.34.0 // indirect - golang.org/x/net v0.35.0 // indirect - golang.org/x/oauth2 v0.28.0 // indirect - golang.org/x/sync v0.12.0 // indirect golang.org/x/text v0.23.0 // indirect - golang.org/x/time v0.11.0 // indirect - google.golang.org/api v0.224.0 // indirect - google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250227231956-55c901821b1e // indirect - google.golang.org/grpc v1.71.0 // indirect - google.golang.org/protobuf v1.36.5 // indirect ) -require ( - cloud.google.com/go/pubsub v1.48.0 - golang.org/x/sys v0.31.0 // indirect - gopkg.in/yaml.v2 v2.4.0 -) +require golang.org/x/sys v0.31.0 // indirect diff --git a/go.sum b/go.sum index 2bc3958..50bb32e 100644 --- a/go.sum +++ b/go.sum @@ -1,162 +1,12 @@ +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03 h1:m1h+vudopHsI67FPT9MOncyndWhTcdUoBtI1R1uajGY= github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03/go.mod h1:8sheVFH84v3PCyFY/O02mIgSQY9I6wMYPWsq7mDnEZY= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.119.0 h1:tw7OjErMzJKbbjaEHkrt60KQrK5Wus/boCZ7tm5/RNE= -cloud.google.com/go v0.119.0/go.mod h1:fwB8QLzTcNevxqi8dcpR+hoMIs3jBherGS9VUBDAW08= -cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps= -cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8= -cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= -cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= -cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= -cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= -cloud.google.com/go/iam v1.4.1 h1:cFC25Nv+u5BkTR/BT1tXdoF2daiVbZ1RLx2eqfQ9RMM= -cloud.google.com/go/iam v1.4.1/go.mod h1:2vUEJpUG3Q9p2UdsyksaKpDzlwOrnMzS30isdReIcLM= -cloud.google.com/go/pubsub v1.48.0 h1:ntFpQVrr10Wj/GXSOpxGmexGynldv/bFp25H0jy8aOs= -cloud.google.com/go/pubsub v1.48.0/go.mod h1:AAtyjyIT/+zaY1ERKFJbefOvkUxRDNp3nD6TdfdqUZk= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= -github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.5 h1:VgzTY2jogw3xt39CusEnFJWm7rlsq5yL5q9XdLOuP5g= -github.com/googleapis/enterprise-certificate-proxy v0.3.5/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= -github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= -github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= -go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= -go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= -go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= -go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= -go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= -go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= -golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= -golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.224.0 h1:Ir4UPtDsNiwIOHdExr3fAj4xZ42QjK7uQte3lORLJwU= -google.golang.org/api v0.224.0/go.mod h1:3V39my2xAGkodXy0vEqcEtkqgw2GtrFL5WuBZlCTCOQ= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE= -google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE= -google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950= -google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250227231956-55c901821b1e h1:YA5lmSs3zc/5w+xsRcHqpETkaYyK63ivEPzNTcUUlSA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250227231956-55c901821b1e/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= -google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/plugin/definition/schemaValidator.go b/pkg/plugin/definition/schemaValidator.go new file mode 100644 index 0000000..dc69ca5 --- /dev/null +++ b/pkg/plugin/definition/schemaValidator.go @@ -0,0 +1,16 @@ +package definition + +import ( + "context" + "net/url" +) + +// SchemaValidator interface for schema validation. +type SchemaValidator interface { + Validate(ctx context.Context, url *url.URL, payload []byte) error +} + +// SchemaValidatorProvider interface for creating validators. +type SchemaValidatorProvider interface { + New(ctx context.Context, config map[string]string) (SchemaValidator, func() error, error) +} diff --git a/pkg/plugin/implementation/schemaValidator/cmd/plugin.go b/pkg/plugin/implementation/schemaValidator/cmd/plugin.go new file mode 100644 index 0000000..2a8f44a --- /dev/null +++ b/pkg/plugin/implementation/schemaValidator/cmd/plugin.go @@ -0,0 +1,33 @@ +package main + +import ( + "context" + "errors" + + definition "github.com/beckn/beckn-onix/pkg/plugin/definition" + schemaValidator "github.com/beckn/beckn-onix/pkg/plugin/implementation/schemaValidator" +) + +// schemaValidatorProvider provides instances of schemaValidator. +type schemaValidatorProvider struct{} + +// New initializes a new Verifier instance. +func (vp schemaValidatorProvider) New(ctx context.Context, config map[string]string) (definition.SchemaValidator, func() error, error) { + if ctx == nil { + return nil, nil, errors.New("context cannot be nil") + } + + // Extract schemaDir from the config map + schemaDir, ok := config["schemaDir"] + if !ok || schemaDir == "" { + return nil, nil, errors.New("config must contain 'schemaDir'") + } + + // Create a new schemaValidator instance with the provided configuration + return schemaValidator.New(ctx, &schemaValidator.Config{ + SchemaDir: schemaDir, + }) +} + +// Provider is the exported symbol that the plugin manager will look for. +var Provider definition.SchemaValidatorProvider = schemaValidatorProvider{} diff --git a/pkg/plugin/implementation/schemaValidator/cmd/plugin_test.go b/pkg/plugin/implementation/schemaValidator/cmd/plugin_test.go new file mode 100644 index 0000000..75fdce0 --- /dev/null +++ b/pkg/plugin/implementation/schemaValidator/cmd/plugin_test.go @@ -0,0 +1,150 @@ +package main + +import ( + "context" + "os" + "path/filepath" + "strings" + "testing" +) + +// setupTestSchema creates a temporary directory and writes a sample schema file. +func setupTestSchema(t *testing.T) string { + t.Helper() + + // Create a temporary directory for the schema + schemaDir, err := os.MkdirTemp("", "schemas") + if err != nil { + t.Fatalf("Failed to create temp directory: %v", err) + } + + // Create the directory structure for the schema file + schemaFilePath := filepath.Join(schemaDir, "example", "1.0", "test_schema.json") + if err := os.MkdirAll(filepath.Dir(schemaFilePath), 0755); err != nil { + t.Fatalf("Failed to create schema directory structure: %v", err) + } + + // Define a sample schema + schemaContent := `{ + "type": "object", + "properties": { + "context": { + "type": "object", + "properties": { + "domain": {"type": "string"}, + "version": {"type": "string"} + }, + "required": ["domain", "version"] + } + }, + "required": ["context"] + }` + + // Write the schema to the file + if err := os.WriteFile(schemaFilePath, []byte(schemaContent), 0644); err != nil { + t.Fatalf("Failed to write schema file: %v", err) + } + + return schemaDir +} + +// TestValidatorProviderSuccess tests successful ValidatorProvider implementation. +func TestValidatorProviderSuccess(t *testing.T) { + schemaDir := setupTestSchema(t) + defer os.RemoveAll(schemaDir) + + // Define test cases. + tests := []struct { + name string + ctx context.Context + config map[string]string + expectedError string + }{ + { + name: "Valid schema directory", + ctx: context.Background(), // Valid context + config: map[string]string{"schemaDir": schemaDir}, + expectedError: "", + }, + } + + // Test using table-driven tests + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + vp := schemaValidatorProvider{} + schemaValidator, _, err := vp.New(tt.ctx, tt.config) + + // Ensure no error occurred + if err != nil { + t.Errorf("unexpected error: %v", err) + return + } + + // Ensure the schemaValidator is not nil + if schemaValidator == nil { + t.Error("expected a non-nil schemaValidator, got nil") + } + }) + } +} + +// TestValidatorProviderSuccess tests cases where ValidatorProvider creation should fail. +func TestValidatorProviderFailure(t *testing.T) { + schemaDir := setupTestSchema(t) + defer os.RemoveAll(schemaDir) + + // Define test cases. + tests := []struct { + name string + ctx context.Context + config map[string]string + expectedError string + }{ + { + name: "Config is empty", + ctx: context.Background(), + config: map[string]string{}, + expectedError: "config must contain 'schemaDir'", + }, + { + name: "schemaDir is empty", + ctx: context.Background(), + config: map[string]string{"schemaDir": ""}, + expectedError: "config must contain 'schemaDir'", + }, + { + name: "Invalid schema directory", + ctx: context.Background(), // Valid context + config: map[string]string{"schemaDir": "/invalid/dir"}, + expectedError: "failed to initialise schemaValidator: schema directory does not exist: /invalid/dir", + }, + { + name: "Nil context", + ctx: nil, // Nil context + config: map[string]string{"schemaDir": schemaDir}, + expectedError: "context cannot be nil", + }, + } + + // Test using table-driven tests + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + vp := schemaValidatorProvider{} + _, _, err := vp.New(tt.ctx, tt.config) + + // Check for expected error + if tt.expectedError != "" { + if err == nil || !strings.Contains(err.Error(), tt.expectedError) { + t.Errorf("expected error %q, got %v", tt.expectedError, err) + } + return + } + + // Ensure no error occurred + if err != nil { + t.Errorf("unexpected error: %v", err) + return + } + }) + } +} diff --git a/pkg/plugin/implementation/schemaValidator/schemaValidator.go b/pkg/plugin/implementation/schemaValidator/schemaValidator.go new file mode 100644 index 0000000..2d6b189 --- /dev/null +++ b/pkg/plugin/implementation/schemaValidator/schemaValidator.go @@ -0,0 +1,197 @@ +package schemaValidator + +import ( + "context" + "encoding/json" + "fmt" + "net/url" + "os" + "path" + "path/filepath" + "strings" + + response "github.com/beckn/beckn-onix/pkg/response" + + "github.com/santhosh-tekuri/jsonschema/v6" +) + +// Payload represents the structure of the data payload with context information. +type payload struct { + Context struct { + Domain string `json:"domain"` + Version string `json:"version"` + } `json:"context"` +} + +// SchemaValidator implements the Validator interface. +type SchemaValidator struct { + config *Config + schemaCache map[string]*jsonschema.Schema +} + +// Config struct for SchemaValidator. +type Config struct { + SchemaDir string +} + +// New creates a new ValidatorProvider instance. +func New(ctx context.Context, config *Config) (*SchemaValidator, func() error, error) { + // Check if config is nil + if config == nil { + return nil, nil, fmt.Errorf("config cannot be nil") + } + v := &SchemaValidator{ + config: config, + schemaCache: make(map[string]*jsonschema.Schema), + } + + // Call Initialise function to load schemas and get validators + if err := v.initialise(); err != nil { + return nil, nil, fmt.Errorf("failed to initialise schemaValidator: %v", err) + } + return v, nil, nil +} + +// Validate validates the given data against the schema. +func (v *SchemaValidator) Validate(ctx context.Context, url *url.URL, data []byte) error { + var payloadData payload + err := json.Unmarshal(data, &payloadData) + if err != nil { + return fmt.Errorf("failed to parse JSON payload: %v", err) + } + + // Extract domain, version, and endpoint from the payload and uri. + cxt_domain := payloadData.Context.Domain + version := payloadData.Context.Version + version = fmt.Sprintf("v%s", version) + + endpoint := path.Base(url.String()) + // ToDo Add debug log here + fmt.Println("Handling request for endpoint:", endpoint) + domain := strings.ToLower(cxt_domain) + domain = strings.ReplaceAll(domain, ":", "_") + + // Construct the schema file name. + schemaFileName := fmt.Sprintf("%s_%s_%s", domain, version, endpoint) + + // Retrieve the schema from the cache. + schema, exists := v.schemaCache[schemaFileName] + if !exists { + return fmt.Errorf("schema not found for domain: %s", schemaFileName) + } + + var jsonData any + if err := json.Unmarshal(data, &jsonData); err != nil { + return fmt.Errorf("failed to parse JSON data: %v", err) + } + err = schema.Validate(jsonData) + if err != nil { + // Handle schema validation errors + if validationErr, ok := err.(*jsonschema.ValidationError); ok { + // Convert validation errors into an array of SchemaValError + var schemaErrors []response.Error + for _, cause := range validationErr.Causes { + // Extract the path and message from the validation error + path := strings.Join(cause.InstanceLocation, ".") // JSON path to the invalid field + message := cause.Error() // Validation error message + + // Append the error to the schemaErrors array + schemaErrors = append(schemaErrors, response.Error{ + Paths: path, + Message: message, + }) + } + // Return the array of schema validation errors + return &response.SchemaValidationErr{Errors: schemaErrors} + } + // Return a generic error for non-validation errors + return fmt.Errorf("validation failed: %v", err) + } + + // Return nil if validation succeeds + return nil +} + +// ValidatorProvider provides instances of Validator. +type ValidatorProvider struct{} + +// Initialise initialises the validator provider by compiling all the JSON schema files +// from the specified directory and storing them in a cache indexed by their schema filenames. +func (v *SchemaValidator) initialise() error { + schemaDir := v.config.SchemaDir + // Check if the directory exists and is accessible. + info, err := os.Stat(schemaDir) + if err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("schema directory does not exist: %s", schemaDir) + } + return fmt.Errorf("failed to access schema directory: %v", err) + } + if !info.IsDir() { + return fmt.Errorf("provided schema path is not a directory: %s", schemaDir) + } + + compiler := jsonschema.NewCompiler() + + // Helper function to process directories recursively. + var processDir func(dir string) error + processDir = func(dir string) error { + entries, err := os.ReadDir(dir) + if err != nil { + return fmt.Errorf("failed to read directory: %v", err) + } + + for _, entry := range entries { + path := filepath.Join(dir, entry.Name()) + if entry.IsDir() { + // Recursively process subdirectories. + if err := processDir(path); err != nil { + return err + } + } else if filepath.Ext(entry.Name()) == ".json" { + // Process JSON files. + compiledSchema, err := compiler.Compile(path) + if err != nil { + return fmt.Errorf("failed to compile JSON schema from file %s: %v", entry.Name(), err) + } + + // Use relative path from schemaDir to avoid absolute paths and make schema keys domain/version specific. + relativePath, err := filepath.Rel(schemaDir, path) + if err != nil { + return fmt.Errorf("failed to get relative path for file %s: %v", entry.Name(), err) + } + // Split the relative path to get domain, version, and schema. + parts := strings.Split(relativePath, string(os.PathSeparator)) + + // Ensure that the file path has at least 3 parts: domain, version, and schema file. + if len(parts) < 3 { + return fmt.Errorf("invalid schema file structure, expected domain/version/schema.json but got: %s", relativePath) + } + + // Extract domain, version, and schema filename from the parts. + // Validate that the extracted parts are non-empty. + domain := strings.TrimSpace(parts[0]) + version := strings.TrimSpace(parts[1]) + schemaFileName := strings.TrimSpace(parts[2]) + schemaFileName = strings.TrimSuffix(schemaFileName, ".json") + + if domain == "" || version == "" || schemaFileName == "" { + return fmt.Errorf("invalid schema file structure, one or more components are empty. Relative path: %s", relativePath) + } + + // Construct a unique key combining domain, version, and schema name (e.g., ondc_trv10_v2.0.0_schema). + uniqueKey := fmt.Sprintf("%s_%s_%s", domain, version, schemaFileName) + // Store the compiled schema in the SchemaCache using the unique key. + v.schemaCache[uniqueKey] = compiledSchema + } + } + return nil + } + + // Start processing from the root schema directory. + if err := processDir(schemaDir); err != nil { + return fmt.Errorf("failed to read schema directory: %v", err) + } + + return nil +} diff --git a/pkg/plugin/implementation/schemaValidator/schemaValidator_test.go b/pkg/plugin/implementation/schemaValidator/schemaValidator_test.go new file mode 100644 index 0000000..277b539 --- /dev/null +++ b/pkg/plugin/implementation/schemaValidator/schemaValidator_test.go @@ -0,0 +1,353 @@ +package schemaValidator + +import ( + "context" + "net/url" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/santhosh-tekuri/jsonschema/v6" +) + +// setupTestSchema creates a temporary directory and writes a sample schema file. +func setupTestSchema(t *testing.T) string { + t.Helper() + + // Create a temporary directory for the schema + schemaDir, err := os.MkdirTemp("", "schemas") + if err != nil { + t.Fatalf("Failed to create temp directory: %v", err) + } + + // Create the directory structure for the schema file + schemaFilePath := filepath.Join(schemaDir, "example", "v1.0", "endpoint.json") + if err := os.MkdirAll(filepath.Dir(schemaFilePath), 0755); err != nil { + t.Fatalf("Failed to create schema directory structure: %v", err) + } + + // Define a sample schema + schemaContent := `{ + "type": "object", + "properties": { + "context": { + "type": "object", + "properties": { + "domain": {"type": "string"}, + "version": {"type": "string"}, + "action": {"type": "string"} + }, + "required": ["domain", "version", "action"] + } + }, + "required": ["context"] + }` + + // Write the schema to the file + if err := os.WriteFile(schemaFilePath, []byte(schemaContent), 0644); err != nil { + t.Fatalf("Failed to write schema file: %v", err) + } + + return schemaDir +} + +func TestValidator_Validate_Success(t *testing.T) { + tests := []struct { + name string + url string + payload string + wantErr bool + }{ + { + name: "Valid payload", + url: "http://example.com/endpoint", + payload: `{"context": {"domain": "example", "version": "1.0", "action": "endpoint"}}`, + wantErr: false, + }, + } + + // Setup a temporary schema directory for testing + schemaDir := setupTestSchema(t) + defer os.RemoveAll(schemaDir) + + config := &Config{SchemaDir: schemaDir} + v, _, err := New(context.Background(), config) + if err != nil { + t.Fatalf("Failed to create validator: %v", err) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + u, _ := url.Parse(tt.url) + err := v.Validate(context.Background(), u, []byte(tt.payload)) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } else { + t.Logf("Test %s passed with no errors", tt.name) + } + }) + } +} + +func TestValidator_Validate_Failure(t *testing.T) { + tests := []struct { + name string + url string + payload string + wantErr string + }{ + { + name: "Invalid JSON payload", + url: "http://example.com/endpoint", + payload: `{"context": {"domain": "example", "version": "1.0"`, + wantErr: "failed to parse JSON payload", + }, + { + name: "Schema validation failure", + url: "http://example.com/endpoint", + payload: `{"context": {"domain": "example", "version": "1.0"}}`, + wantErr: "context: at '/context': missing property 'action'", + }, + { + name: "Schema not found", + url: "http://example.com/unknown_endpoint", + payload: `{"context": {"domain": "example", "version": "1.0"}}`, + wantErr: "schema not found for domain", + }, + } + + // Setup a temporary schema directory for testing + schemaDir := setupTestSchema(t) + defer os.RemoveAll(schemaDir) + + config := &Config{SchemaDir: schemaDir} + v, _, err := New(context.Background(), config) + if err != nil { + t.Fatalf("Failed to create validator: %v", err) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + u, _ := url.Parse(tt.url) + err := v.Validate(context.Background(), u, []byte(tt.payload)) + if tt.wantErr != "" { + if err == nil { + t.Errorf("Expected error containing '%s', but got nil", tt.wantErr) + } else if !strings.Contains(err.Error(), tt.wantErr) { + t.Errorf("Expected error containing '%s', but got '%v'", tt.wantErr, err) + } else { + t.Logf("Test %s passed with expected error: %v", tt.name, err) + } + } else { + if err != nil { + t.Errorf("Unexpected error: %v", err) + } else { + t.Logf("Test %s passed with no errors", tt.name) + } + } + }) + } +} + +func TestValidator_Initialise(t *testing.T) { + tests := []struct { + name string + setupFunc func(schemaDir string) error + wantErr string + }{ + { + name: "Schema directory does not exist", + setupFunc: func(schemaDir string) error { + // Do not create the schema directory + return nil + + }, + wantErr: "schema directory does not exist", + }, + { + name: "Schema path is not a directory", + setupFunc: func(schemaDir string) error { + // Create a file instead of a directory + return os.WriteFile(schemaDir, []byte{}, 0644) + }, + wantErr: "provided schema path is not a directory", + }, + { + name: "Invalid schema file structure", + setupFunc: func(schemaDir string) error { + // Create an invalid schema file structure + invalidSchemaFile := filepath.Join(schemaDir, "invalid_schema.json") + if err := os.MkdirAll(filepath.Dir(invalidSchemaFile), 0755); err != nil { + t.Fatalf("Failed to create directory: %v", err) + } + return os.WriteFile(invalidSchemaFile, []byte(`{}`), 0644) + }, + wantErr: "invalid schema file structure", + }, + { + name: "Failed to compile JSON schema", + setupFunc: func(schemaDir string) error { + // Create a schema file with invalid JSON + invalidSchemaFile := filepath.Join(schemaDir, "example", "1.0", "endpoint.json") + if err := os.MkdirAll(filepath.Dir(invalidSchemaFile), 0755); err != nil { + t.Fatalf("Failed to create directory: %v", err) + } + return os.WriteFile(invalidSchemaFile, []byte(`{invalid json}`), 0644) + }, + wantErr: "failed to compile JSON schema", + }, + { + name: "Invalid schema file structure with empty components", + setupFunc: func(schemaDir string) error { + // Create a schema file with empty domain, version, or schema name + invalidSchemaFile := filepath.Join(schemaDir, "", "1.0", "endpoint.json") + if err := os.MkdirAll(filepath.Dir(invalidSchemaFile), 0755); err != nil { + t.Fatalf("Failed to create directory: %v", err) + } + return os.WriteFile(invalidSchemaFile, []byte(`{ + "type": "object", + "properties": { + "context": { + "type": "object", + "properties": { + "domain": {"type": "string"}, + "version": {"type": "string"} + }, + "required": ["domain", "version"] + } + }, + "required": ["context"] + }`), 0644) + }, + wantErr: "failed to read schema directory: invalid schema file structure, expected domain/version/schema.json but got: 1.0/endpoint.json", + }, + { + name: "Failed to read directory", + setupFunc: func(schemaDir string) error { + // Create a directory and remove read permissions + if err := os.MkdirAll(schemaDir, 0000); err != nil { + t.Fatalf("Failed to create directory: %v", err) + } + return nil + }, + wantErr: "failed to read directory", + }, + { + name: "Valid schema directory", + setupFunc: func(schemaDir string) error { + // Create a valid schema file + validSchemaFile := filepath.Join(schemaDir, "example", "1.0", "endpoint.json") + if err := os.MkdirAll(filepath.Dir(validSchemaFile), 0755); err != nil { + t.Fatalf("Failed to create directory: %v", err) + } + return os.WriteFile(validSchemaFile, []byte(`{ + "type": "object", + "properties": { + "context": { + "type": "object", + "properties": { + "domain": {"type": "string"}, + "version": {"type": "string"} + }, + "required": ["domain", "version"] + } + }, + "required": ["context"] + }`), 0644) + }, + wantErr: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Setup a temporary schema directory for testing + schemaDir := filepath.Join(os.TempDir(), "schemas") + defer os.RemoveAll(schemaDir) + + // Run the setup function to prepare the test case + if err := tt.setupFunc(schemaDir); err != nil { + t.Fatalf("setupFunc() error = %v", err) + } + + config := &Config{SchemaDir: schemaDir} + v := &SchemaValidator{ + config: config, + schemaCache: make(map[string]*jsonschema.Schema), + } + + err := v.initialise() + if (err != nil && !strings.Contains(err.Error(), tt.wantErr)) || (err == nil && tt.wantErr != "") { + t.Errorf("Error: initialise() returned error = %v, expected error = %v", err, tt.wantErr) + } else if err == nil { + t.Logf("Test %s passed: validator initialized successfully", tt.name) + } else { + t.Logf("Test %s passed with expected error: %v", tt.name, err) + } + }) + } +} + +func TestValidatorNew_Success(t *testing.T) { + schemaDir := setupTestSchema(t) + defer os.RemoveAll(schemaDir) + + config := &Config{SchemaDir: schemaDir} + _, _, err := New(context.Background(), config) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } +} + +func TestValidatorNewFailure(t *testing.T) { + tests := []struct { + name string + config *Config + setupFunc func(schemaDir string) error + wantErr string + }{ + { + name: "Config is nil", + config: nil, + setupFunc: func(schemaDir string) error { + return nil + }, + wantErr: "config cannot be nil", + }, + { + name: "Failed to initialise validators", + config: &Config{ + SchemaDir: "/invalid/path", + }, + setupFunc: func(schemaDir string) error { + // Do not create the schema directory + return nil + }, + wantErr: "ailed to initialise schemaValidator: schema directory does not exist: /invalid/path", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Run the setup function if provided + if tt.setupFunc != nil { + schemaDir := "" + if tt.config != nil { + schemaDir = tt.config.SchemaDir + } + if err := tt.setupFunc(schemaDir); err != nil { + t.Fatalf("Setup function failed: %v", err) + } + } + + // Call the New function with the test config + _, _, err := New(context.Background(), tt.config) + if (err != nil && !strings.Contains(err.Error(), tt.wantErr)) || (err == nil && tt.wantErr != "") { + t.Errorf("Error: New() returned error = %v, expected error = %v", err, tt.wantErr) + } else { + t.Logf("Test %s passed with expected error: %v", tt.name, err) + } + }) + } +} diff --git a/pkg/response/response.go b/pkg/response/response.go index 310d06f..7364495 100644 --- a/pkg/response/response.go +++ b/pkg/response/response.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "strings" ) type ErrorType string @@ -23,6 +24,20 @@ type Error struct { Paths string `json:"paths,omitempty"` } +// SchemaValidationErr represents a collection of schema validation failures. +type SchemaValidationErr struct { + Errors []Error +} + +// Error implements the error interface for SchemaValidationErr. +func (e *SchemaValidationErr) Error() string { + var errorMessages []string + for _, err := range e.Errors { + errorMessages = append(errorMessages, fmt.Sprintf("%s: %s", err.Paths, err.Message)) + } + return strings.Join(errorMessages, "; ") +} + type Message struct { Ack struct { Status string `json:"status,omitempty"` diff --git a/schemas.zip b/schemas.zip new file mode 100644 index 0000000..d72c46d Binary files /dev/null and b/schemas.zip differ