diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 7ba405aa..f0803b98 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -17,6 +17,7 @@ updates: - "/uiprojector" - "/trace" - "/storage" + - "/notifications" - "/json" - "/xmlgen" - "/htmlgen" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 67360984..0a449902 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -398,6 +398,41 @@ jobs: - name: Run Tests (includes build) run: go test -v -race -coverprofile=coverage.out -covermode=atomic ./... + # The notifications/ package is its own Go module, so the root ./... jobs above do not + # cover it. Run its quality checks and tests independently. + notifications-module: + name: Notifications Module + runs-on: ubuntu-latest + permissions: + contents: read + defaults: + run: + working-directory: notifications + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + - uses: actions/setup-go@v7 + with: + go-version-file: 'notifications/go.mod' + cache-dependency-path: notifications/go.sum + cache: true + + - name: Verify go.mod + run: | + go mod tidy + git diff --exit-code go.mod go.sum + + - name: golangci-lint + uses: golangci/golangci-lint-action@v9 + with: + version: v2.11.2 + args: --timeout=5m + working-directory: notifications + + - name: Run Tests (includes build) + run: go test -v -race -coverprofile=coverage.out -covermode=atomic ./... + # The uiprojector/ package is its own Go module, so the root ./... jobs above do not # cover it. Run its quality checks and tests independently. uiprojector-module: diff --git a/README.md b/README.md index 44ff729a..58f42b57 100644 --- a/README.md +++ b/README.md @@ -16,30 +16,30 @@ This SDK provides all the infrastructure pieces to build such a system, while ke ## Packages -| Package | Purpose | -|------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [`artifact`](artifact/README.md) | Versioned configuration registry — load workflow definitions, form schemas, and templates by ID (includes [`artifact/adapter`](artifact/adapter/README.md), bridge adapters for domain types) | -| [`authn`](authn/README.md) | JWT validation, identity context injection, and HTTP middleware | -| [`authz`](authz/README.md) | Scope-based authorization middleware and predicates, decoupled from authn | -| [`configyaml`](configyaml/README.md) | Loads a YAML config file into a Go struct, resolving `{{env:NAME}}` / `{{file:/path}}` placeholders via `secret.SecretRef` | -| [`cors`](cors/README.md) | CORS HTTP middleware | -| [`database`](database/README.md) | `database/sql` connection factory (PostgreSQL, MySQL, SQLite) with pooling and health checks | -| [`htmlgen`](htmlgen/README.md) | Print-ready HTML document generation from a static template and structured data, with contextual escaping | -| [`httputil`](httputil/README.md) | Shared HTTP response helpers for JSON payloads and correlation-ID-tagged API error bodies | -| [`json`](json/README.md) | Packages for working with decoded JSON documents (includes [`json/jsonpointer`](json/jsonpointer/README.md), RFC 6901 JSON Pointer get/set) | -| [`notification`](notification/README.md) | Multi-channel notification router (SMS, email) with pluggable providers | -| [`pagination`](pagination/README.md) | Standard pagination envelope and query parameter parsing | -| [`payment`](payment/README.md) | Pluggable payment gateway orchestration with webhook processing and idempotency | -| [`remote`](remote/README.md) | Registry-based outbound HTTP client with pluggable auth (API key, Bearer, OAuth2) | -| [`secret`](secret/README.md) | Secret-bearing config values (`SecretRef`) resolved from env, file, or literal sources | -| [`shared`](shared/README.md) | Small, dependency-free helpers (deep copy, nested map access, config validation) used across other modules | -| [`storage`](storage/README.md) | File storage abstraction (local filesystem and AWS S3) with presigned URLs | -| [`taskflow`](taskflow/README.md) | Micro-interactive task orchestration — the core engine for human-in-the-loop steps | -| [`temporal`](temporal/README.md) | Temporal client factory | -| [`trace`](trace/README.md) | HTTP request trace propagation and context-based trace ID correlation | -| [`uiprojector`](uiprojector/README.md) | Zone-based, metadata-driven UI rendering from workflow state and business data | -| [`workflow`](workflow/README.md) | JSON DSL-driven Temporal workflow graph interpreter | -| [`xmlgen`](xmlgen/README.md) | XML document generation from a static template and structured data, with automatic escaping | +| Package | Purpose | +|--------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [`artifact`](artifact/README.md) | Versioned configuration registry — load workflow definitions, form schemas, and templates by ID (includes [`artifact/adapter`](artifact/adapter/README.md), bridge adapters for domain types) | +| [`authn`](authn/README.md) | JWT validation, identity context injection, and HTTP middleware | +| [`authz`](authz/README.md) | Scope-based authorization middleware and predicates, decoupled from authn | +| [`configyaml`](configyaml/README.md) | Loads a YAML config file into a Go struct, resolving `{{env:NAME}}` / `{{file:/path}}` placeholders via `secret.SecretRef` | +| [`cors`](cors/README.md) | CORS HTTP middleware | +| [`database`](database/README.md) | `database/sql` connection factory (PostgreSQL, MySQL, SQLite) with pooling and health checks | +| [`htmlgen`](htmlgen/README.md) | Print-ready HTML document generation from a static template and structured data, with contextual escaping | +| [`httputil`](httputil/README.md) | Shared HTTP response helpers for JSON payloads and correlation-ID-tagged API error bodies | +| [`json`](json/README.md) | Packages for working with decoded JSON documents (includes [`json/jsonpointer`](json/jsonpointer/README.md), RFC 6901 JSON Pointer get/set) | +| [`notifications`](notifications/README.md) | Multi-channel notification router (SMS, email) with pluggable providers | +| [`pagination`](pagination/README.md) | Standard pagination envelope and query parameter parsing | +| [`payment`](payment/README.md) | Pluggable payment gateway orchestration with webhook processing and idempotency | +| [`remote`](remote/README.md) | Registry-based outbound HTTP client with pluggable auth (API key, Bearer, OAuth2) | +| [`secret`](secret/README.md) | Secret-bearing config values (`SecretRef`) resolved from env, file, or literal sources | +| [`shared`](shared/README.md) | Small, dependency-free helpers (deep copy, nested map access, config validation) used across other modules | +| [`storage`](storage/README.md) | File storage abstraction (local filesystem and AWS S3) with presigned URLs | +| [`taskflow`](taskflow/README.md) | Micro-interactive task orchestration — the core engine for human-in-the-loop steps | +| [`temporal`](temporal/README.md) | Temporal client factory | +| [`trace`](trace/README.md) | HTTP request trace propagation and context-based trace ID correlation | +| [`uiprojector`](uiprojector/README.md) | Zone-based, metadata-driven UI rendering from workflow state and business data | +| [`workflow`](workflow/README.md) | JSON DSL-driven Temporal workflow graph interpreter | +| [`xmlgen`](xmlgen/README.md) | XML document generation from a static template and structured data, with automatic escaping | ## Requirements @@ -76,7 +76,7 @@ flowchart TD P["USER_INPUT · PAYMENT · EXTERNAL_REVIEW · API_CALL · custom"] end - SS["payment · remote · notification · storage"] + SS["payment · remote · notifications · storage"] AR[("artifact registry
workflow defs · task templates
form schemas · render templates")] Portal -->|HTTP| server diff --git a/go.mod b/go.mod index 1d290b82..44270b99 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.26.0 require ( github.com/OpenNSW/core/remote v0.8.0 - github.com/OpenNSW/core/secret v0.2.0 github.com/OpenNSW/core/shared v0.3.0 github.com/OpenNSW/core/uiprojector v0.3.0 github.com/aws/aws-sdk-go-v2 v1.47.1 @@ -21,6 +20,7 @@ require ( ) require ( + github.com/OpenNSW/core/secret v0.2.0 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.20 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.20.1 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.4 // indirect diff --git a/notification/README.md b/notifications/README.md similarity index 85% rename from notification/README.md rename to notifications/README.md index a7f05160..9e277277 100644 --- a/notification/README.md +++ b/notifications/README.md @@ -1,11 +1,17 @@ -# notification +# notifications + +`github.com/OpenNSW/core/notifications` is its own Go module. It moved from `github.com/OpenNSW/core/notification` in the root module. The package is still named `notification`, so only the import path changes; code keeps using `notification.X`. Import it with the name written out, as `goimports` writes it when a package's name differs from its path: + +```go +import notification "github.com/OpenNSW/core/notifications" +``` A multi-channel notification router with a pluggable provider model. Your application registers one provider per channel type (email, SMS, etc.); the manager dispatches each `Request` to the correct provider at runtime. ## Usage ```go -import "github.com/OpenNSW/core/notification" +import notification "github.com/OpenNSW/core/notifications" manager, err := notification.NewManager( notification.Config{ diff --git a/notification/config.go b/notifications/config.go similarity index 100% rename from notification/config.go rename to notifications/config.go diff --git a/notification/config_test.go b/notifications/config_test.go similarity index 100% rename from notification/config_test.go rename to notifications/config_test.go diff --git a/notifications/go.mod b/notifications/go.mod new file mode 100644 index 00000000..555ec1c8 --- /dev/null +++ b/notifications/go.mod @@ -0,0 +1,8 @@ +module github.com/OpenNSW/core/notifications + +go 1.26 + +require ( + github.com/OpenNSW/core/remote v0.8.0 + github.com/OpenNSW/core/secret v0.2.0 +) diff --git a/notifications/go.sum b/notifications/go.sum new file mode 100644 index 00000000..4460318d --- /dev/null +++ b/notifications/go.sum @@ -0,0 +1,12 @@ +github.com/OpenNSW/core/remote v0.8.0 h1:Ot44DtkEtzqKffy77EI0S6BCH1xDspQ2y2lSFKcZpPg= +github.com/OpenNSW/core/remote v0.8.0/go.mod h1:BzfC7ppzEAd5fUQmIsgeR2lZ6LzygNU4prNjvDpDH+4= +github.com/OpenNSW/core/secret v0.2.0 h1:7kxJYbVNJhN9X9/YyTUci4bv9oYKcm0nT/wBVrxBChQ= +github.com/OpenNSW/core/secret v0.2.0/go.mod h1:MEkX9vnlPRVTn+JQQsb192PoY6p3hbFSOIV+FxIMrc0= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/notification/manager.go b/notifications/manager.go similarity index 100% rename from notification/manager.go rename to notifications/manager.go diff --git a/notification/manager_test.go b/notifications/manager_test.go similarity index 100% rename from notification/manager_test.go rename to notifications/manager_test.go diff --git a/notification/provider.go b/notifications/provider.go similarity index 100% rename from notification/provider.go rename to notifications/provider.go diff --git a/notification/providers/email.go b/notifications/providers/email.go similarity index 97% rename from notification/providers/email.go rename to notifications/providers/email.go index 24384164..b9903863 100644 --- a/notification/providers/email.go +++ b/notifications/providers/email.go @@ -10,7 +10,7 @@ import ( "fmt" "net/http" - "github.com/OpenNSW/core/notification" + notification "github.com/OpenNSW/core/notifications" "github.com/OpenNSW/core/remote" "github.com/OpenNSW/core/remote/auth" "github.com/OpenNSW/core/secret" diff --git a/notification/providers/helpers.go b/notifications/providers/helpers.go similarity index 100% rename from notification/providers/helpers.go rename to notifications/providers/helpers.go diff --git a/notification/providers/sms.go b/notifications/providers/sms.go similarity index 97% rename from notification/providers/sms.go rename to notifications/providers/sms.go index a92b39c2..5eeba2ca 100644 --- a/notification/providers/sms.go +++ b/notifications/providers/sms.go @@ -10,7 +10,7 @@ import ( "fmt" "net/http" - "github.com/OpenNSW/core/notification" + notification "github.com/OpenNSW/core/notifications" "github.com/OpenNSW/core/remote" ) diff --git a/notification/test/integration/sms_integration_test.go b/notifications/test/integration/sms_integration_test.go similarity index 95% rename from notification/test/integration/sms_integration_test.go rename to notifications/test/integration/sms_integration_test.go index 36f1bfcf..0cf6c51a 100644 --- a/notification/test/integration/sms_integration_test.go +++ b/notifications/test/integration/sms_integration_test.go @@ -11,8 +11,8 @@ import ( "testing" "time" - "github.com/OpenNSW/core/notification" - "github.com/OpenNSW/core/notification/providers" + notification "github.com/OpenNSW/core/notifications" + "github.com/OpenNSW/core/notifications/providers" ) func TestSMSIntegration(t *testing.T) { diff --git a/notification/types.go b/notifications/types.go similarity index 100% rename from notification/types.go rename to notifications/types.go