Add OSM service policy architecture with per-provider tile caching#128
Add OSM service policy architecture with per-provider tile caching#128dougborg wants to merge 7 commits intoFoggedLens:mainfrom
Conversation
Introduces a unified service policy system (ServiceType, ServicePolicy, ServicePolicyResolver, ServiceRateLimiter) that resolves compliance rules per-URL, covering OSMF official services and third-party tile providers. Custom/self-hosted endpoints get permissive defaults. Key changes: - ServicePolicyResolver maps URLs to policies (OSM tile server, Nominatim, editing API, Overpass, Bing, Mapbox, custom) - Nominatim: adds 1-req/sec rate limiting and client-side result caching with 5-minute TTL as required by Nominatim usage policy - OSM tile server: blocks offline tile downloads (explicitly prohibited by tile usage policy) with user-facing dialog - Attribution dialog: adds tappable license link to openstreetmap.org/copyright when using OSM-based tile providers (ODbL requirement) - OSM editing API: enforces max 2 concurrent download threads via semaphore - 23 unit tests covering policy resolution, URL template parsing, custom overrides, rate limiting, and all policy values Builds on top of PR FoggedLens#123 (UserAgentClient) and PR FoggedLens#127 (NetworkTileProvider). Compatible with PR FoggedLens#114 (Overpass parallelization) — Overpass policy defers to NodeDataManager's _AsyncSemaphore. https://claude.ai/code/session_01XyRTrax1tmtjcuT7CMoJhD
…e guard - Fix wrong package name in test (deflock -> deflockapp) that prevented compilation - Fix double-release of Nominatim semaphore on non-200 responses using try/finally - Remove silent _currentCount > 0 guard in semaphore release to surface mismatched calls - Add public resolveByType() to avoid private member access from ServiceRateLimiter Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces a unified “service policy” layer to enforce OSM ecosystem compliance rules (rate limiting, caching, offline download restrictions, attribution links, and concurrency caps) across URL-based services like Nominatim, OSM tiles, and the OSM editing API.
Changes:
- Added
ServicePolicy/ServicePolicyResolverand a globalServiceRateLimiterto enforce per-service compliance constraints. - Integrated compliance into UX and services: block offline downloads for restricted tile servers, add attribution link, and enforce OSM API + Nominatim limits.
- Added a comprehensive unit test suite for policy resolution and rate limiting behavior.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
lib/services/service_policy.dart |
Adds policy definitions, URL/type resolution, and shared rate limiter/semaphore implementation. |
lib/services/search_service.dart |
Adds Nominatim client-side caching + uses ServiceRateLimiter for 1 req/sec enforcement. |
lib/services/map_data_submodules/nodes_from_osm_api.dart |
Enforces OSM editing API concurrency limit via ServiceRateLimiter. |
lib/models/tile_provider.dart |
Exposes allowsOfflineDownload and servicePolicy from tile URL templates. |
lib/widgets/download_area_dialog.dart |
Blocks offline downloads when the selected tile type’s policy disallows it. |
lib/widgets/map/map_overlays.dart |
Adds attribution dialog license link opening via url_launcher. |
test/services/service_policy_test.dart |
Adds tests for policy resolution, overrides, and rate limiting. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Each tile provider/type gets its own DeflockTileProvider with frozen config and a dedicated ProviderTileCacheStore (MapCachingProvider impl). This enforces policy-driven TTL (e.g., OSM 7-day minimum), configurable per-provider cache size limits with LRU eviction, and eliminates AppState lookups at request time. Also fixes: unused import in map_overlays, untyped variable in nodes_from_osm_api, and isOfflineOnly excluded from offline tile ImageProvider equality (prevented Flutter image cache from distinguishing online vs offline tile requests). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…issues - Fix custom policy override using substring matching (now exact/subdomain) - Reorder rate limiter to acquire semaphore before checking interval - Add StateError guard for semaphore over-release - Add error handling for attribution URL launch - Make Nominatim result cache static across SearchService instances - Fix Navigator.pop context deactivation in download area dialog - Remove flaky timing assertion; add concurrent caller rate-limit test Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Cache TileType.servicePolicy as late final field to avoid repeated URL parsing - Make TileLayerManager.dispose() synchronous to match Flutter's State.dispose contract - Add clarifying comments on _ensureDirectory Completer latch and _evictIfNeeded staleness - Internationalize offline-not-permitted dialog message across all 11 locales - Add test verifying 3rd semaphore acquire blocks until a slot is released - Remove extra blank line in nodes_from_osm_api.dart Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Fix eviction accounting: only count freed bytes after successful delete - Fix TTL override to enforce minimum (use later of server staleAt vs override) - Fix _lastRequestTime doc comment to reflect it tracks acquire time, not completion - Use unawaited() with catchError in dispose() to avoid dropped exceptions - Use non-empty urlTemplate in fallback provider to avoid empty tile URLs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Guard rate-limit delay with try/catch to release semaphore slot on failure - Fix cache size estimate: increment _estimatedSize on putTile writes - Clean up orphan .meta files during eviction (no matching .tile) - Make resolveType() check custom overrides for consistency with resolve() - Simplify UUID to const Uuid() (MathRNG unnecessary for deterministic v5) - Fix misleading doc comment about flutter_map UUID matching Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Let's put a pin in this one for a week or so; we may end up being able to just move to vector tiles. That'll be a massive change, but worthy if a box of crabs can pull it off. |
Combines per-provider disk caching (PR FoggedLens#128) with error retry via reset stream and silenceExceptions: false (PR FoggedLens#132). Merge conflicts in tile_layer_manager.dart resolved to keep both the multi-provider cache map and the reset stream / retry timer. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Service Policy System
ServiceType,ServicePolicy,ServicePolicyResolver,ServiceRateLimiter) that resolves compliance rules per-URL, covering OSMF official services and third-party tile providers. Custom/self-hosted endpoints get permissive defaults.Per-Provider Tile Caching
DeflockTileProviderinstance with frozen config (no AppState lookups at request time) and a dedicatedProviderTileCacheStoreimplementing flutter_map'sMapCachingProvider{appCacheDir}/tile_cache/{providerId}/{tileTypeId}/— separate size limits, independent evictionBuilds on top of PR #123 (UserAgentClient) and PR #127 (NetworkTileProvider). Compatible with PR #114 (Overpass parallelization) — Overpass policy defers to NodeDataManager's _AsyncSemaphore.
Test plan
flutter analyze— zero issues{appCacheDir}/tile_cache/🤖 Generated with Claude Code