Skip to content

feat: minimal MailerLite addon — sync pipeline + per-form config + resync action#4

Closed
jurnskie wants to merge 1 commit into
0.xfrom
f/js/mailerlite-minimal
Closed

feat: minimal MailerLite addon — sync pipeline + per-form config + resync action#4
jurnskie wants to merge 1 commit into
0.xfrom
f/js/mailerlite-minimal

Conversation

@jurnskie
Copy link
Copy Markdown

This is the v1.0 marketplace release shape, rebuilt per colleague review:

"Basis was prima, alleen die formconfig dingen moesten anders."

The basis (sync job + listener + MailerLite SDK wrapper + per-form blueprint
section) is preserved. The over-engineering generated during exploration
(dashboard, activity log surface, retry controller + rate limiter, central
form-config repository, custom CP routes/permissions/nav, Vue/Inertia pages)
has been removed. Re-sync is delivered as a Statamic native Submission Action
that appears on the form's built-in submissions list — one button, no
parallel CP surface.

Per-form config is read DIRECTLY from each form's own YAML via
Form::find($handle)->get('enabled' | 'mappings' | 'groups'). No addon-side
mirror, no central form-config repository. Statamic's Form::appendConfigFields
already persists the values where Statamic looks for them.

Components:

src/AddonSettings.php
Thin wrapper around Statamic's native addon-settings store. Single
responsibility: read the API key. Container-bound singleton; tests
replace via app()->instance().

src/MailerLite/MailerLiteService.php + Contracts/, Exceptions/
SDK wrapper. upsertSubscriber(), ping(), listFields(), listGroups().
Translates SDK exceptions to addon-namespace types so the rest of the
addon doesn't depend on the SDK's surface.

src/Sync/SyncSubscriberJob.php
Queued upsert. Reads the API key from AddonSettings and the per-form
mappings/groups directly off the form blueprint. tries=3, backoff=
[60,300,900]. 4xx -> permanent_failure (no retry); 5xx -> transient
(Laravel retries). The finally block emits a Log::info breadcrumb but
does not maintain a separate activity store.

src/Listeners/FormSubmittedListener.php
Captures the visitor IP onto the submission pre-persist.

src/Listeners/SubmissionCreatedListener.php
Thin dispatch gate — reads enabled from the form, dispatches the
job. No driver dependency, no form-config repository.

src/Actions/ResyncToMailerLite.php
Statamic Submission Action (auto-discovered from src/Actions/). Visible
on the form's submissions list when the form has MailerLite sync
enabled. Dispatches a fresh SyncSubscriberJob per selected submission.

src/Fieldtypes/MailerLite{Field,Group}Fieldtype.php
Live-fetched dropdowns. Per-request cache keyed by sha256(api_key)
keeps the SDK call count to one per CP render.

src/StatamicMailerLiteServiceProvider.php
Bindings (AddonSettings singleton + MailerLiteService via interface)
and the per-form MailerLite section append. Listeners and the Action
auto-discover from their respective folders.

resources/blueprints/settings.yaml
Statamic native addon-settings blueprint — API key field. Rendered
under Tools → Addons → MailerLite.

Removed (vs the exploratory PR #3 history on f/js/gsd-mailerlite):

  • StorageDriverInterface + AddonYamlDriver (and the FormConfig methods).
    Per-form config now lives where Statamic stores it natively, not
    mirrored into addon storage.
  • ActivityLogger, ActivityLogEntry, ActivityLogStore (JSONL).
  • All four CP/* controllers (Dashboard, Activity, Retry, FormHealthSummary).
  • PruneActivityLogCommand.
  • FormSavedListener (the mirror writer).
  • Custom CP routes, top-level nav, configure-mailerlite permission,
    retry rate limiter.
  • Vue/Inertia Dashboard + Activity pages and the Vite build pipeline.

Quality gates:

Pest 124/124 · PHPStan level 5 clean · Pint clean.

Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

…sync action

This is the v1.0 marketplace release shape, rebuilt per colleague review:

  > "Basis was prima, alleen die formconfig dingen moesten anders."

The basis (sync job + listener + MailerLite SDK wrapper + per-form blueprint
section) is preserved. The over-engineering generated during exploration
(dashboard, activity log surface, retry controller + rate limiter, central
form-config repository, custom CP routes/permissions/nav, Vue/Inertia pages)
has been removed. Re-sync is delivered as a Statamic native Submission Action
that appears on the form's built-in submissions list — one button, no
parallel CP surface.

Per-form config is read DIRECTLY from each form's own YAML via
`Form::find($handle)->get('enabled' | 'mappings' | 'groups')`. No addon-side
mirror, no central form-config repository. Statamic's Form::appendConfigFields
already persists the values where Statamic looks for them.

Components:

  src/AddonSettings.php
    Thin wrapper around Statamic's native addon-settings store. Single
    responsibility: read the API key. Container-bound singleton; tests
    replace via app()->instance().

  src/MailerLite/MailerLiteService.php  +  Contracts/, Exceptions/
    SDK wrapper. upsertSubscriber(), ping(), listFields(), listGroups().
    Translates SDK exceptions to addon-namespace types so the rest of the
    addon doesn't depend on the SDK's surface.

  src/Sync/SyncSubscriberJob.php
    Queued upsert. Reads the API key from AddonSettings and the per-form
    mappings/groups directly off the form blueprint. tries=3, backoff=
    [60,300,900]. 4xx -> permanent_failure (no retry); 5xx -> transient
    (Laravel retries). The finally block emits a Log::info breadcrumb but
    does not maintain a separate activity store.

  src/Listeners/FormSubmittedListener.php
    Captures the visitor IP onto the submission pre-persist.

  src/Listeners/SubmissionCreatedListener.php
    Thin dispatch gate — reads `enabled` from the form, dispatches the
    job. No driver dependency, no form-config repository.

  src/Actions/ResyncToMailerLite.php
    Statamic Submission Action (auto-discovered from src/Actions/). Visible
    on the form's submissions list when the form has MailerLite sync
    enabled. Dispatches a fresh SyncSubscriberJob per selected submission.

  src/Fieldtypes/MailerLite{Field,Group}Fieldtype.php
    Live-fetched dropdowns. Per-request cache keyed by sha256(api_key)
    keeps the SDK call count to one per CP render.

  src/StatamicMailerLiteServiceProvider.php
    Bindings (AddonSettings singleton + MailerLiteService via interface)
    and the per-form MailerLite section append. Listeners and the Action
    auto-discover from their respective folders.

  resources/blueprints/settings.yaml
    Statamic native addon-settings blueprint — API key field. Rendered
    under Tools → Addons → MailerLite.

Removed (vs the exploratory PR #3 history on f/js/gsd-mailerlite):

  - StorageDriverInterface + AddonYamlDriver (and the FormConfig methods).
    Per-form config now lives where Statamic stores it natively, not
    mirrored into addon storage.
  - ActivityLogger, ActivityLogEntry, ActivityLogStore (JSONL).
  - All four CP/* controllers (Dashboard, Activity, Retry, FormHealthSummary).
  - PruneActivityLogCommand.
  - FormSavedListener (the mirror writer).
  - Custom CP routes, top-level nav, configure-mailerlite permission,
    retry rate limiter.
  - Vue/Inertia Dashboard + Activity pages and the Vite build pipeline.

Quality gates:

  Pest 124/124 · PHPStan level 5 clean · Pint clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@jurnskie jurnskie closed this May 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant