backend-manager 5.0.38 → 5.0.40
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.
- package/.claude/settings.local.json +3 -2
- package/.codeclimate.yml +32 -0
- package/CHANGELOG.md +25 -0
- package/CLAUDE.md +106 -12
- package/README.md +106 -7
- package/REFACTOR-BEM-API.md +76 -0
- package/REFACTOR-MIDDLEWARE.md +62 -0
- package/REFACTOR-PAYMENT.md +61 -0
- package/TODO.md +149 -0
- package/bin/bem +1 -1
- package/package.json +11 -10
- package/src/cli/commands/base-command.js +129 -0
- package/src/cli/commands/deploy.js +0 -5
- package/src/cli/commands/emulators.js +90 -0
- package/src/cli/commands/index.js +2 -0
- package/src/cli/commands/serve.js +5 -7
- package/src/cli/commands/setup-tests/emulators-config.js +79 -0
- package/src/cli/commands/setup-tests/env-file.js +109 -0
- package/src/cli/commands/setup-tests/env-runtime-config-deprecated.js +45 -0
- package/src/cli/commands/setup-tests/firestore-rules-file.js +11 -5
- package/src/cli/commands/setup-tests/gitignore.js +99 -18
- package/src/cli/commands/setup-tests/helpers/merge-line-files.js +181 -0
- package/src/cli/commands/setup-tests/hosting-folder.js +3 -3
- package/src/cli/commands/setup-tests/hosting-rewrites.js +1 -1
- package/src/cli/commands/setup-tests/index.js +10 -8
- package/src/cli/commands/setup-tests/legacy-tests-cleanup.js +43 -0
- package/src/cli/commands/setup-tests/npm-project-scripts.js +45 -0
- package/src/cli/commands/setup.js +12 -10
- package/src/cli/commands/test.js +201 -11
- package/src/cli/commands/watch.js +121 -0
- package/src/cli/{cli-refactored.js → index.js} +14 -0
- package/src/manager/cron/daily/ghostii-auto-publisher.js +278 -0
- package/src/manager/cron/daily/reset-usage.js +140 -0
- package/src/manager/cron/daily.js +54 -0
- package/src/manager/events/auth/before-create.js +52 -0
- package/src/manager/events/auth/before-signin.js +42 -0
- package/src/manager/events/auth/on-create.js +270 -0
- package/src/manager/events/auth/on-delete.js +94 -0
- package/src/manager/events/firestore/notifications/on-write.js +77 -0
- package/src/manager/functions/{core → _legacy}/actions/create-post-handler.js +17 -16
- package/src/manager/functions/{core → _legacy}/actions/generate-uuid.js +1 -1
- package/src/manager/functions/{core → _legacy}/actions/sign-up-handler.js +6 -6
- package/src/manager/functions/{core → _legacy}/admin/create-post.js +3 -4
- package/src/manager/functions/core/actions/api/admin/create-post.js +1 -1
- package/src/manager/functions/core/actions/api/admin/database-write.js +1 -1
- package/src/manager/functions/core/actions/api/admin/edit-post.js +12 -3
- package/src/manager/functions/core/actions/api/admin/get-stats.js +9 -0
- package/src/manager/functions/core/actions/api/admin/send-email.js +416 -192
- package/src/manager/functions/core/actions/api/admin/send-notification.js +66 -8
- package/src/manager/functions/core/actions/api/admin/sync-users.js +2 -2
- package/src/manager/functions/core/actions/api/admin/write-repo-content.js +11 -3
- package/src/manager/functions/core/actions/api/general/add-marketing-contact.js +604 -0
- package/src/manager/functions/core/actions/api/general/fetch-post.js +10 -1
- package/src/manager/functions/core/actions/api/general/generate-uuid.js +1 -1
- package/src/manager/functions/core/actions/api/general/remove-marketing-contact.js +234 -0
- package/src/manager/functions/core/actions/api/general/send-email.js +9 -5
- package/src/manager/functions/core/actions/api/special/setup-electron-manager-client.js +2 -3
- package/src/manager/functions/core/actions/api/test/health.js +24 -0
- package/src/manager/functions/core/actions/api/user/create-custom-token.js +1 -3
- package/src/manager/functions/core/actions/api/user/delete.js +2 -2
- package/src/manager/functions/core/actions/api/user/get-active-sessions.js +2 -4
- package/src/manager/functions/core/actions/api/user/get-subscription-info.js +7 -8
- package/src/manager/functions/core/actions/api/user/oauth2.js +1 -1
- package/src/manager/functions/core/actions/api/user/regenerate-api-keys.js +1 -2
- package/src/manager/functions/core/actions/api/user/sign-out-all-sessions.js +2 -3
- package/src/manager/functions/core/actions/api/user/sign-up.js +182 -57
- package/src/manager/functions/core/actions/api/user/submit-feedback.js +1 -2
- package/src/manager/functions/core/actions/api.js +44 -12
- package/src/manager/helpers/analytics.js +1 -1
- package/src/manager/helpers/api-manager.js +4 -4
- package/src/manager/helpers/assistant.js +7 -2
- package/src/manager/helpers/event-middleware.js +74 -0
- package/src/manager/helpers/middleware.js +26 -23
- package/src/manager/helpers/settings.js +17 -13
- package/src/manager/helpers/subscription-resolver-new.js +44 -45
- package/src/manager/helpers/subscription-resolver.js +44 -45
- package/src/manager/helpers/usage.js +1 -4
- package/src/manager/helpers/user.js +84 -66
- package/src/manager/index.js +61 -77
- package/src/manager/libraries/disposable-domains.json +839 -0
- package/src/manager/routes/admin/backup/post.js +105 -0
- package/src/manager/routes/admin/cron/post.js +37 -0
- package/src/manager/routes/admin/database/get.js +33 -0
- package/src/manager/routes/admin/database/post.js +32 -0
- package/src/manager/routes/admin/email/post.js +451 -0
- package/src/manager/routes/admin/firestore/get.js +33 -0
- package/src/manager/routes/admin/firestore/post.js +54 -0
- package/src/manager/routes/admin/firestore/query/post.js +126 -0
- package/src/manager/routes/admin/hook/post.js +83 -0
- package/src/manager/routes/admin/notification/post.js +260 -0
- package/src/manager/routes/admin/payment/post.js +56 -0
- package/src/manager/routes/admin/post/post.js +301 -0
- package/src/manager/routes/admin/post/put.js +136 -0
- package/src/manager/routes/admin/post/templates/post.html +15 -0
- package/src/manager/routes/admin/repo/content/post.js +102 -0
- package/src/manager/routes/admin/stats/get.js +213 -0
- package/src/manager/routes/admin/users/sync/post.js +119 -0
- package/src/manager/routes/content/post/get.js +104 -0
- package/src/manager/routes/firebase/providers/get.js +80 -0
- package/src/manager/routes/general/email/post.js +99 -0
- package/src/manager/routes/general/email/templates/download-app-link.js +25 -0
- package/src/manager/routes/general/uuid/post.js +34 -0
- package/src/manager/routes/handler/post/post.js +131 -0
- package/src/manager/routes/index.js +4 -20
- package/src/manager/routes/marketing/contact/delete.js +207 -0
- package/src/manager/routes/marketing/contact/post.js +504 -0
- package/src/manager/routes/restart/index.js +10 -22
- package/src/manager/routes/special/electron-client/post.js +72 -0
- package/src/manager/routes/test/authenticate/get.js +8 -0
- package/src/manager/routes/test/health/get.js +13 -0
- package/src/manager/routes/test/index.js +2 -26
- package/src/manager/routes/test/lab/post.js +17 -0
- package/src/manager/routes/test/redirect/get.js +9 -0
- package/src/manager/routes/test/schema/post.js +20 -0
- package/src/manager/routes/test/usage/post.js +50 -0
- package/src/manager/routes/test/webhook/post.js +21 -0
- package/src/manager/routes/user/api-keys/post.js +49 -0
- package/src/manager/routes/user/delete.js +71 -0
- package/src/manager/routes/user/feedback/post.js +85 -0
- package/src/manager/routes/user/get.js +14 -0
- package/src/manager/routes/user/oauth2/post.js +323 -0
- package/src/manager/routes/user/oauth2/providers/discord.js +47 -0
- package/src/manager/routes/user/oauth2/providers/google.js +38 -0
- package/src/manager/routes/user/sessions/delete.js +93 -0
- package/src/manager/routes/user/sessions/get.js +38 -0
- package/src/manager/routes/user/settings/validate/post.js +85 -0
- package/src/manager/routes/user/signup/post.js +185 -0
- package/src/manager/routes/user/subscription/get.js +61 -0
- package/src/manager/routes/user/token/post.js +28 -0
- package/src/manager/schemas/admin/backup/post.js +6 -0
- package/src/manager/schemas/admin/cron/post.js +6 -0
- package/src/manager/schemas/admin/database/get.js +7 -0
- package/src/manager/schemas/admin/database/post.js +12 -0
- package/src/manager/schemas/admin/email/post.js +20 -0
- package/src/manager/schemas/admin/firestore/get.js +7 -0
- package/src/manager/schemas/admin/firestore/post.js +22 -0
- package/src/manager/schemas/admin/firestore/query/post.js +7 -0
- package/src/manager/schemas/admin/hook/post.js +6 -0
- package/src/manager/schemas/admin/notification/post.js +21 -0
- package/src/manager/schemas/admin/payment/post.js +6 -0
- package/src/manager/schemas/admin/post/post.js +20 -0
- package/src/manager/schemas/admin/post/put.js +11 -0
- package/src/manager/schemas/admin/repo/content/post.js +10 -0
- package/src/manager/schemas/admin/stats/get.js +7 -0
- package/src/manager/schemas/admin/users/sync/post.js +6 -0
- package/src/manager/schemas/content/post/get.js +6 -0
- package/src/manager/schemas/firebase/providers/get.js +6 -0
- package/src/manager/schemas/general/email/post.js +8 -0
- package/src/manager/schemas/general/uuid/post.js +22 -0
- package/src/manager/schemas/handler/post/post.js +11 -0
- package/src/manager/schemas/marketing/contact/delete.js +7 -0
- package/src/manager/schemas/marketing/contact/post.js +13 -0
- package/src/manager/schemas/restart/index.js +6 -13
- package/src/manager/schemas/special/electron-client/post.js +9 -0
- package/src/manager/schemas/test/authenticate/get.js +1 -0
- package/src/manager/schemas/test/health/get.js +1 -0
- package/src/manager/schemas/test/index.js +6 -13
- package/src/manager/schemas/test/lab/post.js +1 -0
- package/src/manager/schemas/test/redirect/get.js +7 -0
- package/src/manager/schemas/test/schema/post.js +145 -0
- package/src/manager/schemas/test/usage/post.js +7 -0
- package/src/manager/schemas/test/webhook/post.js +17 -0
- package/src/manager/schemas/user/api-keys/post.js +12 -0
- package/src/manager/schemas/user/delete.js +7 -0
- package/src/manager/schemas/user/feedback/post.js +22 -0
- package/src/manager/schemas/user/get.js +1 -0
- package/src/manager/schemas/user/oauth2/post.js +77 -0
- package/src/manager/schemas/user/sessions/delete.js +12 -0
- package/src/manager/schemas/user/sessions/get.js +12 -0
- package/src/manager/schemas/user/settings/validate/post.js +22 -0
- package/src/manager/schemas/user/signup/post.js +22 -0
- package/src/manager/schemas/user/subscription/get.js +7 -0
- package/src/manager/schemas/user/token/post.js +7 -0
- package/src/test/run-tests.js +51 -0
- package/src/test/runner.js +820 -0
- package/src/test/test-accounts.js +456 -0
- package/src/test/utils/assertions.js +189 -0
- package/src/test/utils/firestore-rules-client.js +284 -0
- package/src/test/utils/http-client.js +251 -0
- package/templates/_.env +31 -0
- package/templates/_.gitignore +54 -0
- package/templates/firestore.rules +3 -3
- package/test/_init/accounts-validation.js +58 -0
- package/test/_legacy/ai/index.js +231 -0
- package/test/_legacy/ai/test.jpg +0 -0
- package/test/_legacy/ai/test.pdf +0 -0
- package/test/_legacy/cli-commands.test.js +224 -0
- package/test/_legacy/index.js +31 -0
- package/test/_legacy/payment-resolver/chargebee/orders/refunded.json +0 -0
- package/test/_legacy/payment-resolver/chargebee/orders/unpaid.json +98 -0
- package/test/_legacy/payment-resolver/chargebee/subscriptions/active.json +578 -0
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-in-trial.json +38 -0
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-skipped-to-active.json +135 -0
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-to-active-to-cancelled.json +42 -0
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-to-active.json +44 -0
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-to-cancelled.json +39 -0
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-to-refund.json +143 -0
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-to-suspended.json +48 -0
- package/test/_legacy/payment-resolver/coinbase/orders/regular.json +204 -0
- package/test/_legacy/payment-resolver/coinbase/subscriptions/cancelled.json +204 -0
- package/test/_legacy/payment-resolver/coinbase/subscriptions/paid-2.json +125 -0
- package/test/_legacy/payment-resolver/index.js +1558 -0
- package/test/_legacy/payment-resolver/paypal/orders/refunded.json +0 -0
- package/test/_legacy/payment-resolver/paypal/orders/regular.json +120 -0
- package/test/_legacy/payment-resolver/paypal/subscriptions/active-refund-previous-stmnt.json +323 -0
- package/test/_legacy/payment-resolver/paypal/subscriptions/active.json +192 -0
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-in-trial.json +125 -0
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-payment-not-complete.json +76 -0
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-payment-overdue-2.json +114 -0
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-payment-overdue.json +114 -0
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-to-active-to-cancelled.json +111 -0
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-to-active.json +132 -0
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-to-cancelled.json +107 -0
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-to-expired.json +91 -0
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-to-refund.json +147 -0
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-to-suspended.json +120 -0
- package/test/_legacy/payment-resolver/stripe/orders/refunded.json +0 -0
- package/test/_legacy/payment-resolver/stripe/orders/regular.json +91 -0
- package/test/_legacy/payment-resolver/stripe/subscriptions/active.json +421 -0
- package/test/_legacy/payment-resolver/stripe/subscriptions/trial-in-trial.json +349 -0
- package/test/_legacy/payment-resolver/stripe/subscriptions/trial-to-active-failed-authorization.json +430 -0
- package/test/_legacy/payment-resolver/stripe/subscriptions/trial-to-active.json +319 -0
- package/test/_legacy/payment-resolver/stripe/subscriptions/trial-to-cancelled.json +319 -0
- package/test/_legacy/payment-resolver/stripe/subscriptions/trial-to-refund.json +414 -0
- package/test/_legacy/payment-resolver/stripe/subscriptions/trial-to-suspended.json +319 -0
- package/test/_legacy/payment-resolver/stripe/subscriptions/unsure.json +339 -0
- package/test/_legacy/test-new +1178 -0
- package/test/_legacy/test.md +89 -0
- package/test/_legacy/usage.js +170 -0
- package/test/_legacy/user.js +31 -0
- package/test/functions/admin/database-read.js +148 -0
- package/test/functions/admin/database-write.js +173 -0
- package/test/functions/admin/edit-post.js +366 -0
- package/test/functions/admin/firestore-query.js +221 -0
- package/test/functions/admin/firestore-read.js +144 -0
- package/test/functions/admin/firestore-write.js +116 -0
- package/test/functions/admin/get-stats.js +115 -0
- package/test/functions/admin/send-email.js +206 -0
- package/test/functions/admin/send-notification.js +208 -0
- package/test/functions/admin/write-repo-content.js +223 -0
- package/test/functions/general/add-marketing-contact.js +373 -0
- package/test/functions/general/fetch-post.js +54 -0
- package/test/functions/general/generate-uuid.js +114 -0
- package/test/functions/test/authenticate.js +64 -0
- package/test/functions/user/create-custom-token.js +73 -0
- package/test/functions/user/delete.js +135 -0
- package/test/functions/user/get-active-sessions.js +111 -0
- package/test/functions/user/get-subscription-info.js +105 -0
- package/test/functions/user/regenerate-api-keys.js +156 -0
- package/test/functions/user/resolve.js +52 -0
- package/test/functions/user/sign-out-all-sessions.js +94 -0
- package/test/functions/user/sign-up.js +214 -0
- package/test/functions/user/submit-feedback.js +104 -0
- package/test/functions/user/validate-settings.js +82 -0
- package/test/routes/admin/database.js +144 -0
- package/test/routes/admin/email.js +163 -0
- package/test/routes/admin/firestore-query.js +217 -0
- package/test/routes/admin/firestore.js +141 -0
- package/test/routes/admin/notification.js +198 -0
- package/test/routes/admin/post.js +366 -0
- package/test/routes/admin/repo-content.js +223 -0
- package/test/routes/admin/stats.js +112 -0
- package/test/routes/content/post.js +54 -0
- package/test/routes/general/uuid.js +115 -0
- package/test/routes/marketing/contact.js +375 -0
- package/test/routes/test/authenticate.js +59 -0
- package/test/routes/test/schema.js +554 -0
- package/test/routes/test/usage.js +228 -0
- package/test/routes/user/api-keys.js +156 -0
- package/test/routes/user/delete.js +136 -0
- package/test/routes/user/feedback.js +104 -0
- package/test/routes/user/sessions.js +199 -0
- package/test/routes/user/settings-validate.js +82 -0
- package/test/routes/user/signup.js +214 -0
- package/test/routes/user/subscription.js +105 -0
- package/test/routes/user/token.js +73 -0
- package/test/routes/user/user.js +153 -0
- package/test/rules/notifications.js +410 -0
- package/test/rules/user.js +366 -0
- package/_backup/analytics copy.js +0 -217
- package/_backup/assistant-old-auth.js +0 -116
- package/_backup/create-post-old.js +0 -215
- package/_backup/get-providers copy.js +0 -154
- package/firebase-debug.log +0 -444
- package/src/cli/cli.js +0 -1435
- package/src/cli/commands/setup-tests/backend-manager-tests-file.js +0 -23
- package/src/cli/commands/setup-tests/env-runtime-config.js +0 -68
- package/src/cli/commands/setup-tests/npm-dist-script.js +0 -20
- package/src/cli/commands/setup-tests/npm-start-script.js +0 -20
- package/src/manager/functions/core/actions/api/handler/create-post.js +0 -146
- package/src/manager/functions/core/actions/api/test/create-test-accounts.js +0 -27
- package/src/manager/functions/core/actions/api/user/sign-up copy.js +0 -544
- package/src/manager/functions/core/cron/daily/ghostii-auto-publisher.js +0 -376
- package/src/manager/functions/core/cron/daily/reset-usage.js +0 -173
- package/src/manager/functions/core/cron/daily.js +0 -114
- package/src/manager/functions/core/events/auth/before-create.js +0 -124
- package/src/manager/functions/core/events/auth/before-signin.js +0 -62
- package/src/manager/functions/core/events/auth/on-create copy.js +0 -121
- package/src/manager/functions/core/events/auth/on-create.js +0 -564
- package/src/manager/functions/core/events/auth/on-delete.js +0 -72
- package/src/manager/functions/core/events/firestore/notifications/on-write.js +0 -118
- package/src/manager/functions/test/create-test-accounts.js +0 -144
- package/templates/backend-manager-tests.js +0 -153
- package/templates/gitignore.md +0 -4
- /package/src/manager/functions/{core → _legacy}/admin/firestore-write.js +0 -0
- /package/src/manager/functions/{core → _legacy}/admin/get-stats.js +0 -0
- /package/src/manager/functions/{core → _legacy}/admin/query.js +0 -0
- /package/src/manager/functions/{core → _legacy}/admin/send-notification.js +0 -0
- /package/src/manager/functions/{core/actions/api → _legacy}/template.js +0 -0
- /package/src/manager/functions/{test → _legacy/test}/authenticate.js +0 -0
- /package/src/manager/functions/{test → _legacy/test}/webhook.js +0 -0
package/.codeclimate.yml
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
version: "2" # required to adjust maintainability checks
|
|
2
|
+
checks:
|
|
3
|
+
# argument-count:
|
|
4
|
+
# config:
|
|
5
|
+
# threshold: 4
|
|
6
|
+
# complex-logic:
|
|
7
|
+
# config:
|
|
8
|
+
# threshold: 4
|
|
9
|
+
# file-lines:
|
|
10
|
+
# config:
|
|
11
|
+
# threshold: 250
|
|
12
|
+
# method-complexity:
|
|
13
|
+
# config:
|
|
14
|
+
# threshold: 5
|
|
15
|
+
# method-count:
|
|
16
|
+
# config:
|
|
17
|
+
# threshold: 20
|
|
18
|
+
method-lines:
|
|
19
|
+
config:
|
|
20
|
+
threshold: 200
|
|
21
|
+
# nested-control-flow:
|
|
22
|
+
# config:
|
|
23
|
+
# threshold: 4
|
|
24
|
+
# return-statements:
|
|
25
|
+
# config:
|
|
26
|
+
# threshold: 4
|
|
27
|
+
# similar-code:
|
|
28
|
+
# config:
|
|
29
|
+
# threshold: # language-specific defaults. an override will affect all languages.
|
|
30
|
+
identical-code:
|
|
31
|
+
config:
|
|
32
|
+
threshold: 10
|
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,31 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
14
14
|
- `Fixed` for any bug fixes.
|
|
15
15
|
- `Security` in case of vulnerabilities.
|
|
16
16
|
|
|
17
|
+
# [5.0.39] - 2025-01-12
|
|
18
|
+
### Added
|
|
19
|
+
- New test infrastructure with Firebase emulator support for reliable, isolated testing.
|
|
20
|
+
- Test runner with emulator auto-detection and startup.
|
|
21
|
+
- Test types: standalone, suite (sequential with shared state), group (independent).
|
|
22
|
+
- Built-in test accounts with SSOT configuration (basic, admin, premium-active, etc.).
|
|
23
|
+
- Firestore security rules testing support.
|
|
24
|
+
- HTTP client with auth helpers (`http.as('admin').command()`).
|
|
25
|
+
- Rich assertion library (`isSuccess`, `isError`, `hasProperty`, etc.).
|
|
26
|
+
- New `bm emulators` command for standalone emulator management.
|
|
27
|
+
- Enhanced `bm test` with path filtering and parallel test support.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- Reorganized test files to `test/functions/` with `admin/`, `user/`, `general/` categories.
|
|
31
|
+
- Standardized auth test naming to `unauthenticated-rejected`.
|
|
32
|
+
- Auth rejection tests moved to end of test files (before cleanup).
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
- Changed unauthenticated API error from 500 to 401 with proper "Authentication required" message.
|
|
36
|
+
|
|
37
|
+
### Removed
|
|
38
|
+
- Removed legacy test files (moved to `test/_legacy/`).
|
|
39
|
+
- Removed deprecated CLI files and templates.
|
|
40
|
+
- Consolidated test account creation from API endpoint to test runner.
|
|
41
|
+
|
|
17
42
|
# [5.0.31] - 2025-01-17
|
|
18
43
|
### Changed
|
|
19
44
|
- Refactored CLI to modular command architecture with individual command classes and test files for better maintainability.
|
package/CLAUDE.md
CHANGED
|
@@ -71,7 +71,7 @@ src/
|
|
|
71
71
|
routes/ # Built-in routes
|
|
72
72
|
schemas/ # Built-in schemas
|
|
73
73
|
cli/
|
|
74
|
-
|
|
74
|
+
index.js # CLI entry point
|
|
75
75
|
commands/ # CLI commands
|
|
76
76
|
templates/
|
|
77
77
|
backend-manager-config.json # Config template
|
|
@@ -384,19 +384,112 @@ Manager.handlers.bm_api = function (mod, position) {
|
|
|
384
384
|
|
|
385
385
|
## Testing
|
|
386
386
|
|
|
387
|
+
### Running Tests
|
|
387
388
|
```bash
|
|
388
|
-
#
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
389
|
+
# Option 1: Two terminals
|
|
390
|
+
npx bm emulators # Terminal 1 - keeps emulators running
|
|
391
|
+
npx bm test # Terminal 2 - runs tests
|
|
392
|
+
|
|
393
|
+
# Option 2: Single command (auto-starts emulators)
|
|
394
|
+
npx bm test
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Filtering Tests
|
|
398
|
+
```bash
|
|
399
|
+
npx bm test rules/ # Run rules tests (both BEM and project)
|
|
400
|
+
npx bm test bem:rules/ # Only BEM's rules tests
|
|
401
|
+
npx bm test project:rules/ # Only project's rules tests
|
|
402
|
+
npx bm test user/ admin/ # Multiple paths
|
|
398
403
|
```
|
|
399
404
|
|
|
405
|
+
### Test Locations
|
|
406
|
+
- **BEM core tests:** `test/`
|
|
407
|
+
- **Project tests:** `functions/test/bem/`
|
|
408
|
+
|
|
409
|
+
Use `bem:` or `project:` prefix to filter by source.
|
|
410
|
+
|
|
411
|
+
### Test Types
|
|
412
|
+
|
|
413
|
+
| Type | Use When | Behavior |
|
|
414
|
+
|------|----------|----------|
|
|
415
|
+
| Standalone | Single logical test | Runs once |
|
|
416
|
+
| Suite (`type: 'suite'`) | Sequential dependent tests | Shared state, stops on failure |
|
|
417
|
+
| Group (`type: 'group'`) | Multiple independent tests | Continues on failure |
|
|
418
|
+
|
|
419
|
+
### Standalone Test
|
|
420
|
+
```javascript
|
|
421
|
+
module.exports = {
|
|
422
|
+
description: 'Test name',
|
|
423
|
+
auth: 'none', // none, user, admin, premium-active, premium-expired
|
|
424
|
+
timeout: 10000,
|
|
425
|
+
async run({ http, assert, accounts, firestore, state, waitFor }) { },
|
|
426
|
+
async cleanup({ ... }) { }, // Optional
|
|
427
|
+
};
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### Suite (Sequential with Shared State)
|
|
431
|
+
```javascript
|
|
432
|
+
module.exports = {
|
|
433
|
+
description: 'Suite name',
|
|
434
|
+
type: 'suite',
|
|
435
|
+
tests: [
|
|
436
|
+
{ name: 'step-1', async run({ state }) { state.value = 'shared'; } },
|
|
437
|
+
{ name: 'step-2', async run({ state }) { /* state.value available */ } },
|
|
438
|
+
],
|
|
439
|
+
};
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Group (Independent Tests)
|
|
443
|
+
```javascript
|
|
444
|
+
module.exports = {
|
|
445
|
+
description: 'Group name',
|
|
446
|
+
type: 'group',
|
|
447
|
+
tests: [
|
|
448
|
+
{ name: 'test-1', auth: 'admin', async run({ http, assert }) { } },
|
|
449
|
+
{ name: 'test-2', auth: 'none', async run({ http, assert }) { } },
|
|
450
|
+
],
|
|
451
|
+
};
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Context Object
|
|
455
|
+
| Property | Description |
|
|
456
|
+
|----------|-------------|
|
|
457
|
+
| `http` | HTTP client (`http.command()`, `http.as('admin').command()`) |
|
|
458
|
+
| `assert` | Assertion helpers (see below) |
|
|
459
|
+
| `accounts` | Test accounts `{ basic, admin, premium-active, ... }` |
|
|
460
|
+
| `firestore` | Direct DB access (`get`, `set`, `delete`, `exists`) |
|
|
461
|
+
| `state` | Shared state (suites only) |
|
|
462
|
+
| `waitFor` | Polling helper `waitFor(condition, timeout, interval)` |
|
|
463
|
+
|
|
464
|
+
### Assert Methods
|
|
465
|
+
```javascript
|
|
466
|
+
assert.ok(value, message) // Truthy
|
|
467
|
+
assert.equal(a, b, message) // Strict equality
|
|
468
|
+
assert.notEqual(a, b, message) // Not equal
|
|
469
|
+
assert.deepEqual(a, b, message) // Deep equality
|
|
470
|
+
assert.match(value, /regex/, message) // Regex match
|
|
471
|
+
assert.isSuccess(response, message) // Response success
|
|
472
|
+
assert.isError(response, code, message) // Response error with code
|
|
473
|
+
assert.hasProperty(obj, 'path.to.prop', msg) // Property exists
|
|
474
|
+
assert.propertyEquals(obj, 'path', value, msg) // Property value
|
|
475
|
+
assert.isType(value, 'string', message) // Type check
|
|
476
|
+
assert.contains(array, value, message) // Array includes
|
|
477
|
+
assert.inRange(value, min, max, message) // Number range
|
|
478
|
+
assert.fail(message) // Explicit fail
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### Auth Levels
|
|
482
|
+
`none`, `user`/`basic`, `admin`, `premium-active`, `premium-expired`
|
|
483
|
+
|
|
484
|
+
### Key Test Files
|
|
485
|
+
| File | Purpose |
|
|
486
|
+
|------|---------|
|
|
487
|
+
| `src/test/runner.js` | Test runner |
|
|
488
|
+
| `test/` | BEM core tests |
|
|
489
|
+
| `src/test/utils/assertions.js` | Assert helpers |
|
|
490
|
+
| `src/test/utils/http-client.js` | HTTP client |
|
|
491
|
+
| `src/test/test-accounts.js` | Test account definitions |
|
|
492
|
+
|
|
400
493
|
## Common Mistakes to Avoid
|
|
401
494
|
|
|
402
495
|
1. **Don't modify Manager internals directly** - Use factory methods and public APIs
|
|
@@ -428,13 +521,14 @@ test/
|
|
|
428
521
|
| Batch utilities | `src/manager/helpers/utilities.js` |
|
|
429
522
|
| Main API handler | `src/manager/functions/core/actions/api.js` |
|
|
430
523
|
| Config template | `templates/backend-manager-config.json` |
|
|
431
|
-
| CLI entry | `src/cli/
|
|
524
|
+
| CLI entry | `src/cli/index.js` |
|
|
432
525
|
|
|
433
526
|
## Environment Detection
|
|
434
527
|
|
|
435
528
|
```javascript
|
|
436
529
|
assistant.isDevelopment() // true when ENVIRONMENT !== 'production' or in emulator
|
|
437
530
|
assistant.isProduction() // true when ENVIRONMENT === 'production'
|
|
531
|
+
assistant.isTesting() // true when running tests (via npx bm test)
|
|
438
532
|
```
|
|
439
533
|
|
|
440
534
|
## Response Headers
|
package/README.md
CHANGED
|
@@ -359,7 +359,7 @@ The main API endpoint accepts commands in the format `category:action`:
|
|
|
359
359
|
| `general` | `generate-uuid`, `send-email`, `fetch-post` |
|
|
360
360
|
| `handler` | `create-post` |
|
|
361
361
|
| `firebase` | `get-providers` |
|
|
362
|
-
| `test` | `authenticate`, `
|
|
362
|
+
| `test` | `authenticate`, `webhook`, `lab`, `redirect` |
|
|
363
363
|
| `special` | `setup-electron-manager-client` |
|
|
364
364
|
|
|
365
365
|
### Auth Events
|
|
@@ -447,6 +447,7 @@ assistant.debug('Debug message');
|
|
|
447
447
|
// Environment
|
|
448
448
|
assistant.isDevelopment(); // true in emulator
|
|
449
449
|
assistant.isProduction(); // true in production
|
|
450
|
+
assistant.isTesting(); // true when running tests
|
|
450
451
|
|
|
451
452
|
// File uploads
|
|
452
453
|
const { fields, files } = await assistant.parseMultipartFormData();
|
|
@@ -737,7 +738,8 @@ npx backend-manager <command>
|
|
|
737
738
|
| `bem setup` | Run Firebase project setup and validation |
|
|
738
739
|
| `bem serve` | Start local Firebase emulator |
|
|
739
740
|
| `bem deploy` | Deploy functions to Firebase |
|
|
740
|
-
| `bem test` | Run
|
|
741
|
+
| `bem test [paths...]` | Run integration tests |
|
|
742
|
+
| `bem emulators` | Start Firebase emulators (keep-alive mode) |
|
|
741
743
|
| `bem version`, `bem v` | Show BEM version |
|
|
742
744
|
| `bem clear` | Clear cache and temp files |
|
|
743
745
|
| `bem install`, `bem i` | Install BEM (local or production) |
|
|
@@ -747,14 +749,11 @@ npx backend-manager <command>
|
|
|
747
749
|
|
|
748
750
|
## Environment Variables
|
|
749
751
|
|
|
752
|
+
Set these in your `functions/.env` file:
|
|
753
|
+
|
|
750
754
|
| Variable | Description |
|
|
751
755
|
|----------|-------------|
|
|
752
|
-
| `FIREBASE_CONFIG` | Firebase project config (auto-set by Firebase) |
|
|
753
|
-
| `RUNTIME_CONFIG` | BEM runtime config (JSON5 format) |
|
|
754
756
|
| `BACKEND_MANAGER_KEY` | Admin authentication key |
|
|
755
|
-
| `ENVIRONMENT` | `'production'` or `'development'` |
|
|
756
|
-
| `GOOGLE_APPLICATION_CREDENTIALS` | Path to service account JSON |
|
|
757
|
-
| `HCAPTCHA_SECRET` | hCaptcha secret for usage validation |
|
|
758
757
|
|
|
759
758
|
## Response Headers
|
|
760
759
|
|
|
@@ -764,6 +763,106 @@ BEM attaches metadata to responses:
|
|
|
764
763
|
bm-properties: {"code":200,"tag":"functionName/executionId","usage":{...},"schema":{...}}
|
|
765
764
|
```
|
|
766
765
|
|
|
766
|
+
## Testing
|
|
767
|
+
|
|
768
|
+
BEM includes an integration test framework that runs against Firebase emulators.
|
|
769
|
+
|
|
770
|
+
### Running Tests
|
|
771
|
+
|
|
772
|
+
```bash
|
|
773
|
+
# Option 1: Two terminals (recommended for development)
|
|
774
|
+
npx bm emulators # Terminal 1 - keeps emulators running
|
|
775
|
+
npx bm test # Terminal 2 - runs tests
|
|
776
|
+
|
|
777
|
+
# Option 2: Single command (auto-starts emulators, shuts down after)
|
|
778
|
+
npx bm test
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
### Filtering Tests
|
|
782
|
+
|
|
783
|
+
```bash
|
|
784
|
+
npx bm test rules/ # Run rules tests (both BEM and project)
|
|
785
|
+
npx bm test bem:rules/ # Only BEM's rules tests
|
|
786
|
+
npx bm test project:rules/ # Only project's rules tests
|
|
787
|
+
npx bm test user/ admin/ # Multiple paths
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
### Test Locations
|
|
791
|
+
|
|
792
|
+
- **BEM core tests:** `test/`
|
|
793
|
+
- **Project tests:** `functions/test/bem/`
|
|
794
|
+
|
|
795
|
+
Use `bem:` or `project:` prefix to filter by source.
|
|
796
|
+
|
|
797
|
+
### Writing Tests
|
|
798
|
+
|
|
799
|
+
**Suite** - Sequential tests with shared state (stops on first failure):
|
|
800
|
+
|
|
801
|
+
```javascript
|
|
802
|
+
// test/functions/user/sign-up.js
|
|
803
|
+
module.exports = {
|
|
804
|
+
description: 'User signup flow with affiliate tracking',
|
|
805
|
+
type: 'suite',
|
|
806
|
+
tests: [
|
|
807
|
+
{
|
|
808
|
+
name: 'verify-referrer-exists',
|
|
809
|
+
async run({ firestore, assert, state, accounts }) {
|
|
810
|
+
state.referrerUid = accounts.referrer.uid;
|
|
811
|
+
const doc = await firestore.get(`users/${state.referrerUid}`);
|
|
812
|
+
assert.ok(doc, 'Referrer should exist');
|
|
813
|
+
},
|
|
814
|
+
},
|
|
815
|
+
{
|
|
816
|
+
name: 'call-user-signup-with-affiliate',
|
|
817
|
+
async run({ http, assert, state }) {
|
|
818
|
+
const response = await http.as('referred').command('user:sign-up', {
|
|
819
|
+
attribution: { affiliate: { code: 'TESTREF' } },
|
|
820
|
+
});
|
|
821
|
+
assert.isSuccess(response);
|
|
822
|
+
},
|
|
823
|
+
},
|
|
824
|
+
],
|
|
825
|
+
};
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
**Group** - Independent tests (continues even if one fails):
|
|
829
|
+
|
|
830
|
+
```javascript
|
|
831
|
+
// test/functions/admin/firestore-write.js
|
|
832
|
+
module.exports = {
|
|
833
|
+
description: 'Admin Firestore write operation',
|
|
834
|
+
type: 'group',
|
|
835
|
+
tests: [
|
|
836
|
+
{
|
|
837
|
+
name: 'admin-auth-succeeds',
|
|
838
|
+
auth: 'admin',
|
|
839
|
+
async run({ http, assert }) {
|
|
840
|
+
const response = await http.command('admin:firestore-write', {
|
|
841
|
+
path: '_test/doc',
|
|
842
|
+
document: { test: 'value' },
|
|
843
|
+
});
|
|
844
|
+
assert.isSuccess(response);
|
|
845
|
+
},
|
|
846
|
+
},
|
|
847
|
+
{
|
|
848
|
+
name: 'unauthenticated-rejected',
|
|
849
|
+
auth: 'none',
|
|
850
|
+
async run({ http, assert }) {
|
|
851
|
+
const response = await http.command('admin:firestore-write', {
|
|
852
|
+
path: '_test/doc',
|
|
853
|
+
document: { test: 'value' },
|
|
854
|
+
});
|
|
855
|
+
assert.isError(response, 401);
|
|
856
|
+
},
|
|
857
|
+
},
|
|
858
|
+
],
|
|
859
|
+
};
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
**Auth levels:** `none`, `user`/`basic`, `admin`, `premium-active`, `premium-expired`
|
|
863
|
+
|
|
864
|
+
See `CLAUDE.md` for complete test API documentation.
|
|
865
|
+
|
|
767
866
|
## Final Words
|
|
768
867
|
|
|
769
868
|
If you are still having difficulty, we would love for you to post a question to [the Backend Manager issues page](https://github.com/itw-creative-works/backend-manager/issues). It is much easier to answer questions that include your code and relevant files! So if you can provide them, we'd be extremely grateful (and more likely to help you find the answer!)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
We need to do a pretty significatn refactor of our BEM API now.
|
|
2
|
+
|
|
3
|
+
Since that was orignally implemented, I built a much better process for hading incoming http requests. That is, the route/schema system found here:
|
|
4
|
+
/Users/ian/Developer/Repositories/ITW-Creative-Works/backend-manager/src/manager/helpers/assistant.js
|
|
5
|
+
/Users/ian/Developer/Repositories/ITW-Creative-Works/backend-manager/src/manager/helpers/middleware.js
|
|
6
|
+
/Users/ian/Developer/Repositories/ITW-Creative-Works/backend-manager/src/manager/helpers/settings.js
|
|
7
|
+
|
|
8
|
+
You can see an example of it in our consuming project:
|
|
9
|
+
/Users/ian/Developer/Repositories/ITW-Creative-Works/ultimate-jekyll-backend/functions/index.js
|
|
10
|
+
/Users/ian/Developer/Repositories/ITW-Creative-Works/ultimate-jekyll-backend/functions/routes/example/index.js
|
|
11
|
+
/Users/ian/Developer/Repositories/ITW-Creative-Works/ultimate-jekyll-backend/functions/schemas/example/index.js
|
|
12
|
+
|
|
13
|
+
We built a single unified bem_api function that handles all http requests in a single place, and then routes them, see here:
|
|
14
|
+
/Users/ian/Developer/Repositories/ITW-Creative-Works/backend-manager/src/manager/index.js
|
|
15
|
+
.https.onRequest(async (req, res) => self._process((new (require(`${core}/actions/api.js`))()).init(self, { req: req, res: res, })));
|
|
16
|
+
|
|
17
|
+
We should refactor this system to USE THE NEW ROUTE/SCHEMA SYSTEM rather than the old way of doing things. This will make it much easier to maintain and extend in the future.
|
|
18
|
+
|
|
19
|
+
We can start with a simple one like:
|
|
20
|
+
/Users/ian/Developer/Repositories/ITW-Creative-Works/backend-manager/src/manager/functions/core/actions/api/general/generate-uuid.js
|
|
21
|
+
|
|
22
|
+
and once we perfect that we can move on to the others.
|
|
23
|
+
|
|
24
|
+
For each BEM refactor, we should create a route and a schema for the expected input. As you can see, BEM APIs epect a command and payload in the body, requiring it to be a POST operation. I would like to rebuild this system to be more proper, so that each BEM api can be GET, POST, etc as appropriate.
|
|
25
|
+
|
|
26
|
+
Previously:
|
|
27
|
+
request('/backend-manager', {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
body: {
|
|
30
|
+
command: 'generate-uuid',
|
|
31
|
+
payload: { ... }
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
but i think it owud be more intuitive if going forward we just had endpoints like:
|
|
36
|
+
request('/backend-manager/general:uuid', {
|
|
37
|
+
method: 'POST',
|
|
38
|
+
body: { ... }
|
|
39
|
+
})
|
|
40
|
+
OR
|
|
41
|
+
request('/backend-manager/general/uuid', {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
body: { ... }
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
Im not sure how we can do this to be backwards compatible with existing BEM API consumers, but it does need to be backwards compatible.
|
|
47
|
+
|
|
48
|
+
If you look at the firebase.json in the consuming project we can see that
|
|
49
|
+
/Users/ian/Developer/Repositories/ITW-Creative-Works/ultimate-jekyll-backend/firebase.json
|
|
50
|
+
{
|
|
51
|
+
"source": "/backend-manager",
|
|
52
|
+
"function": "bm_api"
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
So maybe we could make it:
|
|
56
|
+
{
|
|
57
|
+
"source": "/backend-manager/**",
|
|
58
|
+
"function": "bm_api"
|
|
59
|
+
},
|
|
60
|
+
and then parse the route inside the bem_api function to determine which route/schema to use, falling back to the old system if the route is just /backend-manager with a "command" in the body, and then use the new route/schema system if the path is /backend-manager/something?
|
|
61
|
+
|
|
62
|
+
Either way, i think we need a minimal intermediary step where we determine which one to use based on the incoming request and then either just route to the old one or route to the new "middleware", "settings", route/schema system
|
|
63
|
+
|
|
64
|
+
I would like each new route to have a great name clearly indicating its purpose, the method should be appropriate for the action (GET for fetches, POST for creates, etc) and the schema should be well defined for each route.
|
|
65
|
+
|
|
66
|
+
Since we can build this new API system however we want, i also expect you to rewrite and refactor the BEM api endppints to be kickass, modern, and well designed.
|
|
67
|
+
|
|
68
|
+
Also, certain VERBS should be removed from the actual file/function names since they are implied by the HTTP method. For example, instead of having a generate-uuid.js file, we could just have uuid.js since the POST method implies that we are generating/creating a new one, or insetad of add-marketing-contact we could just have marketing-contact.js since the POST method implies adding a new one and GET would imply fetching them.
|
|
69
|
+
|
|
70
|
+
Next, some fucntions have a lot crammed isnide them that could use some separation. For example, in add-marketing-contact.js we have code for handling multiple email providers (SendGrid, Beehiiv) all jammed into a single file. I think we should refactor this to have a separate file for each provider in subfolder that handles the specifics of that provider, and then the main route file just calls those provider-specific files as needed. This will make it much easier to maintain and extend in the future as we add more providers.
|
|
71
|
+
|
|
72
|
+
Also, sometimes there are two endpoints that should be combined,for example
|
|
73
|
+
* /Users/ian/Developer/Repositories/ITW-Creative-Works/backend-manager/src/manager/functions/core/actions/api/general/add-marketing-contact.js
|
|
74
|
+
* /Users/ian/Developer/Repositories/ITW-Creative-Works/backend-manager/src/manager/functions/core/actions/api/general/remove-marketing-contact.js
|
|
75
|
+
These could be combined into a single marketing-contact.js file that handles both adding and removing based on the HTTP method (POST for add, DELETE for remove). This will make the API more RESTful and easier to understand.
|
|
76
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
MIDDLEWARE REFACTOR
|
|
2
|
+
We have a system where we handle incoming requests using a route/schema system found here:
|
|
3
|
+
/Users/ian/Developer/Repositories/ITW-Creative-Works/backend-manager/src/manager/helpers/assistant.js
|
|
4
|
+
/Users/ian/Developer/Repositories/ITW-Creative-Works/backend-manager/src/manager/helpers/middleware.js
|
|
5
|
+
/Users/ian/Developer/Repositories/ITW-Creative-Works/backend-manager/src/manager/helpers/settings.js
|
|
6
|
+
|
|
7
|
+
You can see an example of it in our consuming project:
|
|
8
|
+
/Users/ian/Developer/Repositories/ITW-Creative-Works/ultimate-jekyll-backend/functions/index.js
|
|
9
|
+
/Users/ian/Developer/Repositories/ITW-Creative-Works/ultimate-jekyll-backend/functions/routes/example/index.js
|
|
10
|
+
/Users/ian/Developer/Repositories/ITW-Creative-Works/ultimate-jekyll-backend/functions/schemas/example/index.js
|
|
11
|
+
|
|
12
|
+
I have some ideas iw as thinking about and id like to know your thoughts:
|
|
13
|
+
* new design so that each route is modern JS that does a single export instead of exportting a class with an init() and a main() method.
|
|
14
|
+
* schema system currently uses the user's plan when designing the schma like
|
|
15
|
+
module.exports = function (assistant) {
|
|
16
|
+
return {
|
|
17
|
+
// DEFAULTS
|
|
18
|
+
['defaults']: {
|
|
19
|
+
key: {
|
|
20
|
+
types: ['string'],
|
|
21
|
+
value: undefined,
|
|
22
|
+
default: '',
|
|
23
|
+
required: false,
|
|
24
|
+
min: 0,
|
|
25
|
+
max: 2048,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
// Premium plan
|
|
30
|
+
['premium']: {
|
|
31
|
+
key: {
|
|
32
|
+
types: ['string'],
|
|
33
|
+
value: undefined,
|
|
34
|
+
default: 'premium-default',
|
|
35
|
+
required: false,
|
|
36
|
+
min: 0,
|
|
37
|
+
max: 4096,
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
however it hink we should instead eliminate the toplevel plan/default system and code each plan changes into the individual keys like:
|
|
44
|
+
const schema = {
|
|
45
|
+
id: {
|
|
46
|
+
types: ['string'],
|
|
47
|
+
value: () => assistant.Manager.Utilities().randomId(),
|
|
48
|
+
required: false,
|
|
49
|
+
},
|
|
50
|
+
feature: {
|
|
51
|
+
types: ['string'],
|
|
52
|
+
default: '',
|
|
53
|
+
required: true,
|
|
54
|
+
min: 1,
|
|
55
|
+
max: 4,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Adjust schema based on plan
|
|
60
|
+
if (assistant.user.plan === 'premium') {
|
|
61
|
+
schema.feature.max = 8;
|
|
62
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
PAYMETN SYSTEM REFACTOR
|
|
2
|
+
Question
|
|
3
|
+
Propose a schema for storing user's subscription data in their firestore user document. Currently the user looks like this
|
|
4
|
+
users/{uid}
|
|
5
|
+
{
|
|
6
|
+
auth: {
|
|
7
|
+
uid: string,
|
|
8
|
+
email: string,
|
|
9
|
+
},
|
|
10
|
+
roles: {
|
|
11
|
+
admin: boolean,
|
|
12
|
+
},
|
|
13
|
+
... (other stuff that is not really relevant)
|
|
14
|
+
}
|
|
15
|
+
I will be using a variety of payment processors including Stripe and PayPal, so I need a flexible way to store subscription data that can work with multiple providers.
|
|
16
|
+
Before inserting the subscription data, we wil need to receive the processor webhooks.
|
|
17
|
+
We will be keeping track of the subcsription data in the user doc AS WELL AS a dedicated subscription doc where we can have more information
|
|
18
|
+
Thus, the user doc doesnt need to contain the entire subscription Object, just enough to grant the user access to premium features in the backend or frontend as well as display important info in the user's account page like next billing date, plan name, status, anything else that is important.
|
|
19
|
+
Also, since we will be checking subscription status often, I thought it would be nice to store 2 main objects:
|
|
20
|
+
1. the original subscirpiton object from the payment provider, unmodified
|
|
21
|
+
2. a unified and standardized subscription object that we define, so that when checking subscription status we dont have to deal with the differences between payment providers (both for displaying info and for checking status/plan/etc to grant access when making requests or on the frontend)
|
|
22
|
+
We could store BOTH in both the user doc and the subscription doc, or only store the unified object in the user doc and both in the subscription doc.
|
|
23
|
+
|
|
24
|
+
For our standardized Object, we should be able to get the current plan and whether the subscription is active or not EXTREMELY easily. LIke a single if statement, allowing us to grant access if if the user is premium or whatever.
|
|
25
|
+
|
|
26
|
+
However, i would like it to be flexible enough so that we can show something like:
|
|
27
|
+
- User is on premium plan and paid up --> grant access to premium features, show "You are Premium and your next billing date is X"
|
|
28
|
+
- User is on premium plan but payment failed --> restrict access to premium features, show "Your payment failed, please update your payment method"
|
|
29
|
+
- User was on premium plan but cancelled --> restrict access to premium features, show "You WERE premium but it was cancelled so now youre on Free plan"
|
|
30
|
+
- User was on premium plan but cancellation is pending --> grant access to premium features, show "You are Premium until X date when your plan will be cancelled"
|
|
31
|
+
- User is on trial --> grant access to premium features, show "You are on a free trial that ends on X date"
|
|
32
|
+
So essentially we need a way to be able to determine all of these different scenarios EASILY (SINGLE IF STATEMENT)
|
|
33
|
+
|
|
34
|
+
I know all payment proivders are different and ahve different concepts of how exaclty a subscirption is active, cancelled, past due, trialing, etc but we need to come up with a unified way of representing this data in our standardized object so that we can easily check status and display info regardless of payment provider.
|
|
35
|
+
|
|
36
|
+
Like, i i think stripe you can "cancel at period end" and thus the sub will not actually be cancelled until the end of the billing period, but paypal might be slightly different.
|
|
37
|
+
|
|
38
|
+
BEM API ENDPOINTS
|
|
39
|
+
* For our payment system to work, we shoudl implemnt some BEM API endpoints to create, listen for webhooks, and manage subscriptions.
|
|
40
|
+
* anytime there is an aciton that handles multiple payment providers, we should have the entryppoint import a file for each provider, where each provider handles the request in its own way, but STILL STANDARDIZED and similar across all providers.
|
|
41
|
+
|
|
42
|
+
backend-manager/payments/intent
|
|
43
|
+
* handle creating payment intents or equivalent in other providers
|
|
44
|
+
* various checks like
|
|
45
|
+
* is the user currently subscribed? if so block
|
|
46
|
+
* is the user allowed to have a trial (havent had one before)? if not block their trial
|
|
47
|
+
* validate their gcaptcha token. block if invalid or missing
|
|
48
|
+
* create the payment intent or equivalent at "payments-intents/{id}" in firestore
|
|
49
|
+
|
|
50
|
+
backend-manager/payments/webhook (or something similar)
|
|
51
|
+
* handle receiving webhooks from payment providers to update subscription status
|
|
52
|
+
* different file for processing each paymnt processor (returning some comon things like the event id)
|
|
53
|
+
* various checks like
|
|
54
|
+
* verify the webhook by checking the querystring for the BEM token (same as .env BACKEND_MANAGER_KEY)
|
|
55
|
+
* check for payment-webhooks/{id} doc to see if we already processed this webhook (id is provided by the payment provider in the webhook payload)
|
|
56
|
+
* Immediately save the raw webhook data to "payments-webhooks/{id}" in firestore so we can return a 200 as soon as possible. THe webhook will be processed in a firestore function trigger onWrite for that doc (status === pending)
|
|
57
|
+
|
|
58
|
+
Firestore trigger for payments-webhooks/{id}
|
|
59
|
+
* process the webhook data and update the user's subscription data in both their user doc and their subscription doc
|
|
60
|
+
* various checks like
|
|
61
|
+
* if status === completed, do nothing
|