Skip to content

Release 2.24.0#147

Merged
btoron merged 38 commits intomasterfrom
release/2.24.0
Mar 4, 2026
Merged

Release 2.24.0#147
btoron merged 38 commits intomasterfrom
release/2.24.0

Conversation

@btoron
Copy link
Owner

@btoron btoron commented Mar 4, 2026

Release 2.24.0

Summary

  • Implements 22 async write methods for the Metadata API (AsyncOFSMetadata) covering workzones, workskills, properties, activity types, workskill groups, routing profiles, workskill conditions, service windows, capacity categories, and org units
  • Adds comprehensive round-trip tests (test_async_metadata_roundtrip.py) and write tests (test_async_metadata_write.py)
  • Adds Statistics API GET operations (AsyncOFSStatistics) with 3 endpoints: activity duration stats, activity travel stats, airline distance based travel
  • Adds Statistics API write operations (3 PATCH methods) with live roundtrip tests
  • Adds async resource file property methods (CO057G/U/D) with live roundtrip test using tech_photo fixture
  • Adds async miscellaneous GET operations for Core API (CO003G and others)
  • Implements Bearer token auth for async client (access_token param + useToken=True)
  • Implements async capacity area sub-resources & miscellaneous GETs for Metadata API
  • Fixes required parameters for get_calendars, get_resource_assistants, and get_resource_plans async methods
  • Fixes create_link_template to use collection URL and correct en-US language code
  • Audits and tightens Pydantic models in ofsc/models/metadata.py with extra=ignore
  • Updates docs/ENDPOINTS.md and README.md

Changes

  • ofsc/async_client/metadata.py — 22 new async write methods (create, update, delete); fix create_link_template URL
  • ofsc/async_client/statistics.py — new AsyncOFSStatistics module with 3 GET + 3 PATCH methods
  • ofsc/models/statistics.py — Pydantic models for Statistics API
  • ofsc/models/metadata.py — tighten 13 models from extra=allow to extra=ignore; add missing fields
  • ofsc/async_client/core/resources.py — CO057G/U/D file property methods + fixed required params
  • ofsc/async_client/core/_base.py — miscellaneous GET methods including get_multiday_segments with dedicated MultidaySegmentListResponse
  • ofsc/async_client/__init__.pyaccess_token parameter for token-based auth
  • ofsc/models/_base.pyaccess_token field in OFSConfig
  • tests/async/test_async_metadata_write.py — mocked write operation tests
  • tests/async/test_async_metadata_roundtrip.py — round-trip integration tests
  • tests/async/test_async_statistics.py — 48 mocked + 6 live statistics tests
  • tests/async/test_async_resources_write.py — live roundtrip test for file properties
  • tests/async/test_async_activities.py — tests for miscellaneous activity GET operations
  • tests/async/test_async_oauth.py — OAuth token tests + E2E token auth test
  • tests/async/test_async_resources_get.py — tests for fixed resource methods
  • tests/async/test_metadata_model_audit.py — 24 live audit tests for unmapped model fields
  • docs/ENDPOINTS.md — updated implementation status
  • README.md — updated method tags

Closes #137
Closes #138
Closes #139
Closes #140
Closes #141
Closes #142
Closes #146
Closes #149

🤖 Generated with Claude Code

btoron and others added 5 commits March 4, 2026 08:53
Implements all remaining async metadata write operations:
- 8 PUT: create_or_replace for ActivityTypeGroup, ActivityType, Application,
  CapacityCategory, Form, InventoryType, MapLayer, Shift
- 3 DELETE: delete_capacity_category, delete_form, delete_shift
- 4 PATCH: update_application_api_access, update_link_template,
  update_property, update_workzones (bulk)
- 7 POST/PUT: generate_application_client_secret, create_link_template,
  create_map_layer, populate_map_layers, install_plugin,
  replace_workzones (bulk PUT), populate_workzone_shapes

Also adds 33 mocked tests in test_async_metadata_write.py,
updates ENDPOINTS.md and README.md with [Async] tags.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… add round-trip tests for async metadata write operations
Add AsyncOFSStatistics module with 3 GET endpoints:
- get_activity_duration_stats() — /rest/ofscStatistics/v1/activityDurationStats
- get_activity_travel_stats() — /rest/ofscStatistics/v1/activityTravelStats
- get_airline_distance_based_travel() — /rest/ofscStatistics/v1/airlineDistanceBasedTravel

All methods return paginated Pydantic model responses (OFSResponseList subclasses).
Includes 24 mocked tests + 3 live tests, ENDPOINTS.md auto-updated.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add exclude_none=True to all 3 PATCH methods so null fields (keyId,
  resourceId) are not sent in the request body
- Flatten StatisticsPatchResponse to match actual API response shape:
  {"status": "200", "updatedRecords": N} instead of nested {"results": {...}}
- Remove StatisticsPatchResult model (not used by the actual API)
- Fix airline distance roundtrip test to send 2 data points (required by
  API for company-level updates) with a safe +1 override delta

All 54 statistics tests now pass (48 mocked + 6 live).

Closes #142

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
btoron and others added 24 commits March 4, 2026 09:06
Implement three async methods on AsyncOFSCoreResourcesMixin for binary
file-type custom properties on resources:
- get_resource_file_property (CO057G) — GET binary content
- set_resource_file_property (CO057U) — PUT binary content
- delete_resource_file_property (CO057D) — DELETE property

Mirrors the existing user property pattern from users.py. Includes
mocked tests (5) and a live roundtrip test with graceful skip when
property not configured.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…140)

