ycloudyusa / yusaopeny_ymca360
YUSA OpenY YMCA360 integration
Package info
github.com/YCloudYUSA/yusaopeny_ymca360
Type:drupal-module
pkg:composer/ycloudyusa/yusaopeny_ymca360
README
πΊπ¦ | This module is maintained by Ukrainian developers. Please consider supporting Ukraine in a fight for their freedom and the safety of Europe. |
Pulls YMCA360 schedule occurrences (in-studio + live stream) into the YMCA Website Services Program Event Framework as session nodes, using a windowed, reconciliation-based syncer.
- π Read our getting-started instructions
- π Search the documentation
- π€ Review community resources
Table of contents
- Highlights
- Requirements
- Installation
- Configuration
- How it works
- Drush commands
- Upgrading
- Development
- Maintainers
Highlights
| Windowed sync | Native API filters (start_at, end_at, scheduled_from) β fetches only the next N days, no client-side cap, no unbounded paging. |
| Source-of-truth reconciliation | Mappings missing from the current extract are flagged as orphans and removed. Cap (max_deletes_per_run, default 500) protects against misconfigurations. |
| Empty-extract circuit breaker | Skips orphan reconciliation until N consecutive empty extracts confirm emptiness β prevents a single API blip from wiping the schedule. |
| Trash-aware deletes | Deletes are wrapped in trash.manager->executeInTrashContext('ignore', β¦) so reconciliation performs real deletes instead of soft-trashing. |
| Configurable canceled UX | Canceled occurrences stay published with a CANCELED: prefix by default, mirroring the Y360 app behaviour. |
| Optional theming hooks | Writes field_session_status and field_session_original_instructor when the session bundle exposes them, letting themes apply strikethrough or substitute-instructor labels. |
| Drush surface | y360:status, y360:sync, y360:reset-hashes, y360:ping β operators no longer need php:eval. |
Requirements
| Requirement | Version |
|---|---|
| Drupal core | ^11 |
YMCA Website Services (yusaopeny) |
11.x |
drupal/trash |
optional, recommended |
Note
When the trash module is present, the syncer detects trash.manager at runtime and bypasses soft-delete during reconciliation only β user-initiated deletes of other bundles continue to be captured by Trash.
Installation
composer require ycloudyusa/yusaopeny_ymca360 drush en yusaopeny_ymca360 yusaopeny_ymca360_instudio -y drush updb -y
The submodule's install hook adds yusaopeny_ymca360_instudio.syncer to ymca_sync.settings.active_syncers and (when the trash module is enabled) removes syncer-owned bundles from trash.settings.enabled_entity_types.node so reconciliation deletes are real.
Update hooks (run on existing installs after upgrade)
| Hook | Purpose |
|---|---|
yusaopeny_ymca360_instudio_update_10001 |
Backfills the new sync settings (window_days, page_size, max_deletes_per_run, canceled_title_prefix, canceled_publish_behavior) when missing. |
yusaopeny_ymca360_instudio_update_10002 |
Removes the syncer-owned bundles (session, activity, class, program, program_subcategory) from trash.settings.enabled_entity_types.node if Trash is enabled and tracks nodes. Other bundles the site has opted into Trash for are preserved. |
Configuration
Administration Β» YMCA Website Services Β» Integrations Β» YMCA360
- Credentials & Schedules β API credentials and the
schedule_idallow-list (Group Fitness, Pool, Racquetball, etc.). - Locations mapping β maps Y360 branches and studios onto site location nodes.
Administration Β» YMCA Website Services Β» Integrations Β» YMCA360 In-Studio
Automatic Sync
| Setting | Default | Notes |
|---|---|---|
enable_cron |
off | When on, the submodule's hook_cron runs the syncer on every Drupal cron tick. |
Sync Window
| Setting | Default | Notes |
|---|---|---|
window_days |
14 |
How many days ahead of now to pull. Items before now are never extracted, so reconciliation removes them on the next run. |
page_size |
500 |
Items per API page. Larger pages = fewer requests but more memory per request. |
max_deletes_per_run |
500 |
Hard cap on deletions per sync cycle. Excess is logged and deferred to subsequent runs. Set to 0 to disable. |
empty_extract_threshold |
2 |
How many consecutive empty extracts must occur before reconciliation runs against an empty set. Lower it to 1 to disable the circuit breaker. |
Canceled sessions
| Setting | Default | Notes |
|---|---|---|
canceled_title_prefix |
CANCELED: |
Prepended to the session title when the occurrence is canceled. Empty string = no prefix. |
canceled_publish_behavior |
keep_published |
One of keep_published (canceled stays visible with prefix), follow_api (respects API published flag), always_unpublish (hides canceled). |
Tip
After changing canceled_publish_behavior or canceled_title_prefix, run drush y360:reset-hashes so the next sync rewrites session nodes instead of skipping them via the hash no-op path.
How it works
flowchart LR
API([YMCA360 API]) --> E[Extractor]
E -->|items| T[Transformer]
T -->|create / update / delete| L[Loader]
L -->|session nodes| D[(Drupal)]
L -->|y360_mapping| D
E -.->|circuit breaker streak| KV[(keyvalue)]
Loading
- Extractor builds the
[now, now + window_days]window and callsY360Client::getSchedulesWindowed()with the API's native filters. Pagination is API-driven. Tracks consecutive empty extracts in theyusaopeny_ymca360_syncerkeyvalue collection and togglesDataWrapper::skipOrphanReconciliationuntil emptiness is confirmed. - Transformer classifies the working set:
status=deletedβ existing mapping queued for delete.- hash matches existing mapping β no-op (unchanged item).
- hash differs β update.
- no existing mapping β create.
- mappings whose
y360idis missing from the current extract β orphan, queued for delete (capped, may be skipped by the circuit breaker).
- Loader creates or updates
sessionnodes viaapplySessionFields()(title with optional cancel prefix, class/activity, time paragraph, location, room, instructor, description, ages, optional fields). Publish state derives fromisPublishedSession(). Deletes are wrapped intrash.manager->executeInTrashContext('ignore', β¦).
The hash on each y360_mapping row is md5(serialize($apiItem)) so any field change in the upstream item triggers an update on the next sync.
Note
Under the hood the module uses YMCA Sync. When the syncer runs on Drupal cron, run cron often (e.g. every 15 minutes). Sites that prefer an explicit job runner can wire one with drush y360:sync.
Drush commands
| Command | Alias | Purpose |
|---|---|---|
drush y360:status |
y360-st |
Snapshot of mapping counts (total, in-window, past, future, blank-hash), key config values, last cron run. |
drush y360:sync [--syncer=β¦] |
y360-sync |
Runs the syncer once. Defaults to yusaopeny_ymca360_instudio.syncer. |
drush y360:reset-hashes |
y360-rh |
Clears hash on every mapping row so the next sync re-applies session fields. |
drush y360:ping |
y360-ping |
Pings the YMCA360 API and prints a one-line OK / failure message. |
Upgrading
Important
2.0.0 is a breaking release. Drupal 10 support is dropped (core_version_requirement: ^11). The legacy Y360Cleaner service, the yusaopeny_ymca360_cron hook, and the drush y360:cleanup command have been removed β reconciliation now handles past mappings as part of every sync run.
1.x β 2.0.0 checklist
- Site is on Drupal 11.
-
composer require ycloudyusa/yusaopeny_ymca360:^2.0 -
drush updb -yβ appliesupdate_10001(settings backfill) andupdate_10002(Trash bundle exclusion). - Verify
drush y360:statusshows the expected window and counts. - Run
drush y360:synconce and check the watchdog channelyusaopeny_ymca360_syncer. - If you maintained a custom
ExtractorBasesubclass, update its constructor β aKeyValueFactoryInterfaceargument was added.
Development
The module ships with no version: field in composer.json. Composer resolves the package version from the latest git tag β keep it that way and tag releases off 2.x.
The pipeline is wired through three abstract base services (yusaopeny_ymca360.{extractor,transformer,loader}) that submodules extend via parent: references in their own *.services.yml. See modules/yusaopeny_ymca360_instudio/yusaopeny_ymca360_instudio.services.yml for the canonical example.
Logs land in the yusaopeny_ymca360_syncer channel; tail with:
drush ws --type=yusaopeny_ymca360_syncer --extended
Maintainers
Issues and pull requests live at https://github.com/YCloudYUSA/yusaopeny_ymca360.
Acknowledgements
The 2.0.0 release β windowed sync, reconciliation, Trash-aware deletes, configurable canceled UX, drush commands β was sponsored by YMCA of Northern Colorado and delivered by ITCare.