command_tower 0.13.1 → 0.15.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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/command_tower/me/push_controller.rb +74 -0
  3. data/app/deserializers/command_tower/deserializers/me/push/token_deserializer.rb +42 -0
  4. data/app/errors/command_tower/errors/account/push_capability_unavailable_error.rb +21 -0
  5. data/app/errors/command_tower/errors/account/push_endpoint_not_found_error.rb +21 -0
  6. data/app/errors/command_tower/errors/account/push_test_no_endpoint_error.rb +21 -0
  7. data/app/errors/command_tower/errors/account/push_test_rate_limit_error.rb +25 -0
  8. data/app/serializers/command_tower/serializers/me/push_serializer.rb +47 -0
  9. data/app/services/command_tower/messaging/channel_detectors.rb +6 -0
  10. data/app/services/command_tower/messaging/endpoints/validators/push.rb +6 -0
  11. data/app/services/command_tower/messaging/execution/adapter_request.rb +21 -3
  12. data/app/services/command_tower/messaging/execution/adapters/expo/adapter.rb +229 -0
  13. data/app/services/command_tower/messaging/execution/adapters/expo/configuration.rb +42 -0
  14. data/app/services/command_tower/messaging/execution/adapters/expo/http_client.rb +137 -0
  15. data/app/services/command_tower/messaging/execution/resolve_recipient_address.rb +7 -0
  16. data/app/services/command_tower/messaging/execution/select_adapter.rb +4 -0
  17. data/app/services/command_tower/messaging/rendering/channel_renderer.rb +21 -3
  18. data/app/services/command_tower/messaging/rendering/rendered_push_payload.rb +50 -0
  19. data/app/services/command_tower/services/account/push/check_self_test_rate_limit.rb +41 -0
  20. data/app/services/command_tower/services/account/push/create.rb +25 -0
  21. data/app/services/command_tower/services/account/push/ct_support.rb +38 -0
  22. data/app/services/command_tower/services/account/push/destroy.rb +23 -0
  23. data/app/services/command_tower/services/account/push/list.rb +23 -0
  24. data/app/services/command_tower/services/account/push/replace.rb +26 -0
  25. data/app/services/command_tower/services/account/push/send_self_test.rb +57 -0
  26. data/app/services/command_tower/services/me/push_product_gate.rb +18 -0
  27. data/app/views/command_tower/messaging/rendering/push.text.erb +1 -0
  28. data/app/workflows/command_tower/workflows/me/error_mapping.rb +6 -2
  29. data/app/workflows/command_tower/workflows/me/push/create_workflow.rb +37 -0
  30. data/app/workflows/command_tower/workflows/me/push/destroy_workflow.rb +37 -0
  31. data/app/workflows/command_tower/workflows/me/push/index_workflow.rb +34 -0
  32. data/app/workflows/command_tower/workflows/me/push/replace_workflow.rb +38 -0
  33. data/app/workflows/command_tower/workflows/me/push/test_workflow.rb +54 -0
  34. data/app/workflows/command_tower/workflows/me/push/workflow_support.rb +35 -0
  35. data/app/workflows/command_tower/workflows/messaging/execution/deliver_workflow.rb +1 -0
  36. data/config/routes.rb +11 -0
  37. data/docs/api_reference.md +21 -0
  38. data/docs/controllers.md +1 -0
  39. data/docs/messaging_integration_guide.md +8 -3
  40. data/docs/upgrades/0.14.0.md +31 -0
  41. data/docs/upgrades/0.15.0.md +32 -0
  42. data/docs/upgrades/README.md +2 -0
  43. data/lib/command_tower/authorization/default.yml +8 -0
  44. data/lib/command_tower/configuration/messaging/config.rb +6 -0
  45. data/lib/command_tower/configuration/messaging/expo.rb +51 -0
  46. data/lib/command_tower/version.rb +1 -1
  47. metadata +31 -2
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module Workflows
5
+ module Me
6
+ module Push
7
+ class ReplaceWorkflow < CommandTower::Workflows::ApplicationWorkflow
8
+ retry_strategy :none
9
+
10
+ def call(current_user:, endpoint_id:, address:, auth_context: nil)
11
+ unless CommandTower::Services::Me::PushProductGate.enabled?
12
+ return failure(**WorkflowSupport.capability_failure)
13
+ end
14
+
15
+ result = CommandTower::Services::Account::Push::Replace.call(
16
+ user: current_user,
17
+ endpoint_id:,
18
+ address:,
19
+ )
20
+ unless result.success?
21
+ error = result.errors.first
22
+ return failure(
23
+ errors: result.errors,
24
+ http_status: CommandTower::Workflows::Me::ErrorMapping.http_status_for(error),
25
+ )
26
+ end
27
+
28
+ success(
29
+ payload: WorkflowSupport.serialize_view(result.data[:safe_view]),
30
+ http_status: :ok,
31
+ response_effects: WorkflowSupport.expire_header_effects(auth_context),
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module Workflows
5
+ module Me
6
+ module Push
7
+ class TestWorkflow < CommandTower::Workflows::ApplicationWorkflow
8
+ retry_strategy :none
9
+
10
+ def call(current_user:, auth_context: nil)
11
+ unless CommandTower::Services::Me::PushProductGate.enabled?
12
+ return failure(**WorkflowSupport.capability_failure)
13
+ end
14
+
15
+ result = CommandTower::Services::Account::Push::SendSelfTest.call(user: current_user)
16
+ unless result.success?
17
+ error = result.errors.first
18
+ return failure(
19
+ errors: result.errors,
20
+ http_status: http_status_for(error),
21
+ )
22
+ end
23
+
24
+ success(
25
+ payload: {
26
+ communicationId: result.data[:communication_id],
27
+ destinationPlanId: result.data[:destination_plan_id],
28
+ selectedChannels: result.data[:selected_channels],
29
+ status: result.data[:communication_status],
30
+ },
31
+ http_status: :ok,
32
+ response_effects: WorkflowSupport.expire_header_effects(auth_context),
33
+ )
34
+ end
35
+
36
+ private
37
+
38
+ def http_status_for(error)
39
+ case error
40
+ when CommandTower::Errors::ValidationError,
41
+ CommandTower::Errors::Messaging::RecipientUnresolvedError,
42
+ CommandTower::Errors::Messaging::AcceptRejectedError
43
+ :unprocessable_entity
44
+ when CommandTower::Errors::Messaging::IdempotencyConflictError
45
+ :conflict
46
+ else
47
+ CommandTower::Workflows::Me::ErrorMapping.http_status_for(error)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module Workflows
5
+ module Me
6
+ module Push
7
+ module WorkflowSupport
8
+ module_function
9
+
10
+ def capability_failure
11
+ error = CommandTower::Errors::Account::PushCapabilityUnavailableError.new
12
+ {
13
+ errors: [error],
14
+ http_status: CommandTower::Workflows::Me::ErrorMapping.http_status_for(error),
15
+ }
16
+ end
17
+
18
+ def expire_header_effects(auth_context)
19
+ return if auth_context.nil?
20
+
21
+ { set_expire_header: auth_context.token_expires_at }
22
+ end
23
+
24
+ def serialize_view(safe_view)
25
+ CommandTower::Serializers::Me::PushSerializer.serialize(safe_view)
26
+ end
27
+
28
+ def serialize_collection(safe_views)
29
+ CommandTower::Serializers::Me::PushSerializer.serialize_collection(safe_views)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -114,6 +114,7 @@ module CommandTower
114
114
  channel_key: delivery.channel_key,
115
115
  attempt_id: attempt.id,
116
116
  rendered:,
117
+ eligible_endpoint_ids: Array(readiness.eligible_endpoint_ids),
117
118
  )