Replace hardcoded 'csign' property label with 'tech_photo' fixture and
remove OFSCNotFoundError skip — test now passes unconditionally.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements GET activities/{activityId}/multidaySegments as async method.
Reuses ActivityListResponse model (no new models needed). Adds
segmentable_activity_type and segmentable_activity live test fixtures.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ults (#139)

Replace ActivityListResponse with MultidaySegmentListResponse (backed by
OFSResponseBoundedList) for get_multiday_segments, since the API returns
only items/links with no totalResults field.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…146)

Add OAuthTokenResponse model and implement AsyncOFSOauth2.get_token()
backed by /rest/oauthTokenService/v2/token with form-encoded Basic auth.
Includes mocked + live tests and retires the NotImplementedError stub.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add access_token field to OFSConfig
- Add access_token parameter to AsyncOFSC.__init__
- Replace NotImplementedError with Bearer auth in all 4 async modules
- Add E2E test: get token → token-authed client → get_activity_types

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Ts (#137)

Adds 10 new async GET endpoints to AsyncOFSMetadata:

Capacity area sub-resources (ME012G-ME018G):
- get_capacity_area_capacity_categories(label)
- get_capacity_area_workzones(label) — v2 API
- get_capacity_area_workzones_v1(label) — deprecated v1
- get_capacity_area_time_slots(label)
- get_capacity_area_time_intervals(label)
- get_capacity_area_organizations(label)
- get_capacity_area_children(label, status, fields, expand, type)

Populate status checks (ME030G, ME057G):
- get_populate_map_layers_status(download_id)
- get_populate_workzone_shapes_status(download_id)

Workzone key (ME059G):
- get_workzone_key()

Models added: CapacityAreaCapacityCategory/Response, CapacityAreaWorkZone/Response,
CapacityAreaWorkZoneV1/Response, CapacityAreaTimeSlot/Response,
CapacityAreaTimeInterval/Response, CapacityAreaOrganization/Response,
CapacityAreaChildrenResponse, PopulateStatusResponse,
WorkZoneKeyElement, WorkZoneKeyResponse

Tests: 429 passing, 0 failures.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…sistants, get_resource_plans

- get_calendars: add resources, date_from, date_to (API requires all three)
- get_resource_assistants: add date_from, date_to (API requires both; range ≤ 14 days)
- get_resource_plans: add date_from, date_to (API requires both)
- Fix capture script: correct params for all three endpoints, fix activity link
  path (start_before → starts_after), rename 3 mislabeled *_200_success files
  that returned 404 to *_404_not_supported
- Add live tests and saved-response validation tests for the three fixed methods

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Merges fix for get_calendars, get_resource_assistants, and get_resource_plans
methods that were missing required API parameters.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add missing fields detected by live API audit:
  - Shift: links
  - ResourceType: translations, links
  - CapacityCategory: links
  - InventoryType: name, unitOfMeasurement, links
  - Form: links
  - MapLayer: links
- Tighten 13 models from extra="allow" to extra="ignore":
  Shift, ResourceType, CapacityCategory, InventoryType, Form, MapLayer,
  ActivityTypeFeatures, Link, ApiMethod, ApiEntity,
  ApplicationApiAccessListResponse, RoutingProfile, RoutingPlan
- Add 24 live audit tests that detect unmapped __pydantic_extra__ fields
- Fix test_model.py to compare links as Link model instances

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Introduced a comprehensive markdown report detailing test execution metrics, including total tests, pass rates, code coverage, and failure analysis.
- Included breakdowns by directory and marker distribution, along with root cause analysis for test failures.
- Provided recommendations for new tests and improvements to existing coverage, addressing critical gaps and issues identified during testing.
…=allow

Merges metadata model improvements from issue #149:
- Tighten Pydantic models in ofsc/models/metadata.py with extra=allow
- Add comprehensive model audit tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
btoron and others added 5 commits March 4, 2026 14:36
…k_instance fixture

- Add @pytest.mark.uses_real_data to ~75 unmarked tests that make real API
  calls (async TestAsyncGet* classes, sync test_base/test_model/metadata tests)
- Add credentials-free mock_instance fixture to tests/async/conftest.py using
  dummy credentials so mocked tests run without .env file
- Migrate ~200+ mocked tests from async_instance to mock_instance across 18
  files — any test that sets _client.X = AsyncMock(...) no longer needs real
  credentials for fixture setup
- Result: pytest -m "not uses_real_data" passes 455 tests with 0 failures
  and no .env required; uses_real_data collection grows to 449 tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Register a new pytest marker `uses_local_data` for tests that load files
from tests/saved_responses/ (gitignored, unavailable in CI). Apply it to
23 TestAsync*SavedResponses classes and update the GitHub Actions workflow
to exclude both uses_real_data and uses_local_data markers.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@btoron btoron added the release:minor Triggers minor version tag on merge label Mar 4, 2026
btoron and others added 4 commits March 4, 2026 17:51
Replace async_instance with mock_instance in all mocked tests across 8
test files. These tests already mock the HTTP client internally, so they
don't need real API credentials. Using async_instance caused KeyError on
companyName in CI environments without a .env file.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…fensive validator

- Add uses_local_data marker to test_import_plugin_file_success to skip in CI
  (test reads tests/test.xml before mock intercepts, file is gitignored)
- Add uses_real_data markers to all sync tests in test_users.py,
  test_applications.py, and test_workzones.py that require API credentials
- Add pytest import to test_users.py and test_applications.py
- Make OFSConfig.set_base_URL validator defensive using .get() to avoid
  KeyError when companyName validation fails before baseURL is processed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Run ruff format on all source and test files to establish a consistent
code style baseline now that ruff is a tracked dev dependency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@btoron btoron merged commit f03ac19 into master Mar 4, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment