seams 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +335 -0
- data/LICENSE +21 -0
- data/README.md +104 -0
- data/lib/generators/seams/accounts/accounts_generator.rb +272 -0
- data/lib/generators/seams/accounts/templates/README.md.tt +219 -0
- data/lib/generators/seams/accounts/templates/app/models/account.rb.tt +124 -0
- data/lib/generators/seams/accounts/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/accounts/templates/app/models/current.rb.tt +38 -0
- data/lib/generators/seams/accounts/templates/app/models/membership.rb.tt +114 -0
- data/lib/generators/seams/accounts/templates/config/routes.rb.tt +8 -0
- data/lib/generators/seams/accounts/templates/db/migrate/create_accounts.rb.tt +29 -0
- data/lib/generators/seams/accounts/templates/db/migrate/create_accounts_memberships.rb.tt +49 -0
- data/lib/generators/seams/accounts/templates/lib/accounts.rb.tt +21 -0
- data/lib/generators/seams/accounts/templates/lib/concerns/account_scoped.rb.tt +97 -0
- data/lib/generators/seams/accounts/templates/lib/concerns/authorization.rb.tt +105 -0
- data/lib/generators/seams/accounts/templates/lib/configuration.rb.tt +26 -0
- data/lib/generators/seams/accounts/templates/lib/engine.rb.tt +55 -0
- data/lib/generators/seams/accounts/templates/spec/factories/accounts.rb.tt +49 -0
- data/lib/generators/seams/accounts/templates/spec/models/accounts/account_spec.rb.tt +64 -0
- data/lib/generators/seams/accounts/templates/spec/models/accounts/membership_spec.rb.tt +99 -0
- data/lib/generators/seams/accounts/templates/spec/runtime/accounts_boot_spec.rb.tt +181 -0
- data/lib/generators/seams/admin/admin_generator.rb +852 -0
- data/lib/generators/seams/admin/templates/README.md.tt +266 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/accounts_controller.rb.tt +16 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/accounts_memberships_controller.rb.tt +16 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/application_controller.rb.tt +282 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/identities_controller.rb.tt +26 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/invitations_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/invoices_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/lifetime_passes_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/notification_preferences_controller.rb.tt +15 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/notifications_controller.rb.tt +18 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/plans_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/subscriptions_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/teams_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/teams_memberships_controller.rb.tt +16 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/account_dashboard.rb.tt +50 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/accounts_membership_dashboard.rb.tt +58 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/identity_dashboard.rb.tt +48 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/invitation_dashboard.rb.tt +51 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/invoice_dashboard.rb.tt +67 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/lifetime_pass_dashboard.rb.tt +65 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_dashboard.rb.tt +58 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_preference_dashboard.rb.tt +43 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/plan_dashboard.rb.tt +72 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/subscription_dashboard.rb.tt +59 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/team_dashboard.rb.tt +39 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/teams_membership_dashboard.rb.tt +43 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/account_policy.rb.tt +10 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/accounts_membership_policy.rb.tt +10 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/application_policy.rb.tt +85 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/identity_policy.rb.tt +18 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/invitation_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/invoice_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/lifetime_pass_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/notification_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/notification_preference_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/plan_policy.rb.tt +11 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/subscription_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/team_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/teams_membership_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/account_policy.rb.tt +33 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/accounts_membership_policy.rb.tt +24 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/application_policy.rb.tt +169 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/identity_policy.rb.tt +67 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invitation_policy.rb.tt +24 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invoice_policy.rb.tt +21 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/lifetime_pass_policy.rb.tt +21 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_policy.rb.tt +25 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_preference_policy.rb.tt +23 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/plan_policy.rb.tt +47 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/subscription_policy.rb.tt +22 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/team_policy.rb.tt +28 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/teams_membership_policy.rb.tt +24 -0
- data/lib/generators/seams/admin/templates/config/routes.rb.tt +38 -0
- data/lib/generators/seams/admin/templates/lib/admin.rb.tt +36 -0
- data/lib/generators/seams/admin/templates/lib/concerns/authenticator.rb.tt +66 -0
- data/lib/generators/seams/admin/templates/lib/configuration.rb.tt +90 -0
- data/lib/generators/seams/admin/templates/lib/context.rb.tt +44 -0
- data/lib/generators/seams/admin/templates/lib/engine.rb.tt +68 -0
- data/lib/generators/seams/admin/templates/spec/factories/admin.rb.tt +10 -0
- data/lib/generators/seams/admin/templates/spec/runtime/admin_boot_spec.rb.tt +604 -0
- data/lib/generators/seams/auth/add_oauth_provider/add_oauth_provider_generator.rb +157 -0
- data/lib/generators/seams/auth/add_oauth_provider/templates/adapter.rb.tt +95 -0
- data/lib/generators/seams/auth/add_oauth_provider/templates/adapter_spec.rb.tt +58 -0
- data/lib/generators/seams/auth/auth_generator.rb +311 -0
- data/lib/generators/seams/auth/templates/README.md.tt +289 -0
- data/lib/generators/seams/auth/templates/app/controllers/oauth/callbacks_controller.rb.tt +80 -0
- data/lib/generators/seams/auth/templates/app/controllers/password_resets_controller.rb.tt +44 -0
- data/lib/generators/seams/auth/templates/app/controllers/registrations_controller.rb.tt +34 -0
- data/lib/generators/seams/auth/templates/app/controllers/sessions_controller.rb.tt +49 -0
- data/lib/generators/seams/auth/templates/app/jobs/application_job.rb.tt +7 -0
- data/lib/generators/seams/auth/templates/app/jobs/cleanup_expired_sessions_job.rb.tt +30 -0
- data/lib/generators/seams/auth/templates/app/mailers/passwords_mailer.rb.tt +15 -0
- data/lib/generators/seams/auth/templates/app/models/api_token.rb.tt +62 -0
- data/lib/generators/seams/auth/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/auth/templates/app/models/current.rb.tt +15 -0
- data/lib/generators/seams/auth/templates/app/models/identity.rb.tt +74 -0
- data/lib/generators/seams/auth/templates/app/models/oauth/provider.rb.tt +48 -0
- data/lib/generators/seams/auth/templates/app/models/session.rb.tt +28 -0
- data/lib/generators/seams/auth/templates/app/services/authenticate_identity.rb.tt +31 -0
- data/lib/generators/seams/auth/templates/app/services/generate_api_token.rb.tt +35 -0
- data/lib/generators/seams/auth/templates/app/services/oauth/authenticator.rb.tt +94 -0
- data/lib/generators/seams/auth/templates/app/services/register_identity.rb.tt +57 -0
- data/lib/generators/seams/auth/templates/app/services/reset_password.rb.tt +41 -0
- data/lib/generators/seams/auth/templates/app/services/revoke_api_token.rb.tt +38 -0
- data/lib/generators/seams/auth/templates/app/views/password_resets/edit.html.erb.tt +12 -0
- data/lib/generators/seams/auth/templates/app/views/password_resets/new.html.erb.tt +11 -0
- data/lib/generators/seams/auth/templates/app/views/passwords_mailer/reset_email.html.erb.tt +7 -0
- data/lib/generators/seams/auth/templates/app/views/registrations/new.html.erb.tt +26 -0
- data/lib/generators/seams/auth/templates/app/views/sessions/_oauth_buttons.html.erb.tt +18 -0
- data/lib/generators/seams/auth/templates/app/views/sessions/new.html.erb.tt +17 -0
- data/lib/generators/seams/auth/templates/config/routes.rb.tt +21 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_api_tokens.rb.tt +26 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_identities.rb.tt +29 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_oauth_providers.rb.tt +35 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_sessions.rb.tt +19 -0
- data/lib/generators/seams/auth/templates/lib/auth.rb.tt +39 -0
- data/lib/generators/seams/auth/templates/lib/concerns/api_authenticatable.rb.tt +58 -0
- data/lib/generators/seams/auth/templates/lib/concerns/authenticatable.rb.tt +32 -0
- data/lib/generators/seams/auth/templates/lib/concerns/authentication.rb.tt +60 -0
- data/lib/generators/seams/auth/templates/lib/configuration.rb.tt +45 -0
- data/lib/generators/seams/auth/templates/lib/engine.rb.tt +46 -0
- data/lib/generators/seams/auth/templates/lib/oauth/abstract.rb.tt +87 -0
- data/lib/generators/seams/auth/templates/lib/oauth/github.rb.tt +112 -0
- data/lib/generators/seams/auth/templates/lib/oauth/google.rb.tt +78 -0
- data/lib/generators/seams/auth/templates/lib/tasks/auth_pii.rake.tt +68 -0
- data/lib/generators/seams/auth/templates/spec/factories/auth.rb.tt +38 -0
- data/lib/generators/seams/auth/templates/spec/mailers/passwords_mailer_spec.rb.tt +37 -0
- data/lib/generators/seams/auth/templates/spec/models/api_token_spec.rb.tt +84 -0
- data/lib/generators/seams/auth/templates/spec/models/identity_spec.rb.tt +56 -0
- data/lib/generators/seams/auth/templates/spec/models/oauth/provider_spec.rb.tt +64 -0
- data/lib/generators/seams/auth/templates/spec/models/session_spec.rb.tt +34 -0
- data/lib/generators/seams/auth/templates/spec/runtime/boot_spec.rb.tt +30 -0
- data/lib/generators/seams/auth/templates/spec/runtime/event_payload_spec.rb.tt +29 -0
- data/lib/generators/seams/auth/templates/spec/runtime/login_flow_spec.rb.tt +45 -0
- data/lib/generators/seams/billing/billing_generator.rb +476 -0
- data/lib/generators/seams/billing/templates/README.md.tt +355 -0
- data/lib/generators/seams/billing/templates/app/controllers/admin/lifetime_passes_controller.rb.tt +84 -0
- data/lib/generators/seams/billing/templates/app/controllers/checkout_controller.rb.tt +92 -0
- data/lib/generators/seams/billing/templates/app/controllers/invoices_controller.rb.tt +63 -0
- data/lib/generators/seams/billing/templates/app/controllers/plans_controller.rb.tt +14 -0
- data/lib/generators/seams/billing/templates/app/controllers/portal_controller.rb.tt +45 -0
- data/lib/generators/seams/billing/templates/app/controllers/subscriptions_controller.rb.tt +119 -0
- data/lib/generators/seams/billing/templates/app/controllers/webhooks_controller.rb.tt +98 -0
- data/lib/generators/seams/billing/templates/app/helpers/currency_helper.rb.tt +44 -0
- data/lib/generators/seams/billing/templates/app/jobs/application_job.rb.tt +6 -0
- data/lib/generators/seams/billing/templates/app/jobs/cancel_subscription_job.rb.tt +39 -0
- data/lib/generators/seams/billing/templates/app/jobs/start_subscription_job.rb.tt +32 -0
- data/lib/generators/seams/billing/templates/app/jobs/webhooks/process_event_job.rb.tt +37 -0
- data/lib/generators/seams/billing/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/billing/templates/app/models/invoice.rb.tt +35 -0
- data/lib/generators/seams/billing/templates/app/models/lifetime_pass.rb.tt +60 -0
- data/lib/generators/seams/billing/templates/app/models/plan.rb.tt +95 -0
- data/lib/generators/seams/billing/templates/app/models/subscription.rb.tt +31 -0
- data/lib/generators/seams/billing/templates/app/models/webhook_event.rb.tt +13 -0
- data/lib/generators/seams/billing/templates/app/services/checkout_session_service.rb.tt +25 -0
- data/lib/generators/seams/billing/templates/app/services/customers/find_or_create_service.rb.tt +73 -0
- data/lib/generators/seams/billing/templates/app/services/invoices/sync_service.rb.tt +50 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/create_lifetime_session_service.rb.tt +82 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/create_pass_from_checkout_service.rb.tt +88 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/grant_pass_service.rb.tt +80 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/revoke_pass_service.rb.tt +59 -0
- data/lib/generators/seams/billing/templates/app/services/portal_session_service.rb.tt +23 -0
- data/lib/generators/seams/billing/templates/app/services/service_result.rb.tt +38 -0
- data/lib/generators/seams/billing/templates/app/services/stripe_service.rb.tt +67 -0
- data/lib/generators/seams/billing/templates/app/services/subscriptions/cancel_service.rb.tt +42 -0
- data/lib/generators/seams/billing/templates/app/services/subscriptions/change_plan_service.rb.tt +48 -0
- data/lib/generators/seams/billing/templates/app/services/subscriptions/reactivate_service.rb.tt +28 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/event_router.rb.tt +54 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handler.rb.tt +93 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/charge_refunded_handler.rb.tt +18 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/checkout_session_completed_handler.rb.tt +58 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_created_handler.rb.tt +16 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_finalized_handler.rb.tt +14 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_handler_base.rb.tt +80 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_paid_handler.rb.tt +12 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_payment_failed_handler.rb.tt +12 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_voided_handler.rb.tt +12 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/payment_failed_handler.rb.tt +15 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/payment_succeeded_handler.rb.tt +19 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_created_handler.rb.tt +11 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_deleted_handler.rb.tt +15 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_handler_base.rb.tt +92 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_trial_will_end_handler.rb.tt +15 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_updated_handler.rb.tt +11 -0
- data/lib/generators/seams/billing/templates/app/views/admin/lifetime_passes/index.html.erb.tt +36 -0
- data/lib/generators/seams/billing/templates/app/views/admin/lifetime_passes/new.html.erb.tt +37 -0
- data/lib/generators/seams/billing/templates/app/views/checkout/success.html.erb.tt +5 -0
- data/lib/generators/seams/billing/templates/app/views/invoices/index.html.erb.tt +22 -0
- data/lib/generators/seams/billing/templates/app/views/invoices/show.html.erb.tt +14 -0
- data/lib/generators/seams/billing/templates/app/views/plans/index.html.erb.tt +51 -0
- data/lib/generators/seams/billing/templates/app/views/subscriptions/index.html.erb.tt +16 -0
- data/lib/generators/seams/billing/templates/app/views/subscriptions/show.html.erb.tt +25 -0
- data/lib/generators/seams/billing/templates/config/routes.rb.tt +39 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_invoices.rb.tt +32 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_lifetime_passes.rb.tt +43 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_plans.rb.tt +31 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_subscriptions.rb.tt +33 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_webhook_events.rb.tt +24 -0
- data/lib/generators/seams/billing/templates/lib/billing.rb.tt +34 -0
- data/lib/generators/seams/billing/templates/lib/concerns/billable.rb.tt +100 -0
- data/lib/generators/seams/billing/templates/lib/configuration.rb.tt +52 -0
- data/lib/generators/seams/billing/templates/lib/engine.rb.tt +72 -0
- data/lib/generators/seams/billing/templates/lib/gateways/abstract.rb.tt +65 -0
- data/lib/generators/seams/billing/templates/lib/gateways/adyen.rb.tt +16 -0
- data/lib/generators/seams/billing/templates/lib/gateways/paddle.rb.tt +22 -0
- data/lib/generators/seams/billing/templates/lib/gateways/stripe.rb.tt +155 -0
- data/lib/generators/seams/billing/templates/lib/stripe/client.rb.tt +101 -0
- data/lib/generators/seams/billing/templates/lib/stripe/webhook_signature.rb.tt +43 -0
- data/lib/generators/seams/billing/templates/lib/tasks/billing_check.rake.tt +34 -0
- data/lib/generators/seams/billing/templates/spec/factories/billing.rb.tt +65 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/charge_refunded.json.tt +19 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/checkout_session_completed.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_created.json.tt +25 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_deleted.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_trial_will_end.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_updated.json.tt +28 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_created.json.tt +18 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_finalized.json.tt +18 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_paid.json.tt +19 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_payment_failed.json.tt +20 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_voided.json.tt +18 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_payment_failed.json.tt +21 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_succeeded.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/gateways/contract_spec.rb.tt +11 -0
- data/lib/generators/seams/billing/templates/spec/gateways/stripe_spec.rb.tt +53 -0
- data/lib/generators/seams/billing/templates/spec/models/plan_spec.rb.tt +81 -0
- data/lib/generators/seams/billing/templates/spec/models/subscription_spec.rb.tt +43 -0
- data/lib/generators/seams/billing/templates/spec/runtime/boot_spec.rb.tt +38 -0
- data/lib/generators/seams/billing/templates/spec/runtime/webhook_handlers_spec.rb.tt +382 -0
- data/lib/generators/seams/billing/templates/spec/support/shared_examples/a_billing_gateway.rb.tt +100 -0
- data/lib/generators/seams/billing/templates/spec/support/stripe_helpers.rb.tt +59 -0
- data/lib/generators/seams/core/core_generator.rb +191 -0
- data/lib/generators/seams/core/templates/README.md.tt +45 -0
- data/lib/generators/seams/core/templates/app/controllers/concerns/has_current_attributes.rb.tt +71 -0
- data/lib/generators/seams/core/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/core/templates/app/models/audit_log.rb.tt +19 -0
- data/lib/generators/seams/core/templates/app/models/concerns/auditable.rb.tt +64 -0
- data/lib/generators/seams/core/templates/app/models/concerns/sluggable.rb.tt +53 -0
- data/lib/generators/seams/core/templates/app/models/concerns/soft_deletable.rb.tt +37 -0
- data/lib/generators/seams/core/templates/app/models/concerns/tenant_scoped.rb.tt +39 -0
- data/lib/generators/seams/core/templates/app/models/current.rb.tt +16 -0
- data/lib/generators/seams/core/templates/app/services/event_publisher.rb.tt +23 -0
- data/lib/generators/seams/core/templates/app/validators/email_format_validator.rb.tt +21 -0
- data/lib/generators/seams/core/templates/db/migrate/create_core_audit_logs.rb.tt +29 -0
- data/lib/generators/seams/core/templates/lib/core.rb.tt +8 -0
- data/lib/generators/seams/core/templates/lib/engine.rb.tt +28 -0
- data/lib/generators/seams/core/templates/spec/concerns/auditable_spec.rb.tt +39 -0
- data/lib/generators/seams/core/templates/spec/concerns/sluggable_spec.rb.tt +29 -0
- data/lib/generators/seams/core/templates/spec/models/audit_log_spec.rb.tt +22 -0
- data/lib/generators/seams/core/templates/spec/runtime/boot_spec.rb.tt +25 -0
- data/lib/generators/seams/core/templates/spec/validators/email_format_validator_spec.rb.tt +29 -0
- data/lib/generators/seams/engine/engine_generator.rb +165 -0
- data/lib/generators/seams/engine/templates/Gemfile.tt +19 -0
- data/lib/generators/seams/engine/templates/LICENSE.tt +21 -0
- data/lib/generators/seams/engine/templates/README.md.tt +40 -0
- data/lib/generators/seams/engine/templates/Rakefile.tt +14 -0
- data/lib/generators/seams/engine/templates/app/application_controller.rb.tt +6 -0
- data/lib/generators/seams/engine/templates/app/application_record.rb.tt +16 -0
- data/lib/generators/seams/engine/templates/config/locales/en.yml.tt +14 -0
- data/lib/generators/seams/engine/templates/config/routes.rb.tt +4 -0
- data/lib/generators/seams/engine/templates/gemspec.tt +20 -0
- data/lib/generators/seams/engine/templates/host_initializer.rb.tt +13 -0
- data/lib/generators/seams/engine/templates/lib/engine.rb.tt +27 -0
- data/lib/generators/seams/engine/templates/lib/root.rb.tt +7 -0
- data/lib/generators/seams/engine/templates/lib/version.rb.tt +5 -0
- data/lib/generators/seams/engine/templates/rubocop.yml.tt +55 -0
- data/lib/generators/seams/engine/templates/spec/example_spec.rb.tt +16 -0
- data/lib/generators/seams/engine/templates/spec/spec_helper.rb.tt +23 -0
- data/lib/generators/seams/install/install_generator.rb +211 -0
- data/lib/generators/seams/install/templates/Dockerfile.tt +52 -0
- data/lib/generators/seams/install/templates/Procfile.tt +14 -0
- data/lib/generators/seams/install/templates/bin_seams.tt +107 -0
- data/lib/generators/seams/install/templates/ci.yml.tt +123 -0
- data/lib/generators/seams/install/templates/deploy.yml.tt +63 -0
- data/lib/generators/seams/install/templates/doc/ARCHITECTURE.md.tt +86 -0
- data/lib/generators/seams/install/templates/docker-entrypoint.tt +27 -0
- data/lib/generators/seams/install/templates/rubocop.yml.tt +33 -0
- data/lib/generators/seams/install/templates/ruby-version.tt +1 -0
- data/lib/generators/seams/install/templates/script/collate_coverage.rb.tt +33 -0
- data/lib/generators/seams/install/templates/script/run_affected_tests.sh.tt +64 -0
- data/lib/generators/seams/install/templates/seams.rake.tt +65 -0
- data/lib/generators/seams/install/templates/seams.rb.tt +9 -0
- data/lib/generators/seams/install/templates/seams_engines.rb.tt +15 -0
- data/lib/generators/seams/notifications/notifications_generator.rb +395 -0
- data/lib/generators/seams/notifications/templates/README.md.tt +269 -0
- data/lib/generators/seams/notifications/templates/app/channels/notification_channel.rb.tt +36 -0
- data/lib/generators/seams/notifications/templates/app/controllers/notifications_controller.rb.tt +58 -0
- data/lib/generators/seams/notifications/templates/app/controllers/preferences_controller.rb.tt +54 -0
- data/lib/generators/seams/notifications/templates/app/javascript/controllers/notification_bell_controller.js.tt +34 -0
- data/lib/generators/seams/notifications/templates/app/jobs/application_job.rb.tt +6 -0
- data/lib/generators/seams/notifications/templates/app/jobs/create_notification_job.rb.tt +31 -0
- data/lib/generators/seams/notifications/templates/app/jobs/send_due_notifications_job.rb.tt +22 -0
- data/lib/generators/seams/notifications/templates/app/jobs/send_notification_job.rb.tt +13 -0
- data/lib/generators/seams/notifications/templates/app/mailers/application_mailer.rb.tt +12 -0
- data/lib/generators/seams/notifications/templates/app/mailers/notification_mailer.rb.tt +23 -0
- data/lib/generators/seams/notifications/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/notifications/templates/app/models/delivery.rb.tt +13 -0
- data/lib/generators/seams/notifications/templates/app/models/notification.rb.tt +218 -0
- data/lib/generators/seams/notifications/templates/app/models/notification_preference.rb.tt +29 -0
- data/lib/generators/seams/notifications/templates/app/models/strategies/email.rb.tt +38 -0
- data/lib/generators/seams/notifications/templates/app/models/strategies/in_app.rb.tt +26 -0
- data/lib/generators/seams/notifications/templates/app/models/strategies/sms.rb.tt +33 -0
- data/lib/generators/seams/notifications/templates/app/subscribers/auth_subscriber.rb.tt +71 -0
- data/lib/generators/seams/notifications/templates/app/subscribers/billing_subscriber.rb.tt +127 -0
- data/lib/generators/seams/notifications/templates/app/views/layouts/notifications/mailer.html.erb.tt +22 -0
- data/lib/generators/seams/notifications/templates/app/views/layouts/notifications/mailer.text.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/notifications/_bell.html.erb.tt +15 -0
- data/lib/generators/seams/notifications/templates/app/views/notifications/index.html.erb.tt +15 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_failed.html.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_failed.text.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_paid.html.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_paid.text.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_granted.html.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_granted.text.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_purchased.html.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_purchased.text.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_canceled.html.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_canceled.text.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_started.html.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_started.text.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_updated.html.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_updated.text.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/default.html.erb.tt +10 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/default.text.erb.tt +11 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/welcome.html.erb.tt +6 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/welcome.text.erb.tt +6 -0
- data/lib/generators/seams/notifications/templates/config/initializers/notifications.rb.tt +58 -0
- data/lib/generators/seams/notifications/templates/config/routes.rb.tt +17 -0
- data/lib/generators/seams/notifications/templates/db/migrate/create_notification_deliveries.rb.tt +16 -0
- data/lib/generators/seams/notifications/templates/db/migrate/create_notification_preferences.rb.tt +25 -0
- data/lib/generators/seams/notifications/templates/db/migrate/create_notifications.rb.tt +35 -0
- data/lib/generators/seams/notifications/templates/lib/adapters/abstract.rb.tt +20 -0
- data/lib/generators/seams/notifications/templates/lib/adapters/action_mailer.rb.tt +17 -0
- data/lib/generators/seams/notifications/templates/lib/adapters/null_sms.rb.tt +23 -0
- data/lib/generators/seams/notifications/templates/lib/concerns/notifiable.rb.tt +135 -0
- data/lib/generators/seams/notifications/templates/lib/configuration.rb.tt +24 -0
- data/lib/generators/seams/notifications/templates/lib/engine.rb.tt +35 -0
- data/lib/generators/seams/notifications/templates/lib/notifications.rb.tt +75 -0
- data/lib/generators/seams/notifications/templates/lib/type_registry.rb.tt +74 -0
- data/lib/generators/seams/notifications/templates/spec/factories/notifications.rb.tt +53 -0
- data/lib/generators/seams/notifications/templates/spec/models/delivery_spec.rb.tt +28 -0
- data/lib/generators/seams/notifications/templates/spec/models/notification_preference_spec.rb.tt +46 -0
- data/lib/generators/seams/notifications/templates/spec/models/notification_spec.rb.tt +60 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/bell_broadcast_spec.rb.tt +59 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/billing_subscriber_skip_spec.rb.tt +87 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/boot_spec.rb.tt +39 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/schedule_round_trip_spec.rb.tt +55 -0
- data/lib/generators/seams/remove/remove_generator.rb +259 -0
- data/lib/generators/seams/teams/teams_generator.rb +298 -0
- data/lib/generators/seams/teams/templates/README.md.tt +88 -0
- data/lib/generators/seams/teams/templates/app/controllers/invitations_controller.rb.tt +102 -0
- data/lib/generators/seams/teams/templates/app/controllers/memberships_controller.rb.tt +54 -0
- data/lib/generators/seams/teams/templates/app/controllers/teams_controller.rb.tt +68 -0
- data/lib/generators/seams/teams/templates/app/jobs/application_job.rb.tt +6 -0
- data/lib/generators/seams/teams/templates/app/mailers/invitation_mailer.rb.tt +34 -0
- data/lib/generators/seams/teams/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/teams/templates/app/models/current.rb.tt +30 -0
- data/lib/generators/seams/teams/templates/app/models/invitation.rb.tt +36 -0
- data/lib/generators/seams/teams/templates/app/models/membership.rb.tt +36 -0
- data/lib/generators/seams/teams/templates/app/models/team.rb.tt +32 -0
- data/lib/generators/seams/teams/templates/app/subscribers/invitation_subscriber.rb.tt +36 -0
- data/lib/generators/seams/teams/templates/app/views/invitation_mailer/invite.text.erb.tt +8 -0
- data/lib/generators/seams/teams/templates/app/views/invitations/index.html.erb.tt +44 -0
- data/lib/generators/seams/teams/templates/app/views/memberships/index.html.erb.tt +32 -0
- data/lib/generators/seams/teams/templates/app/views/teams/edit.html.erb.tt +28 -0
- data/lib/generators/seams/teams/templates/app/views/teams/index.html.erb.tt +15 -0
- data/lib/generators/seams/teams/templates/app/views/teams/new.html.erb.tt +24 -0
- data/lib/generators/seams/teams/templates/app/views/teams/show.html.erb.tt +17 -0
- data/lib/generators/seams/teams/templates/config/routes.rb.tt +19 -0
- data/lib/generators/seams/teams/templates/db/migrate/create_team_invitations.rb.tt +24 -0
- data/lib/generators/seams/teams/templates/db/migrate/create_team_memberships.rb.tt +25 -0
- data/lib/generators/seams/teams/templates/db/migrate/create_teams.rb.tt +18 -0
- data/lib/generators/seams/teams/templates/lib/concerns/account_scoped.rb.tt +79 -0
- data/lib/generators/seams/teams/templates/lib/concerns/authorization.rb.tt +55 -0
- data/lib/generators/seams/teams/templates/lib/configuration.rb.tt +45 -0
- data/lib/generators/seams/teams/templates/lib/engine.rb.tt +51 -0
- data/lib/generators/seams/teams/templates/lib/teams.rb.tt +22 -0
- data/lib/generators/seams/teams/templates/spec/factories/teams.rb.tt +47 -0
- data/lib/generators/seams/teams/templates/spec/models/invitation_spec.rb.tt +25 -0
- data/lib/generators/seams/teams/templates/spec/models/membership_spec.rb.tt +29 -0
- data/lib/generators/seams/teams/templates/spec/models/team_spec.rb.tt +23 -0
- data/lib/generators/seams/teams/templates/spec/runtime/boot_spec.rb.tt +32 -0
- data/lib/seams/cli/list.rb +111 -0
- data/lib/seams/cli/quality.rb +99 -0
- data/lib/seams/cli/resolve.rb +276 -0
- data/lib/seams/cli/test_changed.rb +116 -0
- data/lib/seams/cli.rb +48 -0
- data/lib/seams/configuration.rb +19 -0
- data/lib/seams/cops/known_queue_names.rb +42 -0
- data/lib/seams/cops/migration_comments.rb +68 -0
- data/lib/seams/cops/no_cross_engine_dependency.rb +58 -0
- data/lib/seams/cops/no_cross_engine_model_access.rb +153 -0
- data/lib/seams/cops.rb +18 -0
- data/lib/seams/event_registry.rb +49 -0
- data/lib/seams/events/adapter.rb +24 -0
- data/lib/seams/events/adapters/active_support.rb +31 -0
- data/lib/seams/events/publisher.rb +178 -0
- data/lib/seams/events.rb +39 -0
- data/lib/seams/generators/dummy_app_writer.rb +424 -0
- data/lib/seams/generators/eject_aware.rb +102 -0
- data/lib/seams/generators/follow_up_generator.rb +148 -0
- data/lib/seams/generators/host_injector.rb +124 -0
- data/lib/seams/generators/sibling_rubocop_writer.rb +77 -0
- data/lib/seams/generators/splicer.rb +217 -0
- data/lib/seams/observability/adapter.rb +33 -0
- data/lib/seams/observability/adapters/rails_logger.rb +59 -0
- data/lib/seams/observability.rb +34 -0
- data/lib/seams/runtime.rb +23 -0
- data/lib/seams/version.rb +5 -0
- data/lib/seams.rb +23 -0
- metadata +493 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Factories for Notifications engine specs. Each strategy gets its
|
|
4
|
+
# own factory inheriting from :notification with the right STI type
|
|
5
|
+
# locked in. Sequences keep recipient/template values unique.
|
|
6
|
+
#
|
|
7
|
+
# Wave 9: the default Notification owner is :auth_identity (the
|
|
8
|
+
# canonical "human" model). The legacy :notifications_user factory
|
|
9
|
+
# is retained as an alias so older specs / generator-spec assertions
|
|
10
|
+
# keep working.
|
|
11
|
+
FactoryBot.define do
|
|
12
|
+
factory :notification, class: "Notifications::Notification" do
|
|
13
|
+
sequence(:template) { |n| "default-#{n}" }
|
|
14
|
+
type { "Notifications::Strategies::InApp" }
|
|
15
|
+
association :owner, factory: :auth_identity
|
|
16
|
+
|
|
17
|
+
factory :in_app_notification, class: "Notifications::Strategies::InApp" do
|
|
18
|
+
type { "Notifications::Strategies::InApp" }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
factory :email_notification, class: "Notifications::Strategies::Email" do
|
|
22
|
+
type { "Notifications::Strategies::Email" }
|
|
23
|
+
sequence(:recipient) { |n| "to-#{n}@example.com" }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
factory :sms_notification, class: "Notifications::Strategies::Sms" do
|
|
27
|
+
type { "Notifications::Strategies::Sms" }
|
|
28
|
+
sequence(:recipient) { |n| "+1555010#{format('%04d', n)}" }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
factory :auth_identity, class: "Auth::Identity" do
|
|
33
|
+
sequence(:email) { |n| "identity-#{n}@example.com" }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Backwards-compatible alias for specs that still reference
|
|
37
|
+
# :notifications_user. Returns an Auth::Identity (the polymorphic
|
|
38
|
+
# owner doesn't care about the class name as long as it's an AR
|
|
39
|
+
# record with an id).
|
|
40
|
+
factory :notifications_user, parent: :auth_identity
|
|
41
|
+
|
|
42
|
+
factory :notification_delivery, class: "Notifications::Delivery" do
|
|
43
|
+
association :notification, factory: :in_app_notification
|
|
44
|
+
sent_at { Time.current }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
factory :notification_preference, class: "Notifications::NotificationPreference" do
|
|
48
|
+
sequence(:identity_id) { |n| n }
|
|
49
|
+
channel { "email" }
|
|
50
|
+
notification_type { "default" }
|
|
51
|
+
enabled { true }
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Notifications::Delivery do
|
|
6
|
+
describe "associations" do
|
|
7
|
+
it "belongs to a Notification" do
|
|
8
|
+
delivery = create(:notification_delivery)
|
|
9
|
+
expect(delivery.notification).to be_a(Notifications::Notification)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "is destroyed when its parent notification is destroyed" do
|
|
13
|
+
notification = create(:in_app_notification)
|
|
14
|
+
delivery = create(:notification_delivery, notification: notification)
|
|
15
|
+
|
|
16
|
+
expect { notification.destroy }.to change(described_class, :count).by(-1)
|
|
17
|
+
expect(described_class.where(id: delivery.id)).to be_empty
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe "DB-level constraints" do
|
|
22
|
+
it "rejects a row without sent_at (NOT NULL at the DB level)" do
|
|
23
|
+
expect {
|
|
24
|
+
build(:notification_delivery, sent_at: nil).save(validate: false)
|
|
25
|
+
}.to raise_error(ActiveRecord::NotNullViolation)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/generators/seams/notifications/templates/spec/models/notification_preference_spec.rb.tt
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Notifications::NotificationPreference do
|
|
6
|
+
describe "validations" do
|
|
7
|
+
it "rejects an unknown channel" do
|
|
8
|
+
record = build(:notification_preference, channel: "carrier_pigeon")
|
|
9
|
+
expect(record).not_to be_valid
|
|
10
|
+
expect(record.errors[:channel]).not_to be_empty
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "requires identity_id and channel" do
|
|
14
|
+
record = described_class.new
|
|
15
|
+
expect(record).not_to be_valid
|
|
16
|
+
expect(record.errors[:identity_id]).not_to be_empty
|
|
17
|
+
expect(record.errors[:channel]).not_to be_empty
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "uniqueness scoped on (channel, notification_type) prevents duplicates per identity" do
|
|
21
|
+
create(:notification_preference, identity_id: 7, channel: "email", notification_type: "billing")
|
|
22
|
+
dup = build(:notification_preference, identity_id: 7, channel: "email", notification_type: "billing")
|
|
23
|
+
expect(dup).not_to be_valid
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe ".enabled?" do
|
|
28
|
+
it "returns true when no row exists for the identity/channel (default-on)" do
|
|
29
|
+
expect(described_class.enabled?(identity_id: 999, channel: "email")).to be(true)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "returns the stored value when a specific notification_type row exists" do
|
|
33
|
+
create(:notification_preference, identity_id: 7, channel: "email",
|
|
34
|
+
notification_type: "billing", enabled: false)
|
|
35
|
+
expect(described_class.enabled?(identity_id: 7, channel: "email",
|
|
36
|
+
notification_type: "billing")).to be(false)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "falls back to the channel-wide row (notification_type: nil) when no specific row exists" do
|
|
40
|
+
create(:notification_preference, identity_id: 7, channel: "email",
|
|
41
|
+
notification_type: nil, enabled: false)
|
|
42
|
+
expect(described_class.enabled?(identity_id: 7, channel: "email",
|
|
43
|
+
notification_type: "billing")).to be(false)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Notifications::Notification do
|
|
6
|
+
describe "validations" do
|
|
7
|
+
it "rejects an unknown type" do
|
|
8
|
+
record = build(:notification, type: "Notifications::Strategies::Carrier")
|
|
9
|
+
expect(record).not_to be_valid
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "requires a template" do
|
|
13
|
+
record = build(:notification, template: "")
|
|
14
|
+
expect(record).not_to be_valid
|
|
15
|
+
expect(record.errors[:template]).not_to be_empty
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "rejects template paths with traversal segments" do
|
|
19
|
+
record = build(:notification, template: "../../etc/passwd")
|
|
20
|
+
expect(record).not_to be_valid
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "accepts subdirectory templates with safe segments" do
|
|
24
|
+
record = build(:notification, template: "billing/invoice_paid")
|
|
25
|
+
expect(record).to be_valid
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe "STI subclasses" do
|
|
30
|
+
it "Strategies::InApp persists as the InApp type" do
|
|
31
|
+
record = create(:in_app_notification)
|
|
32
|
+
expect(Notifications::Notification.find(record.id))
|
|
33
|
+
.to be_a(Notifications::Strategies::InApp)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "Strategies::Email persists as the Email type" do
|
|
37
|
+
record = create(:email_notification)
|
|
38
|
+
expect(Notifications::Notification.find(record.id))
|
|
39
|
+
.to be_a(Notifications::Strategies::Email)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe "scopes" do
|
|
44
|
+
it ".due includes notifications whose next_delivery_at is in the past" do
|
|
45
|
+
due = create(:in_app_notification, next_delivery_at: 1.minute.ago)
|
|
46
|
+
late = create(:in_app_notification, next_delivery_at: 1.hour.from_now)
|
|
47
|
+
|
|
48
|
+
expect(described_class.due).to include(due)
|
|
49
|
+
expect(described_class.due).not_to include(late)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it ".unread filters out rows whose read_at is set" do
|
|
53
|
+
unread = create(:in_app_notification, read_at: nil)
|
|
54
|
+
read = create(:in_app_notification, read_at: Time.current)
|
|
55
|
+
|
|
56
|
+
expect(described_class.unread).to include(unread)
|
|
57
|
+
expect(described_class.unread).not_to include(read)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../rails_helper"
|
|
4
|
+
|
|
5
|
+
# End-to-end check that the in-app notification path:
|
|
6
|
+
# 1. Persists a Notifications::Strategies::InApp row.
|
|
7
|
+
# 2. Broadcasts on Notifications::NotificationChannel for the owner.
|
|
8
|
+
# 3. Renders something usable in the bell partial.
|
|
9
|
+
RSpec.describe "In-app notification bell + ActionCable broadcast",
|
|
10
|
+
type: :integration do
|
|
11
|
+
let(:identity) { create(:auth_identity) }
|
|
12
|
+
|
|
13
|
+
describe "InApp#dispatch!" do
|
|
14
|
+
it "broadcasts the notification payload on the owner's channel" do
|
|
15
|
+
notification = create(:in_app_notification, owner: identity, template: "default")
|
|
16
|
+
captured = nil
|
|
17
|
+
allow(Notifications::NotificationChannel).to receive(:broadcast_to) do |target, payload|
|
|
18
|
+
captured = { target: target, payload: payload }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# dispatch! is the private per-strategy hook called by send!.
|
|
22
|
+
# Invoking it directly keeps this spec focused on broadcast
|
|
23
|
+
# behaviour without needing to seed a due schedule.
|
|
24
|
+
notification.send(:dispatch!)
|
|
25
|
+
|
|
26
|
+
expect(captured).not_to be_nil
|
|
27
|
+
expect(captured[:target]).to eq(identity)
|
|
28
|
+
expect(captured[:payload]).to include(
|
|
29
|
+
id: notification.id,
|
|
30
|
+
template: "default"
|
|
31
|
+
)
|
|
32
|
+
expect(captured[:payload][:body]).to include("notification")
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe "rendered_content format selection" do
|
|
37
|
+
it "renders the html template for in-app + email contexts" do
|
|
38
|
+
notification = create(:in_app_notification, owner: identity, template: "default")
|
|
39
|
+
html = notification.rendered_content(format: :html)
|
|
40
|
+
expect(html).to include("<p>")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "renders the text template for sms + plain-text email contexts" do
|
|
44
|
+
notification = create(:in_app_notification, owner: identity, template: "default")
|
|
45
|
+
text = notification.rendered_content(format: :text)
|
|
46
|
+
expect(text).to include("notification")
|
|
47
|
+
expect(text).not_to include("<p>")
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
describe "bell partial" do
|
|
52
|
+
it "renders the unread count for the owner" do
|
|
53
|
+
create_list(:in_app_notification, 3, owner: identity, read_at: nil)
|
|
54
|
+
create(:in_app_notification, owner: identity, read_at: Time.current)
|
|
55
|
+
|
|
56
|
+
expect(identity.unread_in_app_notifications.count).to eq(3)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
data/lib/generators/seams/notifications/templates/spec/runtime/billing_subscriber_skip_spec.rb.tt
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails_helper"
|
|
4
|
+
|
|
5
|
+
# Verifies the deliberately-loud diagnostic that
|
|
6
|
+
# Notifications::BillingSubscriber emits when a billing event arrives
|
|
7
|
+
# without an `account_id`. That warn line is operationally important:
|
|
8
|
+
# without it, "no notification fired" looks identical to "subscriber
|
|
9
|
+
# never matched", and a tenant whose billing notifications silently
|
|
10
|
+
# fail is the worst class of seams bug to debug.
|
|
11
|
+
RSpec.describe "Notifications::BillingSubscriber missing-account_id signal", type: :runtime do
|
|
12
|
+
let(:adapter) { Seams::Observability.adapter }
|
|
13
|
+
|
|
14
|
+
before do
|
|
15
|
+
# The notifications dummy app doesn't host the billing engine, so
|
|
16
|
+
# billing events aren't registered here. Register them just for
|
|
17
|
+
# this spec so Publisher.publish accepts the bus call. Real hosts
|
|
18
|
+
# that mount both engines get the registration from billing's
|
|
19
|
+
# engine.rb at boot.
|
|
20
|
+
%w[
|
|
21
|
+
invoice.paid.billing
|
|
22
|
+
invoice.payment_failed.billing
|
|
23
|
+
subscription.activated.billing
|
|
24
|
+
subscription.cancelled.billing
|
|
25
|
+
subscription.trial_will_end.billing
|
|
26
|
+
lifetime.purchased.billing
|
|
27
|
+
].each do |event_name|
|
|
28
|
+
Seams::EventRegistry.register(event_name, emitted_by: "Billing")
|
|
29
|
+
end
|
|
30
|
+
Notifications::BillingSubscriber.attach!
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
context "when a billing event arrives with no account_id key at all" do
|
|
34
|
+
it "logs notifications.billing_subscriber.skip via the observability adapter" do
|
|
35
|
+
expect(adapter).to receive(:warn).with(
|
|
36
|
+
"notifications.billing_subscriber.skip",
|
|
37
|
+
hash_including(engine: "notifications", reason: /missing account_id/i)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
Seams::Events::Publisher.publish(
|
|
41
|
+
"invoice.paid.billing",
|
|
42
|
+
customer_ref: "cus_no_account_id",
|
|
43
|
+
invoice_ref: "in_no_account_id"
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "does not enqueue a CreateNotificationJob" do
|
|
48
|
+
allow(adapter).to receive(:warn) # silence
|
|
49
|
+
expect(Notifications::CreateNotificationJob).not_to receive(:perform_later)
|
|
50
|
+
|
|
51
|
+
Seams::Events::Publisher.publish(
|
|
52
|
+
"invoice.paid.billing",
|
|
53
|
+
customer_ref: "cus_no_account_id"
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
context "when account_id is present but blank" do
|
|
59
|
+
it "treats blank string as missing and logs the same warn line" do
|
|
60
|
+
expect(adapter).to receive(:warn).with(
|
|
61
|
+
"notifications.billing_subscriber.skip",
|
|
62
|
+
hash_including(reason: /missing account_id/i)
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
Seams::Events::Publisher.publish(
|
|
66
|
+
"invoice.paid.billing",
|
|
67
|
+
account_id: "",
|
|
68
|
+
customer_ref: "cus_blank"
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
context "when account_id is a valid UUID string" do
|
|
74
|
+
it "does NOT emit the skip warning and DOES enqueue the job" do
|
|
75
|
+
expect(adapter).not_to receive(:warn).with("notifications.billing_subscriber.skip", any_args)
|
|
76
|
+
expect(Notifications::CreateNotificationJob).to receive(:perform_later).with(
|
|
77
|
+
hash_including(owner_id: "00000000-0000-0000-0000-000000000abc")
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
Seams::Events::Publisher.publish(
|
|
81
|
+
"invoice.paid.billing",
|
|
82
|
+
account_id: "00000000-0000-0000-0000-000000000abc",
|
|
83
|
+
customer_ref: "cus_with_account"
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../rails_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Notifications engine boot", type: :integration do
|
|
6
|
+
it "loads the engine" do
|
|
7
|
+
expect(defined?(Notifications::Engine)).to eq("constant")
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "registers the three canonical notification events" do
|
|
11
|
+
%w[
|
|
12
|
+
notification.queued.notifications
|
|
13
|
+
notification.delivered.notifications
|
|
14
|
+
notification.failed.notifications
|
|
15
|
+
].each do |event|
|
|
16
|
+
expect(Seams::EventRegistry.registered?(event)).to be(true)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "creates the notification tables from the dummy schema" do
|
|
21
|
+
%i[notifications notification_preferences notification_deliveries].each do |t|
|
|
22
|
+
expect(ActiveRecord::Base.connection.table_exists?(t)).to be(true), "missing #{t}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "exposes the strategy STI subclasses" do
|
|
27
|
+
expect(Notifications::Strategies::InApp.superclass).to eq(Notifications::Notification)
|
|
28
|
+
expect(Notifications::Strategies::Email.superclass).to eq(Notifications::Notification)
|
|
29
|
+
expect(Notifications::Strategies::Sms.superclass).to eq(Notifications::Notification)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "Notifiable can be mixed into Auth::Identity (the canonical Wave-9 owner)" do
|
|
33
|
+
# Wave 9 dropped the auto-include into a host User (no canonical
|
|
34
|
+
# host User after Wave 9 — the human is Auth::Identity). The
|
|
35
|
+
# dummy app exercises the "include Notifiable on Auth::Identity"
|
|
36
|
+
# pattern documented in the engine's README.
|
|
37
|
+
expect(Auth::Identity.ancestors).to include(Notifications::Notifiable)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../rails_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Notifications schedule round-trip", type: :integration do
|
|
6
|
+
let(:identity) { Auth::Identity.create!(email: "x@y.com") }
|
|
7
|
+
|
|
8
|
+
describe "ice_cube serialisation" do
|
|
9
|
+
it "round-trips an immediate one-shot" do
|
|
10
|
+
sched = IceCube::Schedule.new(Time.current)
|
|
11
|
+
n = Notifications::Strategies::InApp.new(owner: identity, template: "default")
|
|
12
|
+
n.schedule = sched
|
|
13
|
+
n.save!
|
|
14
|
+
|
|
15
|
+
reloaded = Notifications::Notification.find(n.id)
|
|
16
|
+
expect(reloaded.schedule).to be_a(IceCube::Schedule)
|
|
17
|
+
expect(reloaded.next_delivery_at).to be_within(1.second).of(sched.first)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "round-trips a weekly recurring schedule" do
|
|
21
|
+
sched = IceCube::Schedule.new(Time.current)
|
|
22
|
+
sched.add_recurrence_rule(IceCube::Rule.weekly)
|
|
23
|
+
|
|
24
|
+
n = Notifications::Strategies::Email.new(owner: identity, template: "default")
|
|
25
|
+
n.schedule = sched
|
|
26
|
+
n.save!
|
|
27
|
+
|
|
28
|
+
reloaded = Notifications::Notification.find(n.id)
|
|
29
|
+
expect(reloaded.schedule.recurrence_rules.first).to be_a(IceCube::WeeklyRule)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe "schedule_config=" do
|
|
34
|
+
it "builds an IceCube schedule from a structured hash" do
|
|
35
|
+
n = Notifications::Strategies::Email.new(owner: identity, template: "default")
|
|
36
|
+
n.schedule_config = { starts_at: 1.day.from_now, frequency: "daily", interval: 2 }
|
|
37
|
+
n.save!
|
|
38
|
+
|
|
39
|
+
expect(n.schedule).to be_a(IceCube::Schedule)
|
|
40
|
+
expect(n.schedule.recurrence_rules.first).to be_a(IceCube::DailyRule)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe "#advance!" do
|
|
45
|
+
it "sets next_delivery_at to nil for a completed one-shot" do
|
|
46
|
+
sched = IceCube::Schedule.new(2.days.ago)
|
|
47
|
+
n = Notifications::Strategies::InApp.new(owner: identity, template: "default")
|
|
48
|
+
n.schedule = sched
|
|
49
|
+
n.save!
|
|
50
|
+
n.advance!
|
|
51
|
+
|
|
52
|
+
expect(n.reload.next_delivery_at).to be_nil
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|