118
119
  adapter = CommandTower::Messaging::Execution::SelectAdapter.call(delivery:, executor:)
119
120
  CommandTower::Messaging::Execution::OperationLogger.adapter_started(
data/config/routes.rb CHANGED
@@ -100,6 +100,17 @@ CommandTower::Engine.routes.draw do
100
100
  put "pushover", to: "pushover#update"
101
101
  delete "pushover", to: "pushover#destroy"
102
102
  post "pushover/verification", to: "pushover/verifications#create"
103
+
104
+ # Expo push lifecycle always drawn (no route constraints).
105
+ # Product readiness is workflow-gated as 503 push_capability_unavailable.
106
+ # Collection JSON (multi-active). No verification POST — create/replace mark_verified.
107
+ # Self-test must be declared before :id routes so "test" is not captured as an id.
108
+ get "push", to: "push#index"
109
+ post "push", to: "push#create"
110
+ post "push/test", to: "push#test"
111
+ patch "push/:id", to: "push#update"
112
+ put "push/:id", to: "push#update"
113
+ delete "push/:id", to: "push#destroy"
103
114
  end
104
115
 
105
116
  namespace :admin do
@@ -426,6 +426,27 @@ Errors include `422` (`pushover_already_configured`, `pushover_not_configured`,
426
426
 
427
427
  ---
428
428
 
429
+ ## Push (Expo)
430
+
431
+ Routes are **always drawn**. Product readiness is workflow-gated as **503** `push_capability_unavailable` (`config.messaging.expo.adapter` must be `fake`, `log`, or `http`).
432
+
433
+ There is **no** `POST /me/push/verification`. Create and replace call `Endpoints.mark_verified` in-workflow after persist.
434
+
435
+ | Method | Path | Body | Notes |
436
+ |--------|------|------|--------|
437
+ | `GET` | `/me/push` | — | `{ endpoints: [...] }` — **active only** (terminal rows omitted) |
438
+ | `POST` | `/me/push` | `token`/`address` (snake or camelCase) | Create or idempotent same-fingerprint; returns verified endpoint |
439
+ | `PATCH` / `PUT` | `/me/push/:id` | same | Replace by id; new row verified; other actives untouched |
440
+ | `DELETE` | `/me/push/:id` | — | Revoke; returns revoked SafeView |
441
+
442
+ **Token form:** `ExponentPushToken[...]` or `ExpoPushToken[...]` (422 otherwise).
443
+
444
+ **Endpoint fields:** `id`, `channelKey`, `lifecycleState`, `verificationState`, `maskedDisplayValue`, `verifiedAt`, `createdAt`, `updatedAt`, `actions: { canReplace, canRemove }`. Never the raw token.
445
+
446
+ **RBAC:** `me_push`. **Spec:** `spec/requests/command_tower/me/push_spec.rb`.
447
+
448
+ ---
449
+
429
450
  ## Admin Workspace
430
451
 
431
452
  ### `GET /admin/workspace`
data/docs/controllers.md CHANGED
@@ -20,6 +20,7 @@ This page is an **index** of route areas. Detailed request/response contracts li
20
20
  | Preferences | `/me/preferences*` | Notification preferences |
21
21
  | Phone | `/me/phone*` | Phone endpoint + verification |
22
22
  | Pushover | `/me/pushover*` | Pushover endpoint lifecycle + verification |
23
+ | Push | `/me/push*` | Expo push endpoint collection (register / replace / revoke) |
23
24
  | Admin messaging | `/admin/messaging/announcements` | Cohort announcements |
24
25
 
25
26
  Exact paths depend on where the host mounts the engine.
@@ -10,7 +10,8 @@ Complete install and host RBAC first: [Host integration](host_integration_guide.
10
10
  |---------|----------------|
11
11
  | User inbox (consume) | Engine `/me/inbox*` — see [API reference](api_reference.md#me-inbox) |
12
12
  | Preferences | Engine `/me/preferences*` — see [API reference](api_reference.md#preferences) |
13
- | Phone / Pushover endpoints | Engine `/me/phone*`, `/me/pushover*` (503 when capability unavailable) |
13
+ | Phone / Pushover / Push endpoints | Engine `/me/phone*`, `/me/pushover*`, `/me/push*` (503 when capability unavailable) |
14
+
14
15
  | Single-recipient emit | `CommandTower::Services::Messaging::Communications::Produce` |
15
16
  | Multi-recipient emit | `CommandTower::Services::Messaging::Communications::ProduceMany` |
16
17
  | Admin cohort announce | Engine `POST /admin/messaging/announcements` |
@@ -73,10 +74,14 @@ Pagination detail: [pagination.md](pagination.md). Full catalog: [api_reference.
73
74
 
74
75
  - Notification catalog content (registered into CommandTower notification types)
75
76
  - `platform_enabled_channels` / channel policy injection
76
- - Messaging adapter credentials (email / SMS / Pushover) via initializer or ENV
77
- - Host product roles that grant CT-owned Me inbox/preferences/phone/pushover entities (and `admin` if used)
77
+ - Messaging adapter credentials (email / SMS / Pushover / Expo push) via initializer or ENV
78
+ - Host product roles that grant CT-owned Me inbox/preferences/phone/pushover/`me_push` entities (and `admin` if used)
78
79
  - Product-specific operational tooling (announce rake tasks, welcome copy)
79
80
 
81
+ Expo push (`config.messaging.expo`): set `adapter` to `fake`, `log`, or `http` to enable the Messaging `push` channel and Me `/me/push*` (default `disabled`). `access_token` is optional — send `Authorization: Bearer` only when non-blank (required only if the Expo project enables enhanced push security). Never log the token.
82
+
83
+ Me push registration: engine `/me/push*` (collection of active endpoints). Create/replace persist via `Endpoints` and **mark_verified** in-workflow — there is no `POST /me/push/verification`. Tokens must be Expo form (`ExponentPushToken[...]` / `ExpoPushToken[...]`).
84
+
80
85
  ## Related
81
86
 
82
87
  - [Controllers / routes](controllers.md)
@@ -0,0 +1,31 @@
1
+ # Upgrade: CommandTower 0.14.0
2
+
3
+ **From:** `0.13.1`
4
+ **To:** `0.14.0`
5
+
6
+ Minor release: Expo push as an executable Messaging channel, plus engine `/me/push*` registration HTTP. No schema migration.
7
+
8
+ ## Host-visible changes
9
+
10
+ | Change | Host impact |
11
+ |--------|-------------|
12
+ | `config.messaging.expo` | New composer: `adapter` (`disabled` / `fake` / `log` / `http`) and optional `access_token` (Bearer sent only when non-blank). Required for `push` to be platform-configured. |
13
+ | Channel `push` execution | Detector, renderer (`RenderedPushPayload`), Expo adapter (send + one getReceipts pass), multi-endpoint fan-out via `AdapterRequest.eligible_endpoint_ids`. `DeviceNotRegistered` → `Endpoints.mark_invalid`. |
14
+ | `/api/me/push*` | Collection register / list (active) / replace / revoke. Create/replace call `mark_verified` in-workflow (no verification POST). 503 when Expo adapter is disabled. |
15
+ | RBAC `me_push` | Show/create/update/destroy for Me push. Hosts must grant on roles that should register device tokens (e.g. `member`). |
16
+ | `Validators::Push` | Requires Expo token form (`ExponentPushToken[…]` / `ExpoPushToken[…]`). |
17
+
18
+ ## Host actions
19
+
20
+ 1. Bump gem to `0.14.0` (or `>= 0.14.0`).
21
+ 2. **No new migration.**
22
+ 3. Set `config.messaging.expo.adapter` (use `http` on production TF API; `fake` is fine for development/test with `allow_fake_adapter`).
23
+ 4. Optionally set `config.messaging.expo.access_token` **only if** the Expo project has enhanced push security enabled.
24
+ 5. Enable `push` in `platform_enabled_channels` and grant `me_push` when the host product should register/send push.
25
+ 6. Add `push` to announcement (or other) message types’ allowed/default channels when Planner should select it.
26
+
27
+ ## Not in this release
28
+
29
+ - Host channel policy / Pick’em enablement (host repo)
30
+ - Native client token lifecycle / CT FE `beforeLogout`
31
+ - EAS/APNs credentials or TestFlight proof
@@ -0,0 +1,32 @@
1
+ # Upgrade: CommandTower 0.15.0
2
+
3
+ **From:** `0.14.0`
4
+ **To:** `0.15.0`
5
+
6
+ Minor release: Me push self-test (`POST /me/push/test`) through Messaging Produce, with configurable rate limits. No schema migration.
7
+
8
+ ## Host-visible changes
9
+
10
+ | Change | Host impact |
11
+ |--------|-------------|
12
+ | `POST /api/me/push/test` | Authenticated self-test. Produces `push_delivery_test` via Communications::Produce (Expo path). Requires at least one active push endpoint. Returns Produce identifiers only (no Expo tokens). |
13
+ | RBAC `me_push` | Adds action `test` (same entity). Hosts that already grant `me_push` get the new action. |
14
+ | `config.messaging.expo.self_test_per_user_hour` | Max self-test sends per user per rolling window (default **10**). |
15
+ | `config.messaging.expo.self_test_window_seconds` | Rolling window length (default **3600**). |
16
+ | Rate limit HTTP | Exceeded limit → **429** `push_test_rate_limited` with `retry_after_seconds` details (existing CT envelope). |
17
+ | No endpoint | **422** `push_test_no_endpoint`. |
18
+ | Gate off | **503** `push_capability_unavailable` (same as other `/me/push*` when Expo adapter disabled). |
19
+
20
+ ## Host actions
21
+
22
+ 1. Bump gem to `0.15.0` (or `>= 0.15.0`).
23
+ 2. **No new migration.**
24
+ 3. **Required before enabling Account Push notifications settings:** register notification type `push_delivery_test` with push-only plan (e.g. `allowed/default %w[push]`, `inbox_available: false`, `user_configurable: false`, not preference-visible). CT hardcodes this type key for Me self-test.
25
+ 4. Set `config.messaging.expo.self_test_per_user_hour` explicitly when product policy differs from the default (Pick’em: **10**).
26
+ 5. Keep Expo adapter enabled (`http` / `fake`) and `me_push` granted for roles that use push.
27
+
28
+ ## Not in this release
29
+
30
+ - CT FE Account Push notifications settings / device lifecycle migration
31
+ - Host Pick’em enablement / type registration / IPA
32
+ - Physical TestFlight proof
@@ -4,6 +4,8 @@ Host-facing upgrade / change summaries for CommandTower releases.
4
4
 
5
5
  | Version | Summary |
6
6
  |---------|---------|
7
+ | [0.15.0](0.15.0.md) | Me `POST /me/push/test` → Produce (`push_delivery_test`); configurable Expo self-test rate limit composers; `me_push#test` |
8
+ | [0.14.0](0.14.0.md) | Expo push Messaging channel (`config.messaging.expo`) + `/api/me/push*` registration HTTP; `me_push` RBAC |
7
9
  | [0.13.1](0.13.1.md) | Canonical `Intervention::Severity` constants (`blocking`, `warning`, `informational`) |
8
10
  | [0.13.0](0.13.0.md) | Account self-service deletion (`DELETE /api/me/account`); tombstone + PII scrub; `me_account` RBAC; `users.deleted_at` migration |
9
11
  | [0.12.0](0.12.0.md) | Intervention envelope serializers/deserializers; product-tool admin_scope without Users/Audit narrowing |
@@ -68,6 +68,14 @@ entities:
68
68
  - name: me_pushover_verification
69
69
  controller: CommandTower::Me::Pushover::VerificationsController
70
70
  only: create
71
+ - name: me_push
72
+ controller: CommandTower::Me::PushController
73
+ only:
74
+ - index
75
+ - create
76
+ - update
77
+ - destroy
78
+ - test
71
79
  - name: me_audit_events
72
80
  controller: CommandTower::Me::AuditEventsController
73
81
  only:
@@ -3,6 +3,7 @@
3
3
  require "class_composer"
4
4
  require "command_tower/configuration/messaging/sms"
5
5
  require "command_tower/configuration/messaging/pushover"
6
+ require "command_tower/configuration/messaging/expo"
6
7
 
7
8
  module CommandTower
8
9
  module Configuration
@@ -54,6 +55,11 @@ module CommandTower
54
55
  desc: "Pushover verification transport configuration (credential validate + test notification)",
55
56
  allowed: Pushover,
56
57
  default: Pushover.new
58
+
59
+ add_composer :expo,
60
+ desc: "Expo Push delivery configuration (Messaging push channel; optional access_token)",
61
+ allowed: Expo,
62
+ default: Expo.new
57
63
  end
58
64
  end
59
65
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "class_composer"
4
+
5
+ module CommandTower
6
+ module Configuration
7
+ module Messaging
8
+ class Expo < ::CommandTower::Configuration::Base
9
+ include ClassComposer::Generator
10
+
11
+ ADAPTERS = %w[disabled fake log http].freeze
12
+
13
+ add_composer :adapter,
14
+ desc: "Expo Push delivery: disabled | fake | log | http. Default disabled (fail closed).",
15
+ allowed: String,
16
+ default: "disabled",
17
+ validator: ->(val) { ADAPTERS.include?(val.to_s) },
18
+ invalid_message: ->(val) { "Provided #{val.inspect}. Allowed: #{ADAPTERS.join(', ')}" }
19
+
20
+ add_composer :api_base_url,
21
+ desc: "Expo Push API origin (send = {base}/send, receipts = {base}/getReceipts).",
22
+ allowed: String,
23
+ default: "https://exp.host/--/api/v2/push"
24
+
25
+ add_composer :timeout_seconds,
26
+ desc: "HTTP open/read timeout seconds for Expo Push provider calls.",
27
+ allowed: Integer,
28
+ default: 5
29
+
30
+ add_composer :access_token,
31
+ desc: "Optional Expo Push access token. When non-blank, send Authorization: Bearer. " \
32
+ "Required only if the Expo project enables enhanced push security. Never log.",
33
+ allowed: String,
34
+ default: ""
35
+
36
+ add_composer :self_test_per_user_hour,
37
+ desc: "Max Me push self-test (POST /me/push/test) sends allowed per user per rolling window. " \
38
+ "Hosts enabling Account Push notifications settings should set this explicitly " \
39
+ "(Pick'em: 10).",
40
+ allowed: Integer,
41
+ default: 10
42
+
43
+ add_composer :self_test_window_seconds,
44
+ desc: "Rolling window length in seconds for Me push self-test rate limiting " \
45
+ "(default 3600 = one hour).",
46
+ allowed: Integer,
47
+ default: 3600
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CommandTower
4
- VERSION = "0.13.1"
4
+ VERSION = "0.15.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: command_tower
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.1
4
+ version: 0.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - matt-taylor
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-08-30 00:00:00.000000000 Z
11
+ date: 2026-09-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: phonelib
@@ -218,6 +218,7 @@ files:
218
218
  - app/controllers/command_tower/me/phone_verification/verifications_controller.rb
219
219
  - app/controllers/command_tower/me/phone_verification/verify_controller.rb
220
220
  - app/controllers/command_tower/me/preferences_controller.rb
221
+ - app/controllers/command_tower/me/push_controller.rb
221
222
  - app/controllers/command_tower/me/pushover/verifications_controller.rb
222
223
  - app/controllers/command_tower/me/pushover_controller.rb
223
224
  - app/controllers/command_tower/me_controller.rb
@@ -254,6 +255,7 @@ files:
254
255
  - app/deserializers/command_tower/deserializers/me/change_password_deserializer.rb
255
256
  - app/deserializers/command_tower/deserializers/me/delete_account_deserializer.rb
256
257
  - app/deserializers/command_tower/deserializers/me/phone_verification/verify_deserializer.rb
258
+ - app/deserializers/command_tower/deserializers/me/push/token_deserializer.rb
257
259
  - app/deserializers/command_tower/deserializers/me/pushover/credentials_deserializer.rb
258
260
  - app/deserializers/command_tower/deserializers/me/update_name_deserializer.rb
259
261
  - app/deserializers/command_tower/deserializers/me/update_phone_deserializer.rb
@@ -267,6 +269,10 @@ files:
267
269
  - app/errors/command_tower/errors/account/phone_verification_send_failed_error.rb
268
270
  - app/errors/command_tower/errors/account/phone_verification_stale_error.rb
269
271
  - app/errors/command_tower/errors/account/phone_verification_throttled_error.rb
272
+ - app/errors/command_tower/errors/account/push_capability_unavailable_error.rb
273
+ - app/errors/command_tower/errors/account/push_endpoint_not_found_error.rb
274
+ - app/errors/command_tower/errors/account/push_test_no_endpoint_error.rb
275
+ - app/errors/command_tower/errors/account/push_test_rate_limit_error.rb
270
276
  - app/errors/command_tower/errors/account/pushover_already_configured_error.rb
271
277
  - app/errors/command_tower/errors/account/pushover_capability_unavailable_error.rb
272
278
  - app/errors/command_tower/errors/account/pushover_not_configured_error.rb
@@ -363,6 +369,7 @@ files:
363
369
  - app/serializers/command_tower/serializers/me/account_serializer.rb
364
370
  - app/serializers/command_tower/serializers/me/change_password_response_serializer.rb
365
371
  - app/serializers/command_tower/serializers/me/delete_account_response_serializer.rb
372
+ - app/serializers/command_tower/serializers/me/push_serializer.rb
366
373
  - app/serializers/command_tower/serializers/me/pushover_serializer.rb
367
374
  - app/serializers/command_tower/serializers/messaging/inbox.rb
368
375
  - app/serializers/command_tower/serializers/messaging/preferences/catalog_serializer.rb
@@ -486,6 +493,9 @@ files:
486
493
  - app/services/command_tower/messaging/execution/adapter_result.rb
487
494
  - app/services/command_tower/messaging/execution/adapters/email/adapter.rb
488
495
  - app/services/command_tower/messaging/execution/adapters/email/configuration.rb
496
+ - app/services/command_tower/messaging/execution/adapters/expo/adapter.rb
497
+ - app/services/command_tower/messaging/execution/adapters/expo/configuration.rb
498
+ - app/services/command_tower/messaging/execution/adapters/expo/http_client.rb
489
499
  - app/services/command_tower/messaging/execution/adapters/fake_adapter.rb
490
500
  - app/services/command_tower/messaging/execution/adapters/pushover/adapter.rb
491
501
  - app/services/command_tower/messaging/execution/adapters/pushover/configuration.rb
@@ -579,6 +589,7 @@ files:
579
589
  - app/services/command_tower/messaging/rendering/channel_renderer.rb
580
590
  - app/services/command_tower/messaging/rendering/render_error.rb
581
591
  - app/services/command_tower/messaging/rendering/rendered_payload.rb
592
+ - app/services/command_tower/messaging/rendering/rendered_push_payload.rb
582
593
  - app/services/command_tower/messaging/rendering/rendered_pushover_payload.rb
583
594
  - app/services/command_tower/messaging/rendering/rendered_sms_payload.rb
584
595
  - app/services/command_tower/secrets.rb
@@ -590,6 +601,13 @@ files:
590
601
  - app/services/command_tower/services/account/clear_phone.rb
591
602
  - app/services/command_tower/services/account/phone_verification/send.rb
592
603
  - app/services/command_tower/services/account/phone_verification/verify.rb
604
+ - app/services/command_tower/services/account/push/check_self_test_rate_limit.rb
605
+ - app/services/command_tower/services/account/push/create.rb
606
+ - app/services/command_tower/services/account/push/ct_support.rb
607
+ - app/services/command_tower/services/account/push/destroy.rb
608
+ - app/services/command_tower/services/account/push/list.rb
609
+ - app/services/command_tower/services/account/push/replace.rb
610
+ - app/services/command_tower/services/account/push/send_self_test.rb
593
611
  - app/services/command_tower/services/account/pushover/create.rb
594
612
  - app/services/command_tower/services/account/pushover/ct_support.rb
595
613
  - app/services/command_tower/services/account/pushover/destroy.rb
@@ -642,6 +660,7 @@ files:
642
660
  - app/services/command_tower/services/me/capabilities.rb
643
661
  - app/services/command_tower/services/me/change_password.rb
644
662
  - app/services/command_tower/services/me/delete_account.rb
663
+ - app/services/command_tower/services/me/push_product_gate.rb
645
664
  - app/services/command_tower/services/me/pushover_product_gate.rb
646
665
  - app/services/command_tower/services/me/sms_product_gate.rb
647
666
  - app/services/command_tower/services/me/update_name.rb
@@ -660,6 +679,7 @@ files:
660
679
  - app/views/command_tower/email_verification_mailer/verify_email.html.erb
661
680
  - app/views/command_tower/messaging/rendering/email.html.erb
662
681
  - app/views/command_tower/messaging/rendering/email.text.erb
682
+ - app/views/command_tower/messaging/rendering/push.text.erb
663
683
  - app/views/command_tower/messaging/rendering/pushover.text.erb
664
684
  - app/views/command_tower/messaging/rendering/sms.text.erb
665
685
  - app/views/command_tower/password_reset_mailer/reset_password.html.erb
@@ -720,6 +740,12 @@ files:
720
740
  - app/workflows/command_tower/workflows/me/error_status.rb
721
741
  - app/workflows/command_tower/workflows/me/phone_verification/send_workflow.rb
722
742
  - app/workflows/command_tower/workflows/me/phone_verification/verify_workflow.rb
743
+ - app/workflows/command_tower/workflows/me/push/create_workflow.rb
744
+ - app/workflows/command_tower/workflows/me/push/destroy_workflow.rb
745
+ - app/workflows/command_tower/workflows/me/push/index_workflow.rb
746
+ - app/workflows/command_tower/workflows/me/push/replace_workflow.rb
747
+ - app/workflows/command_tower/workflows/me/push/test_workflow.rb
748
+ - app/workflows/command_tower/workflows/me/push/workflow_support.rb
723
749
  - app/workflows/command_tower/workflows/me/pushover/create_workflow.rb
724
750
  - app/workflows/command_tower/workflows/me/pushover/destroy_workflow.rb
725
751
  - app/workflows/command_tower/workflows/me/pushover/replace_workflow.rb
@@ -776,6 +802,8 @@ files:
776
802
  - docs/upgrades/0.12.0.md
777
803
  - docs/upgrades/0.13.0.md
778
804
  - docs/upgrades/0.13.1.md
805
+ - docs/upgrades/0.14.0.md
806
+ - docs/upgrades/0.15.0.md
779
807
  - docs/upgrades/README.md
780
808
  - lib/command_tower.rb
781
809
  - lib/command_tower/admin_scope.rb
@@ -822,6 +850,7 @@ files:
822
850
  - lib/command_tower/configuration/login/strategy/plain_text/lockable.rb
823
851
  - lib/command_tower/configuration/login/strategy/plain_text/password_reset.rb
824
852
  - lib/command_tower/configuration/messaging/config.rb
853
+ - lib/command_tower/configuration/messaging/expo.rb
825
854
  - lib/command_tower/configuration/messaging/pushover.rb
826
855
  - lib/command_tower/configuration/messaging/sms.rb
827
856
  - lib/command_tower/configuration/otp/config.rb