Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions docs/DATA EVENTS/data-events-reference/data-events-on.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,19 @@ ON('change', 'weather_summary', callback);
```

```js
var callback = function () {
// Do something with the location via LATITUDE() AND LONGITUDE() values
var callback = function (event) {
// event.value is the record geometry as GeoJSON (Point, LineString, or Polygon).
// event.gpsData is optional and present when external GPS metadata is available.
// gpsData keys can differ by platform. Persisted snake_case metadata is record gps_device_capture.
};

// Listens for changes to a record's geometry (location) and executes callback
ON('change-geometry', callback);
```

```js
var callback = function () {
// Do something with the repeatable location via LATITUDE() AND LONGITUDE() values
var callback = function (event) {
// Repeatable geometry is on event.value. Optional event.gpsData is device-dependent.
};

// Listens for changes to a repeatable item's geometry and executes callback
Expand Down
21 changes: 20 additions & 1 deletion docs/DATA EVENTS/data-events-reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Data Events allow users to perform ​\_actions\_​ on the mobile device when c
| save-record | Fires immediately before a record is saved and after it's been validated. Inside this event it's possible to make last-second updates to records right before the record is saved. You cannot perform asynchronous tasks in this event. Once the callback is finished the record will be saved and the editor will close. If you want to prevent the record from saving, you must use the `validate-record` or `validate-repeatable` events. The callback is passed an `event` parameter that contains `isValid` and `isDraft` properties. The `isValid` property indicates if the record is valid according to the built-in validation rules. The `isDraft` property indicates whether the record is being saved as a draft. | `ON('save-record', callback);` |
| cancel-record | Fires after a record editing session is cancelled, before `unload-record`. | `ON('cancel-record', callback);` |
| validate-record | Fires right before the record is saved to check any validations. Custom validations done in this callback will be displayed in the app alongside normal built-in validations. The callback function should contain custom validation logic and usage of `INVALID()` to notify the user with a message of why the record is invalid. This event is similar to `save-record` and `save-repeatable` except it gives you the option to prevent the record from being saved by using the `INVALID()` function. Asynchronous functions like `REQUEST()` are not supported in this event. The callback must perform all validations in a synchronous fashion with `INVALID()`. The callback is passed an `event` parameter that contains `isValid` and `isDraft` properties. The `isValid` property indicates if the record is valid according to the built-in validation rules. The `isDraft` property of this event indicates if the record was previously saved as a draft. The current draft state is determined during the `save-record` event which fires after `validate-record`. If you are looking to use the most current `isDraft` value, then make sure you use the event value in `save-record`, not the one in `validate-record`. | `ON('validate-record', callback);` |
| change-geometry | Fires when a record's geometry changes. For a new record, this event fires when the device gets a location from the GPS and adds it to the record. Once the record has a location, this event is only fired when the location is manually changed using the 'Set Location' screen. Calling `SETLOCATION` or `SETGEOMETRY` does not fire a `change-geometry` event. If you need to handle programmatic changes to the location you must explicitly handle it in your code. | `ON('change-geometry', callback);` |
| change-geometry | Fires when a record's geometry changes. For a new record, this event fires when the device gets a location from the GPS and adds it to the record. Once the record has a location, this event is only fired when the location is manually changed using the 'Set Location' screen. Calling `SETLOCATION` or `SETGEOMETRY` does not fire a `change-geometry` event. If you need to handle programmatic changes to the location you must explicitly handle it in your code. The callback receives an event object: `event.value` is the record geometry as GeoJSON. Optional `event.gpsData` may be present when external GPS metadata is available (keys can differ by platform). Persisted snake_case metadata is `gps_device_capture` on the record. | `ON('change-geometry', callback);` |
| change-project | Fires when a record's project changes. This event does not fire on default values. If you need to handle the project being set when the record is created you can use `new-record`. Setting the project programmatically with `SETPROJECT()` does not fire a `change-project` event. If you need to respond to programmatic changes in the project you must handle it explicitly after `SETPROJECT()` is called. | `ON('change-project', callback);` |
| change-status | Fires when a record's status changes. This event does not fire on default values. If you need to handle the status being set when the record is created you can use `new-record`. Setting the status programmatically with `SETSTATUS()` does not fire a `change-status` event. If you need to respond to programmatic changes in the status you must handle it explicitly after `SETSTATUS()` is called. | `ON('change-status', callback);` |
| change-assignment | Fires when a record's assignment changes. The callback is passed an `email` parameter, which is either `null` or the email address of the user assigned. | `ON('change-assignment', callback);` |
Expand All @@ -49,6 +49,25 @@ The callback for record events is passed an event parameter with a `name` attrib
}
```

A `change-geometry` event uses `value` for the GeoJSON geometry. When external GPS metadata is available, `gpsData` is a top-level sibling of `value`. Keys inside `gpsData` vary by device and client. The object below is an illustrative example, not a canonical schema.

```json
{
"name": "change-geometry",
"value": {
"type": "Point",
"coordinates": [-82.638039, 27.770787]
},
"gpsData": {
"deviceName": "Trimble R2",
"fixType": "RTK",
"satellites": 14,
"hdop": 0.8,
"...": "additional device-specific keys"
}
Comment thread
sjperaltas marked this conversation as resolved.
}
```

Below we're using the same callback to handle events from both `edit-record` and `new-record`.

```js
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Design: GPS device capture docs

## Context

`gps_device_capture` is a flexible JSON object on records. OpenAPI already defines `GpsDeviceCaptureBase` (response) and `GpsDeviceCaptureRequest` (create/update, nullable to clear). Known keys: `device_name`, `manufacturer`, `fix_type`, `satellite_count`, `hdop`, `vdop`, `pdop`, `differential_correction`, `antenna_height`, `firmware_version`, `geometry`. Additional device-specific keys are allowed.

Query API form tables expose the value as `_gps_device_capture` (schema v6+). Older form schemas may omit the column.

Ticket “Search API” means the Query API.

## Goals / Non-Goals

**Goals:**

- Make the field discoverable without reading raw OpenAPI.
- Show a realistic create/update/get payload.
- Show JSONB queries on inner keys.
- Update `change-geometry` docs only with verified event data.

**Non-Goals:**

- Schema changes, backend changes, Cypress tests.
- Re-adding `geometry_matches_capture`.
- Inventing Data Events fields that are not in source or published docs.

## Decisions

### 1. Example payload

Use the backend spec example:

```json
{
"device_name": "Trimble R2",
"manufacturer": "Trimble",
"fix_type": "RTK",
"satellite_count": 14,
"hdop": 0.8,
"geometry": { "type": "Point", "coordinates": [-82.637, 27.771] }
}
```

### 2. Query examples

Document `_gps_device_capture` as jsonb. Examples:

```sql
SELECT _record_id, _gps_device_capture
FROM "Form Name"
WHERE _gps_device_capture->>'device_name' = 'Trimble R2';

SELECT _record_id
FROM "Form Name"
WHERE _gps_device_capture @> '{"fix_type": "RTK"}';
```

Note that the column exists on schema v6 form tables.

### 3. change-geometry

`fulcrum-core` only serializes record `gps_device_capture`; it does not define the Data Events payload. `fulcrum-expressions` `GeometryEvent` still types `name` / optional `field` / `value` (GeoJSON).

Verified from mobile clients:

- Android `GeometryChangeEventHelper` and iOS `NMEALocationEventPayload` attach optional top-level `gpsData` as a **sibling of `value`** when external GPS metadata is available.
- iOS event keys are camelCase (`deviceName`, `fixType`, `satellites`). Android `LocationInfo.toMap()` currently emits snake_case (`device_name`, `fix_type`, `satellite_count`) plus `deviceName`.

Public docs MUST:

- Keep `event.value` as the GeoJSON geometry.
- Document optional `event.gpsData` without claiming a single canonical key list.
- Not invent fields beyond `gpsData` / `value` / `field` / `name`.

## File map

- `openspec/changes/document-gps-device-capture/` — this change
Comment thread
sjperaltas marked this conversation as resolved.
- `reference/RECORDS/records-intro.md`
- `reference/RECORDS/records-create.md`
- `reference/rest-api.json` (examples only)
- `reference/QUERY/query-intro.md`
- `docs/DATA EVENTS/data-events-reference/index.md`
- `docs/DATA EVENTS/data-events-reference/data-events-on.md`
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Change: Document GPS device capture in API docs

## Why

Integrators need accurate Records v2, Query API, and Data Events docs for `gps_device_capture`. [api#75](https://github.com/fulcrumapp/api/pull/75) added the OpenAPI schema only. [FLCRM-20930](https://fulcrumapp.atlassian.net/browse/FLCRM-20930) still lacks human-readable field docs, request/response examples, Query API JSONB examples, and `change-geometry` event notes.

## What Changes

- Document `gps_device_capture` on the Records API intro properties table.
- Add JSON request/response examples showing flexible, device-dependent metadata.
- Document Query API column `_gps_device_capture` and SQL examples that filter on inner keys.
- Document `change-geometry` event data only where the payload can be verified.
- Do not change the existing OpenAPI schema from api#75. Do not re-document `geometry_matches_capture`.

## Capabilities

### New Capabilities

- `gps-device-capture-docs`: Public developer documentation for GPS device capture on Records v2, Query API, and Data Events.

### Modified Capabilities

- None. This is a documentation-only change in `fulcrumapp/api`.

## Impact

- **Docs site**: Records intro, record create/update examples, Query intro, Data Events reference.
- **OpenAPI**: Example payloads only in `reference/rest-api.json`.
- **Backend / mobile**: No code changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# gps-device-capture-docs Specification

## Purpose

Developer documentation for `gps_device_capture` on Records API v2, Query API, and Data Events.

## Requirements

### Requirement: Records intro documents gps_device_capture

The Records API intro properties table SHALL include `gps_device_capture` as an optional object. The description SHALL state that it is flexible device-dependent GPS metadata, additional keys are allowed, and `null` clears the value on write.

#### Scenario: Integrator looks up record properties

- **WHEN** a developer reads Records API intro
- **THEN** they see `gps_device_capture` listed with type object and a short description of known keys (`device_name`, `manufacturer`, `fix_type`, `satellite_count`, `hdop`, `vdop`, `pdop`, `geometry`)

### Requirement: Request and response examples include GPS metadata

Create/update request examples and get-record response examples SHALL include a `gps_device_capture` object with device-dependent keys and nested GeoJSON `geometry`.

#### Scenario: Create record example includes GPS capture

- **WHEN** a developer copies the create-record example
- **THEN** the JSON includes `gps_device_capture` with at least `device_name`, `manufacturer`, `fix_type`, `satellite_count`, `hdop`, and `geometry`

### Requirement: Query API documents JSONB filtering

The Query API form system-columns table SHALL include `_gps_device_capture` (jsonb). Docs SHALL include SQL examples that filter on inner keys with `->>` and `@>`.

#### Scenario: Query records by device name

- **WHEN** a developer wants records from a Trimble R2
- **THEN** docs show `WHERE _gps_device_capture->>'device_name' = 'Trimble R2'`

#### Scenario: Query records by fix type containment

- **WHEN** a developer wants RTK fixes
- **THEN** docs show `WHERE _gps_device_capture @> '{"fix_type": "RTK"}'`

### Requirement: change-geometry documents added event data

Data Events `change-geometry` docs SHALL keep `event.value` as the GeoJSON geometry and SHALL document optional top-level `event.gpsData` (sibling of `value`) when external GPS metadata is available. Docs SHALL NOT invent a canonical key list inside `gpsData`.

Verified: Android `GeometryChangeEventHelper`; iOS `NMEALocationEventPayload` event destination. Gaps: `fulcrum-core` has no Data Events payload; `fulcrum-expressions` `GeometryEvent` does not declare `gpsData`; iOS camelCase vs Android snake_case keys.

#### Scenario: Developer handles geometry changes

- **WHEN** a developer reads `change-geometry` in the Data Events reference or `ON` docs
- **THEN** they still see `ON('change-geometry', callback)`
- **AND** they learn that `event.value` is the geometry and `event.gpsData` may be present
- **AND** they are pointed to record `gps_device_capture` for persisted snake_case metadata

### Requirement: OpenAPI schema is not rewritten

This change SHALL NOT remove or rename `GpsDeviceCaptureBase` / `GpsDeviceCaptureRequest`. It SHALL NOT document `geometry_matches_capture`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Tasks

- [x] 1. OpenSpec artifacts for `document-gps-device-capture`
- [x] 2. Add `gps_device_capture` to Records intro properties table
- [x] 3. Add GPS payload to create/update/partial-update library examples
- [x] 4. Add GPS payload to OpenAPI get/create/update examples
- [x] 5. Add `_gps_device_capture` Query API column and JSONB examples
- [x] 6. Update `change-geometry` Data Events docs with verified `gpsData` only
- [x] 7. Validate `reference/rest-api.json`
Loading
Loading