$npx -y skills add microsoft/power-platform-skills --skill add-geolocationInternal implementation skill invoked by /add-native for native background GPS tracking with durable storage and Dataverse sync using @microsoft/power-apps-native-bglocation.
| 1 | **Shared instructions: [shared-instructions.md](${CLAUDE_SKILL_DIR}/../../../shared/shared-instructions.md)** - read first. |
| 2 | |
| 3 | # Add Geolocation |
| 4 | |
| 5 | Internal helper for `/add-native geolocation`, `/add-native location-tracking`, `/add-native background-location`, `/add-native gps-tracking`, and `/add-native @microsoft/power-apps-native-bglocation`. |
| 6 | |
| 7 | Use this only for continuous/background GPS tracking with native durable storage and inline Dataverse upload. For a single foreground coordinate read, use `/add-native location` (`expo-location`) instead. |
| 8 | |
| 9 | Hard rules: |
| 10 | - Auth is MSAL-only. Do not expose OneAuth or an auth selector. |
| 11 | - Use `geoService(dataSource, app_id)` and `BgLocationClient`. |
| 12 | - Do not use `GeolocationExtension`, HostingSDK, PCF, Launch URI, or CordovaV2 bridge paths. |
| 13 | - Do not install packages or edit native config. |
| 14 | - The control is not usable until its Dataverse target table and mapped columns are verified. |
| 15 | |
| 16 | ## 1. Verify app and package |
| 17 | |
| 18 | ```bash |
| 19 | test -f app.config.js && test -f power.config.json && test -f package.json && test -d src |
| 20 | node -e "const p=require('./package.json'); const m='@microsoft/power-apps-native-bglocation'; if (!p.dependencies?.[m]) { console.error('MISSING: ' + m); process.exit(1); } console.log('OK: geolocation package present');" |
| 21 | ``` |
| 22 | |
| 23 | If either check fails, stop. The template/app must already ship the native package. |
| 24 | |
| 25 | ## 2. Verify the Dataverse target table first |
| 26 | |
| 27 | This control uses the package default Dataverse entity set: |
| 28 | |
| 29 | ```text |
| 30 | msdyn_locationrecords |
| 31 | ``` |
| 32 | |
| 33 | Do not ask the user for a table name and do not invent a custom table. `msdyn_locationrecords` must already exist before the control can be used. |
| 34 | |
| 35 | ```bash |
| 36 | ENV_JSON=$(node "${CLAUDE_SKILL_DIR}/../../../scripts/resolve-environment.js" "$(node -e "console.log(require('./power.config.json').environmentId)")") |
| 37 | ENV_URL=$(node -e "const j=JSON.parse(process.argv[1]); process.stdout.write(j.environmentUrl || '')" "$ENV_JSON") |
| 38 | |
| 39 | TABLE="msdyn_locationrecords" |
| 40 | node "${CLAUDE_SKILL_DIR}/../../../scripts/dataverse-request.js" "$ENV_URL" GET \ |
| 41 | "EntityDefinitions?\$filter=EntitySetName eq '$TABLE'&\$select=LogicalName,EntitySetName" |
| 42 | ``` |
| 43 | |
| 44 | Required result: |
| 45 | - One row returned: capture `LogicalName` and verify columns. |
| 46 | - Empty `value: []`: stop. Mark `BLOCKED (target table missing)`. Do not create the table, do not route to `/add-dataverse`, and do not report the control as usable. Tell the user to run the geolocation-control table provisioning/setup mechanism, then re-run `/add-native geolocation`. |
| 47 | - Auth/environment error: stop. Mark `UNVERIFIED (target table not checked)`. Do not report the control as usable. |
| 48 | |
| 49 | When the table exists, verify every mapped column exists: |
| 50 | |
| 51 | ```bash |
| 52 | node "${CLAUDE_SKILL_DIR}/../../../scripts/dataverse-request.js" "$ENV_URL" GET \ |
| 53 | "EntityDefinitions(LogicalName='<logicalName>')/Attributes?\$select=LogicalName,AttributeType" |
| 54 | ``` |
| 55 | |
| 56 | Required default `fieldMap` columns from the package README: |
| 57 | |
| 58 | ```text |
| 59 | msdyn_locationrecordid, msdyn_appid, msdyn_latitude, msdyn_longitude, |
| 60 | msdyn_altitude, msdyn_accuracy, msdyn_heading, msdyn_speed, msdyn_timestamp |
| 61 | ``` |
| 62 | |
| 63 | If any active `fieldMap` column is missing, stop. Mark `BLOCKED (target columns missing)` and tell the user to fix the control table through the geolocation-control table provisioning/setup mechanism. |
| 64 | |
| 65 | ## 3. Write `src/native/geolocation.ts` |
| 66 | |
| 67 | Create or patch `src/native/geolocation.ts` so screens import this wrapper, not the package directly. |
| 68 | |
| 69 | The target config must require the README's two tracking flags: |
| 70 | - `trackInBackground: boolean` |
| 71 | - `persistAcrossRestarts: boolean` |
| 72 | |
| 73 | ```ts |
| 74 | // src/native/geolocation.ts |
| 75 | import { |
| 76 | geoService, |
| 77 | BgLocationClient, |
| 78 | AuthMethod, |
| 79 | ConnectionType, |
| 80 | } from '@microsoft/power-apps-native-bglocation'; |
| 81 | import type { |
| 82 | DataverseDataSource, |
| 83 | LocationData, |
| 84 | PermissionStatus, |
| 85 | } from '@microsoft/power-apps-native-bglocation'; |
| 86 | |
| 87 | export type { LocationData, PermissionStatus } from '@microsoft/power-apps-native-bglocation'; |
| 88 | |
| 89 | export type GeoTrackingTarget = Omit<DataverseDataSource, 'authMethod' | 'connectionType'>; |
| 90 | |
| 91 | export type GeoResult<T> = |
| 92 | | { ok: true; value: T } |
| 93 | | { |
| 94 | ok: false; |
| 95 | reason: 'NATIVE_MODULE_MISSING' | 'PERMISSION_DENIED' | 'TRACKING_FAILED'; |
| 96 | message?: string; |
| 97 | }; |
| 98 | |
| 99 | let activeClient: BgLocationClient | null = null; |
| 100 | |
| 101 | function client(): BgLocationClient { |
| 102 | return activeClient ?? new BgLocationClient(); |
| 103 | } |
| 104 | |
| 105 | function dataSource(target: GeoTrackingTarget): DataverseDataSource { |
| 106 | return { |
| 107 | ...target, |
| 108 | authMethod: AuthMethod.MSAL, |
| 109 | connectionType: ConnectionType.Dataverse, |
| 110 | }; |
| 111 | } |
| 112 | |
| 113 | function fail(error: unknown): Extract<GeoResult<never>, { ok: false }> { |
| 114 | const message = error instanceof Error |