diff --git a/example/go.mod b/example/go.mod index 072feed5..42bf27cf 100644 --- a/example/go.mod +++ b/example/go.mod @@ -18,11 +18,24 @@ require ( cloud.google.com/go/iam v1.5.3 // indirect cloud.google.com/go/monitoring v1.24.3 // indirect cloud.google.com/go/storage v1.60.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect github.com/BurntSushi/toml v1.6.0 // indirect github.com/CrisisTextLine/modular/modules/auth v0.4.0 // indirect github.com/CrisisTextLine/modular/modules/cache v0.4.0 // indirect + github.com/CrisisTextLine/modular/modules/chimux v1.4.0 // indirect github.com/CrisisTextLine/modular/modules/eventbus/v2 v2.0.0 // indirect + github.com/CrisisTextLine/modular/modules/httpclient v0.5.0 // indirect + github.com/CrisisTextLine/modular/modules/httpserver v0.4.0 // indirect + github.com/CrisisTextLine/modular/modules/jsonschema v1.4.0 // indirect + github.com/CrisisTextLine/modular/modules/letsencrypt v0.4.0 // indirect + github.com/CrisisTextLine/modular/modules/logmasker v0.3.0 // indirect github.com/CrisisTextLine/modular/modules/reverseproxy/v2 v2.2.0 // indirect github.com/CrisisTextLine/modular/modules/scheduler v0.4.0 // indirect github.com/DataDog/datadog-go/v5 v5.4.0 // indirect @@ -82,6 +95,8 @@ require ( github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/go-acme/lego/v4 v4.26.0 // indirect + github.com/go-chi/chi/v5 v5.2.2 // indirect github.com/go-jose/go-jose/v4 v4.1.3 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -117,9 +132,11 @@ require ( github.com/jcmturner/gofork v1.7.6 // indirect github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect github.com/jcmturner/rpc/v2 v2.0.3 // indirect - github.com/json-iterator/go v1.1.12 // indirect + github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect github.com/klauspost/compress v1.18.3 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/miekg/dns v1.1.68 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect @@ -133,6 +150,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/prometheus/client_golang v1.19.1 // indirect @@ -144,6 +162,7 @@ require ( github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect @@ -162,12 +181,14 @@ require ( go.uber.org/zap v1.27.0 // indirect golang.org/x/crypto v0.48.0 // indirect golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect + golang.org/x/mod v0.33.0 // indirect golang.org/x/net v0.50.0 // indirect golang.org/x/oauth2 v0.35.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect golang.org/x/time v0.14.0 // indirect + golang.org/x/tools v0.42.0 // indirect google.golang.org/api v0.265.0 // indirect google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 // indirect diff --git a/example/go.sum b/example/go.sum index 16d6c141..a61a88d7 100644 --- a/example/go.sum +++ b/example/go.sum @@ -20,8 +20,31 @@ cloud.google.com/go/storage v1.60.0 h1:oBfZrSOCimggVNz9Y/bXY35uUcts7OViubeddTTVz cloud.google.com/go/storage v1.60.0/go.mod h1:q+5196hXfejkctrnx+VYU8RKQr/L3c0cBIlrjmiAKE0= cloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U= cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s= +github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 h1:5YTBM8QDVIBN3sxBil89WfdAAqDZbyJTgh688DSxX5w= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0 h1:MhRfI58HblXzCtWEZCO0feHs8LweePB3s90r7WaR1KU= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0/go.mod h1:okZ+ZURbArNdlJ+ptXoyHNuOETzOl1Oww19rm8I2WLA= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxwrQ919lCZoNCd69rVt8u1eLZuMORrGXqy8sNf3c= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0/go.mod h1:fSvRkb8d26z9dbL40Uf/OO6Vo9iExtZK3D0ulRV+8M0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsIIvxVT+uE6yrNldntJKlLRgxGbZ85kgtz5SNBhMw= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0/go.mod h1:AW8VEadnhw9xox+VaVd9sP7NjzOAnaZBLRH6Tq3cJ38= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 h1:yzrctSl9GMIQ5lHu7jc8olOsGjWDCsBpJhWqfGa/YIM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0/go.mod h1:GE4m0rnnfwLGX0Y9A9A25Zx5N/90jneT5ABevqzhuFQ= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 h1:zLzoX5+W2l95UJoVwiyNS4dX8vHyQ6x2xRLoBBL9wMk= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0/go.mod h1:wVEOJfGTj0oPAUGA1JuRAvz/lxXQsWW16axmHPP47Bk= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= +github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= +github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/CrisisTextLine/modular v1.11.11 h1:6rx271wWZ1r+RoPWuQRmhvpd5kmgGPAk1qYlX3kFsYs= @@ -30,10 +53,22 @@ github.com/CrisisTextLine/modular/modules/auth v0.4.0 h1:sP7CYgdJPz88M1PrfOz2knw github.com/CrisisTextLine/modular/modules/auth v0.4.0/go.mod h1:0DnUawpxdFCka4BjMhmXubQIjkF4VRwRdN6c64Jbvvo= github.com/CrisisTextLine/modular/modules/cache v0.4.0 h1:vlPXAsucSM1M0RsPly9cWyODouMLQMUwhW/wltQZHZk= github.com/CrisisTextLine/modular/modules/cache v0.4.0/go.mod h1:4irZOGXxUlgJqAnWlpMyPC3C1tM/f5145/wMThYnAsY= +github.com/CrisisTextLine/modular/modules/chimux v1.4.0 h1:lUX7SI3W25jhNzPX8TBrhAQPD8+MYVNN7kaem74WmAw= +github.com/CrisisTextLine/modular/modules/chimux v1.4.0/go.mod h1:9s5ndk4pPWtAMSi53UlQNTpOWfU4QaXwdBGhXTSkTUc= github.com/CrisisTextLine/modular/modules/eventbus v1.7.0 h1:SSeu7rjuECDgFa+iNyndn94YPQxffHxJgfR7U4psz6E= github.com/CrisisTextLine/modular/modules/eventbus v1.7.0/go.mod h1:I1tGf3DmadwyMP2NE2m6XHYl9ebXB9wBc/KZLywTR4c= github.com/CrisisTextLine/modular/modules/eventbus/v2 v2.0.0 h1:bDNWBparvVzXnhLxjFPJ9MDg7N4NUnNOjfn56G/CwGU= github.com/CrisisTextLine/modular/modules/eventbus/v2 v2.0.0/go.mod h1:5DmacIYrhhiN18i2OHyAVBiNKbN2jHuEv2UJoRToMg0= +github.com/CrisisTextLine/modular/modules/httpclient v0.5.0 h1:Wi9YJNNALBgmz2aU9xq8GILXXfmEJaImMrL26suA/c0= +github.com/CrisisTextLine/modular/modules/httpclient v0.5.0/go.mod h1:fxRWgjzYdWgpgkk+Li1RU8Wvhs4t0Gpl9yddFzRxowM= +github.com/CrisisTextLine/modular/modules/httpserver v0.4.0 h1:uFHZK9Pk5yy2+DxxtsAZSb8D9RkvtfV8cSfqwLc4zVw= +github.com/CrisisTextLine/modular/modules/httpserver v0.4.0/go.mod h1:Wr1VWGXhwDYTRobJWmLIyC4/DydRMEaAgA5RWvkDbkg= +github.com/CrisisTextLine/modular/modules/jsonschema v1.4.0 h1:NIhTrDgjhGwMi2D0ukGsd3n/M1W807u6Rhlqm89Sj8Q= +github.com/CrisisTextLine/modular/modules/jsonschema v1.4.0/go.mod h1:TeM3mt/+1X5VmlWF4nZpgp4qCGPmAahQs5jAzuWLbOo= +github.com/CrisisTextLine/modular/modules/letsencrypt v0.4.0 h1:PomfCavLGe/jZynO4wydJQ0B8Muq1R8GqPf9Kwr4Kq0= +github.com/CrisisTextLine/modular/modules/letsencrypt v0.4.0/go.mod h1:8TUUiT4nkEQwIo2Pc5vPAq28xYZliN754KbXqxXoVXw= +github.com/CrisisTextLine/modular/modules/logmasker v0.3.0 h1:m/TXbnpLlNbiE7ZztphvbkmrWzwJX23SGYe/Bx0eb7A= +github.com/CrisisTextLine/modular/modules/logmasker v0.3.0/go.mod h1:eNVM8Hjx/9J1WDnz/2qHT/uFgqEWS+xuCzlP8xB/9rw= github.com/CrisisTextLine/modular/modules/reverseproxy/v2 v2.2.0 h1:SUJEPA61IbjdUwKdSembQTbX9rKz5v4vmyr/cbvb4tY= github.com/CrisisTextLine/modular/modules/reverseproxy/v2 v2.2.0/go.mod h1:/jVQz+0c/OSm0KcLElNAQueI5BoLd48l1KHV4Np+RO8= github.com/CrisisTextLine/modular/modules/scheduler v0.4.0 h1:PDYAD+hL7E6mM7YJey9ag1dnTTcJwsepoylxfZY8trw= @@ -154,6 +189,8 @@ github.com/digitalocean/godo v1.175.0 h1:tpfwJFkBzpePxvvFazOn69TXctdxuFlOs7DMVXs github.com/digitalocean/godo v1.175.0/go.mod h1:xQsWpVCCbkDrWisHA72hPzPlnC+4W5w/McZY5ij9uvU= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +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/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= @@ -184,6 +221,8 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8 github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/go-acme/lego/v4 v4.26.0 h1:521aEQxNstXvPQcFDDPrJiFfixcCQuvAvm35R4GbyYA= +github.com/go-acme/lego/v4 v4.26.0/go.mod h1:BQVAWgcyzW4IT9eIKHY/RxYlVhoyKyOMXOkq7jK1eEQ= github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618= github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= @@ -197,8 +236,8 @@ github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= -github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= @@ -241,8 +280,8 @@ github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB1 github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c= -github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg= +github.com/hashicorp/go-memdb v1.3.5 h1:b3taDMxCBCBVgyRrS1AZVHO14ubMYZB++QpNhBg+Nyo= +github.com/hashicorp/go-memdb v1.3.5/go.mod h1:8IVKKBkVe+fxFgdFOYxzQQNjz+sWCyHCdIC/+5+Vy1Y= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= @@ -258,8 +297,8 @@ github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0Yg github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I= @@ -290,8 +329,10 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6 github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 h1:9Nu54bhS/H/Kgo2/7xNSUuC5G28VR8ljfrLKU2G4IjU= +github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12/go.mod h1:TBzl5BIHNXfS9+C35ZyJaklL7mLDbgUkcgXzSLa8Tk0= +github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= +github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw= github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= @@ -303,10 +344,14 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= +github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= github.com/minio/highwayhash v1.0.4-0.20251030100505-070ab1a87a76 h1:KGuD/pM2JpL9FAYvBrnBBeENKZNh6eNtjqytV6TYjnk= github.com/minio/highwayhash v1.0.4-0.20251030100505-070ab1a87a76/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -346,6 +391,8 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -375,11 +422,13 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +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/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -480,6 +529,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= diff --git a/go.mod b/go.mod index 383b6912..4882ea71 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/CrisisTextLine/modular/modules/auth v0.4.0 github.com/CrisisTextLine/modular/modules/cache v0.4.0 github.com/CrisisTextLine/modular/modules/eventbus/v2 v2.0.0 + github.com/CrisisTextLine/modular/modules/jsonschema v1.4.0 github.com/CrisisTextLine/modular/modules/reverseproxy/v2 v2.2.0 github.com/CrisisTextLine/modular/modules/scheduler v0.4.0 github.com/GoCodeAlone/go-plugin v0.0.0-20260220090904-b4c35f0e4271 @@ -152,7 +153,7 @@ require ( github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect github.com/jcmturner/rpc/v2 v2.0.3 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect + github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect github.com/klauspost/compress v1.18.3 // indirect github.com/launchdarkly/ccache v1.1.0 // indirect github.com/launchdarkly/eventsource v1.10.0 // indirect @@ -189,6 +190,7 @@ require ( github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect diff --git a/go.sum b/go.sum index 70e4bb2c..d8931945 100644 --- a/go.sum +++ b/go.sum @@ -34,6 +34,8 @@ github.com/CrisisTextLine/modular/modules/eventbus v1.7.0 h1:SSeu7rjuECDgFa+iNyn github.com/CrisisTextLine/modular/modules/eventbus v1.7.0/go.mod h1:I1tGf3DmadwyMP2NE2m6XHYl9ebXB9wBc/KZLywTR4c= github.com/CrisisTextLine/modular/modules/eventbus/v2 v2.0.0 h1:bDNWBparvVzXnhLxjFPJ9MDg7N4NUnNOjfn56G/CwGU= github.com/CrisisTextLine/modular/modules/eventbus/v2 v2.0.0/go.mod h1:5DmacIYrhhiN18i2OHyAVBiNKbN2jHuEv2UJoRToMg0= +github.com/CrisisTextLine/modular/modules/jsonschema v1.4.0 h1:NIhTrDgjhGwMi2D0ukGsd3n/M1W807u6Rhlqm89Sj8Q= +github.com/CrisisTextLine/modular/modules/jsonschema v1.4.0/go.mod h1:TeM3mt/+1X5VmlWF4nZpgp4qCGPmAahQs5jAzuWLbOo= github.com/CrisisTextLine/modular/modules/reverseproxy/v2 v2.2.0 h1:SUJEPA61IbjdUwKdSembQTbX9rKz5v4vmyr/cbvb4tY= github.com/CrisisTextLine/modular/modules/reverseproxy/v2 v2.2.0/go.mod h1:/jVQz+0c/OSm0KcLElNAQueI5BoLd48l1KHV4Np+RO8= github.com/CrisisTextLine/modular/modules/scheduler v0.4.0 h1:PDYAD+hL7E6mM7YJey9ag1dnTTcJwsepoylxfZY8trw= @@ -170,6 +172,8 @@ github.com/digitalocean/godo v1.175.0 h1:tpfwJFkBzpePxvvFazOn69TXctdxuFlOs7DMVXs github.com/digitalocean/godo v1.175.0/go.mod h1:xQsWpVCCbkDrWisHA72hPzPlnC+4W5w/McZY5ij9uvU= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +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/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= @@ -321,8 +325,8 @@ github.com/jhump/protoreflect v1.16.0 h1:54fZg+49widqXYQ0b+usAFHbMkBGR4PpXrsHc8+ github.com/jhump/protoreflect v1.16.0/go.mod h1:oYPd7nPvcBw/5wlDfm/AVmU9zH9BgqGCI469pGxfj/8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 h1:9Nu54bhS/H/Kgo2/7xNSUuC5G28VR8ljfrLKU2G4IjU= +github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12/go.mod h1:TBzl5BIHNXfS9+C35ZyJaklL7mLDbgUkcgXzSLa8Tk0= github.com/karlseguin/expect v1.0.2-0.20190806010014-778a5f0c6003 h1:vJ0Snvo+SLMY72r5J4sEfkuE7AFbixEP2qRbEcum/wA= github.com/karlseguin/expect v1.0.2-0.20190806010014-778a5f0c6003/go.mod h1:zNBxMY8P21owkeogJELCLeHIt+voOSduHYTFUbwRAV8= github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw= @@ -440,6 +444,8 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +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/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= diff --git a/plugins/modularcompat/jsonschema_test.go b/plugins/modularcompat/jsonschema_test.go new file mode 100644 index 00000000..b42a4cec --- /dev/null +++ b/plugins/modularcompat/jsonschema_test.go @@ -0,0 +1,258 @@ +package modularcompat + +import ( + "log/slog" + "os" + "strings" + "testing" + + "github.com/CrisisTextLine/modular" + "github.com/CrisisTextLine/modular/modules/jsonschema" +) + +// userSchema is a sample JSON Schema used across tests to validate user payloads. +const userSchema = `{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "name": { "type": "string", "minLength": 1 }, + "age": { "type": "integer", "minimum": 0 }, + "email": { "type": "string" } + }, + "required": ["name", "age"], + "additionalProperties": false +}` + +// writeSchemaFile creates a temp file containing schema JSON and returns its path. +// The caller is responsible for removing the file. +func writeSchemaFile(t *testing.T, content string) string { + t.Helper() + f, err := os.CreateTemp("", "wf-schema-*.json") + if err != nil { + t.Fatalf("create temp schema file: %v", err) + } + if _, err := f.WriteString(content); err != nil { + t.Fatalf("write schema file: %v", err) + } + if err := f.Close(); err != nil { + t.Fatalf("close schema file: %v", err) + } + t.Cleanup(func() { _ = os.Remove(f.Name()) }) + return f.Name() +} + +// newTestApp initialises a modular application with the jsonschema module registered. +// It returns the started Application; the caller must call Stop. +func newTestApp(t *testing.T) modular.Application { + t.Helper() + app := modular.NewStdApplication( + modular.NewStdConfigProvider(nil), + slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelError})), + ) + app.RegisterModule(jsonschema.NewModule()) + if err := app.Init(); err != nil { + t.Fatalf("app.Init: %v", err) + } + if err := app.Start(); err != nil { + t.Fatalf("app.Start: %v", err) + } + t.Cleanup(func() { + if err := app.Stop(); err != nil { + t.Logf("app.Stop: %v", err) + } + }) + return app +} + +// getJSONSchemaService retrieves the JSONSchemaService registered by the jsonschema module. +func getJSONSchemaService(t *testing.T, app modular.Application) jsonschema.JSONSchemaService { + t.Helper() + var svc jsonschema.JSONSchemaService + if err := app.GetService("jsonschema.service", &svc); err != nil { + t.Fatalf("GetService(jsonschema.service): %v", err) + } + return svc +} + +// TestJSONSchemaModuleWiredIntoApp verifies that the jsonschema module registered +// via the modularcompat plugin factory initialises inside a modular application +// and exposes its service. +func TestJSONSchemaModuleWiredIntoApp(t *testing.T) { + p := New() + factories := p.ModuleFactories() + factory, ok := factories["jsonschema.modular"] + if !ok { + t.Fatal("jsonschema.modular factory not found in modularcompat plugin") + } + + app := modular.NewStdApplication( + modular.NewStdConfigProvider(nil), + slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelError})), + ) + app.RegisterModule(factory("schema-module", nil)) + if err := app.Init(); err != nil { + t.Fatalf("app.Init: %v", err) + } + t.Cleanup(func() { _ = app.Stop() }) + + var svc jsonschema.JSONSchemaService + if err := app.GetService("jsonschema.service", &svc); err != nil { + t.Fatalf("jsonschema.service not available after Init: %v", err) + } +} + +// TestJSONSchemaValidateBytes tests validating raw JSON bytes against a compiled schema. +func TestJSONSchemaValidateBytes(t *testing.T) { + app := newTestApp(t) + svc := getJSONSchemaService(t, app) + + schemaPath := writeSchemaFile(t, userSchema) + schema, err := svc.CompileSchema(schemaPath) + if err != nil { + t.Fatalf("CompileSchema: %v", err) + } + + cases := []struct { + name string + payload string + wantErr bool + }{ + {"valid payload", `{"name":"Alice","age":30}`, false}, + {"valid with optional email", `{"name":"Bob","age":25,"email":"bob@example.com"}`, false}, + {"missing required field age", `{"name":"Charlie"}`, true}, + {"missing required field name", `{"age":40}`, true}, + {"extra property rejected", `{"name":"Dave","age":20,"phone":"555"}`, true}, + {"wrong type for age", `{"name":"Eve","age":"old"}`, true}, + {"empty object", `{}`, true}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + err := svc.ValidateBytes(schema, []byte(tc.payload)) + if tc.wantErr && err == nil { + t.Errorf("expected validation error, got nil") + } + if !tc.wantErr && err != nil { + t.Errorf("unexpected validation error: %v", err) + } + }) + } +} + +// TestJSONSchemaValidateReader tests validating JSON from an io.Reader. +func TestJSONSchemaValidateReader(t *testing.T) { + app := newTestApp(t) + svc := getJSONSchemaService(t, app) + + schemaPath := writeSchemaFile(t, userSchema) + schema, err := svc.CompileSchema(schemaPath) + if err != nil { + t.Fatalf("CompileSchema: %v", err) + } + + t.Run("valid", func(t *testing.T) { + if err := svc.ValidateReader(schema, strings.NewReader(`{"name":"Alice","age":30}`)); err != nil { + t.Errorf("unexpected error: %v", err) + } + }) + + t.Run("invalid", func(t *testing.T) { + if err := svc.ValidateReader(schema, strings.NewReader(`{"name":"Alice"}`)); err == nil { + t.Error("expected validation error, got nil") + } + }) +} + +// TestJSONSchemaValidateInterface tests validating a Go map (unmarshaled JSON) directly. +func TestJSONSchemaValidateInterface(t *testing.T) { + app := newTestApp(t) + svc := getJSONSchemaService(t, app) + + schemaPath := writeSchemaFile(t, userSchema) + schema, err := svc.CompileSchema(schemaPath) + if err != nil { + t.Fatalf("CompileSchema: %v", err) + } + + valid := map[string]any{"name": "Alice", "age": float64(30)} + if err := svc.ValidateInterface(schema, valid); err != nil { + t.Errorf("unexpected error for valid interface: %v", err) + } + + invalid := map[string]any{"name": "Alice"} // missing "age" + if err := svc.ValidateInterface(schema, invalid); err == nil { + t.Error("expected validation error for invalid interface, got nil") + } +} + +// TestJSONSchemaRegistryWorkflow simulates the schema-registry + validator use case +// described in the PR review: compile multiple schemas once into an in-memory +// "registry" map, then validate incoming payloads using the right schema by name. +func TestJSONSchemaRegistryWorkflow(t *testing.T) { + app := newTestApp(t) + svc := getJSONSchemaService(t, app) + + // --- schema definitions --- + schemas := map[string]string{ + "user": `{ + "type": "object", + "properties": { + "name": {"type": "string"}, + "age": {"type": "integer", "minimum": 0} + }, + "required": ["name", "age"], + "additionalProperties": false + }`, + "product": `{ + "type": "object", + "properties": { + "id": {"type": "string"}, + "price": {"type": "number", "minimum": 0} + }, + "required": ["id", "price"], + "additionalProperties": false + }`, + } + + // Compile all schemas once and store in a registry map. + schemaRegistry := make(map[string]jsonschema.Schema, len(schemas)) + for name, def := range schemas { + path := writeSchemaFile(t, def) + compiled, err := svc.CompileSchema(path) + if err != nil { + t.Fatalf("CompileSchema(%s): %v", name, err) + } + schemaRegistry[name] = compiled + } + + // --- validation table --- + type testCase struct { + schemaName string + payload string + wantErr bool + } + cases := []testCase{ + {"user", `{"name":"Alice","age":30}`, false}, + {"user", `{"name":"Bob"}`, true}, // missing age + {"user", `{"name":"Carol","age":25,"x":"y"}`, true}, // extra field + {"product", `{"id":"sku-1","price":9.99}`, false}, + {"product", `{"id":"sku-2"}`, true}, // missing price + {"product", `{"id":"sku-3","price":-1}`, true}, // price below minimum + } + + for _, tc := range cases { + t.Run(tc.schemaName+"/"+tc.payload, func(t *testing.T) { + sc, ok := schemaRegistry[tc.schemaName] + if !ok { + t.Fatalf("schema %q not in registry", tc.schemaName) + } + err := svc.ValidateBytes(sc, []byte(tc.payload)) + if tc.wantErr && err == nil { + t.Errorf("expected validation error, got nil") + } + if !tc.wantErr && err != nil { + t.Errorf("unexpected validation error: %v", err) + } + }) + } +} diff --git a/plugins/modularcompat/plugin.go b/plugins/modularcompat/plugin.go index a906934c..2af3e88c 100644 --- a/plugins/modularcompat/plugin.go +++ b/plugins/modularcompat/plugin.go @@ -1,10 +1,11 @@ // Package modularcompat provides a plugin that registers CrisisTextLine/modular -// framework module adapters: scheduler.modular, cache.modular. +// framework module adapters: scheduler.modular, cache.modular, jsonschema.modular. package modularcompat import ( "github.com/CrisisTextLine/modular" "github.com/CrisisTextLine/modular/modules/cache" + "github.com/CrisisTextLine/modular/modules/jsonschema" "github.com/CrisisTextLine/modular/modules/scheduler" "github.com/GoCodeAlone/workflow/capability" "github.com/GoCodeAlone/workflow/plugin" @@ -22,15 +23,15 @@ func New() *Plugin { BaseNativePlugin: plugin.BaseNativePlugin{ PluginName: "modular-compat", PluginVersion: "1.0.0", - PluginDescription: "CrisisTextLine/modular framework compatibility modules (scheduler, cache)", + PluginDescription: "CrisisTextLine/modular framework compatibility modules (scheduler, cache, jsonschema)", }, Manifest: plugin.PluginManifest{ Name: "modular-compat", Version: "1.0.0", Author: "GoCodeAlone", - Description: "CrisisTextLine/modular framework compatibility modules (scheduler, cache)", + Description: "CrisisTextLine/modular framework compatibility modules (scheduler, cache, jsonschema)", Tier: plugin.TierCore, - ModuleTypes: []string{"scheduler.modular", "cache.modular"}, + ModuleTypes: []string{"cache.modular", "jsonschema.modular", "scheduler.modular"}, Capabilities: []plugin.CapabilityDecl{ {Name: "scheduler", Role: "provider", Priority: 30}, {Name: "cache", Role: "provider", Priority: 30}, @@ -57,11 +58,14 @@ func (p *Plugin) Capabilities() []capability.Contract { // ModuleFactories returns module factories that delegate to the modular framework modules. func (p *Plugin) ModuleFactories() map[string]plugin.ModuleFactory { return map[string]plugin.ModuleFactory{ - "scheduler.modular": func(_ string, _ map[string]any) modular.Module { - return scheduler.NewModule() - }, "cache.modular": func(_ string, _ map[string]any) modular.Module { return cache.NewModule() }, + "jsonschema.modular": func(_ string, _ map[string]any) modular.Module { + return jsonschema.NewModule() + }, + "scheduler.modular": func(_ string, _ map[string]any) modular.Module { + return scheduler.NewModule() + }, } } diff --git a/plugins/modularcompat/plugin_test.go b/plugins/modularcompat/plugin_test.go index 257bef1c..2aa681e8 100644 --- a/plugins/modularcompat/plugin_test.go +++ b/plugins/modularcompat/plugin_test.go @@ -30,13 +30,17 @@ func TestModuleFactories(t *testing.T) { p := New() factories := p.ModuleFactories() - for _, name := range []string{"scheduler.modular", "cache.modular"} { + for _, name := range []string{ + "cache.modular", + "jsonschema.modular", + "scheduler.modular", + } { if _, ok := factories[name]; !ok { t.Errorf("missing module factory: %s", name) } } - if len(factories) != 2 { - t.Errorf("expected 2 module factories, got %d", len(factories)) + if len(factories) != 3 { + t.Errorf("expected 3 module factories, got %d", len(factories)) } } @@ -58,6 +62,15 @@ func TestCacheModuleFactory(t *testing.T) { } } +func TestJSONSchemaModuleFactory(t *testing.T) { + p := New() + factories := p.ModuleFactories() + mod := factories["jsonschema.modular"]("test-jsonschema", nil) + if mod == nil { + t.Fatal("jsonschema.modular factory returned nil") + } +} + func TestPluginLoads(t *testing.T) { p := New() loader := plugin.NewPluginLoader(capability.NewRegistry(), schema.NewModuleSchemaRegistry()) @@ -66,7 +79,7 @@ func TestPluginLoads(t *testing.T) { } modules := loader.ModuleFactories() - if len(modules) != 2 { - t.Fatalf("expected 2 module factories after load, got %d", len(modules)) + if len(modules) != 3 { + t.Fatalf("expected 3 module factories after load, got %d", len(modules)) } } diff --git a/schema/module_schema.go b/schema/module_schema.go index f5094a7f..0303a018 100644 --- a/schema/module_schema.go +++ b/schema/module_schema.go @@ -472,6 +472,16 @@ func (r *ModuleSchemaRegistry) registerBuiltins() { ConfigFields: []ConfigFieldDef{}, }) + r.Register(&ModuleSchema{ + Type: "jsonschema.modular", + Label: "JSON Schema Validator", + Category: "validation", + Description: "CrisisTextLine/modular JSON Schema validation module", + Inputs: []ServiceIODef{{Name: "data", Type: "any", Description: "Data to validate against schema"}}, + Outputs: []ServiceIODef{{Name: "validator", Type: "JSONSchemaService", Description: "JSON Schema validation service"}}, + ConfigFields: []ConfigFieldDef{}, + }) + // ---- Database Category ---- r.Register(&ModuleSchema{ diff --git a/schema/schema.go b/schema/schema.go index f6f312ba..e4423d03 100644 --- a/schema/schema.go +++ b/schema/schema.go @@ -116,6 +116,7 @@ var coreModuleTypes = []string{ "http.router", "http.server", "http.simple_proxy", + "jsonschema.modular", "license.validator", "log.collector", "messaging.broker",