Requirements Document
Introduction
Add an isSensitiveLocked boolean field to both entrances and massifs in the GrottoCenter API. When an administrator locks the sensitivity of an entrance or massif, only administrators can subsequently change its isSensitive value. The lock is a deliberate, symmetric admin action — it can freeze sensitivity in either state (on or off). Additionally, massif cascade operations (marking a massif as sensitive) must skip entrances whose sensitivity is locked.
Glossary
- TEntrance: The Waterline model (
api/models/TEntrance.js) representing a cave entrance in the t_entrance table.
- TMassif: The Waterline model (
api/models/TMassif.js) representing a massif in the t_massif table.
- EntranceService: The service (
api/services/EntranceService.js) containing entrance business logic including data coercion and creation.
- MassifService: The service (
api/services/MassifService.js) containing massif business logic including sensitivity cascade propagation.
- mark-sensitive: The controller (
api/controllers/v1/massif/mark-sensitive.js) that sets a massif as sensitive and cascades to child entrances.
- unmark-sensitive: The controller (
api/controllers/v1/massif/unmark-sensitive.js) that removes sensitivity from a massif (no cascade to entrances).
- preview-sensitive: The controller (
api/controllers/v1/massif/preview-sensitive.js) that returns a count of entrances that would be affected by a sensitivity cascade.
- isSensitiveLocked: A boolean column on
t_entrance and t_massif. When true, only administrators can change the isSensitive value of that entity. Defaults to false (unlocked).
- Cascade_Operation: The process by which marking a massif as sensitive propagates
isSensitive = true to all non-sensitive entrances geographically within the massif polygon.
- Administrator: A user whose JWT token groups include the
ADMINISTRATOR role as checked by RightService.hasGroup.
Requirements
Requirement 1: Add isSensitiveLocked column to entrance and massif tables
User Story: As a database administrator, I want is_sensitive_locked columns on t_entrance and t_massif, so that the lock state is persisted and queryable.
Acceptance Criteria
- THE
t_entrance table SHALL have a column is_sensitive_locked of type boolean, NOT NULL, with a default value of false.
- THE
t_massif table SHALL have a column is_sensitive_locked of type boolean, NOT NULL, with a default value of false.
- THE migration script SHALL be idempotent (using
IF NOT EXISTS or equivalent safe patterns).
- THE TEntrance Waterline model SHALL include an
isSensitiveLocked attribute mapped to the is_sensitive_locked column with type: 'boolean', allowNull: false, defaultsTo: false.
- THE TMassif Waterline model SHALL include an
isSensitiveLocked attribute mapped to the is_sensitive_locked column with type: 'boolean', allowNull: false, defaultsTo: false.
Requirement 2: Return isSensitiveLocked in API responses
User Story: As a frontend developer, I want the API to include isSensitiveLocked in entrance and massif responses, so that the UI can render lock state indicators and enforce client-side logic.
Acceptance Criteria
- THE
toEntrance converter SHALL include isSensitiveLocked in its output, sourced from source.isSensitiveLocked or source.is_sensitive_locked.
- THE
toMassif converter SHALL include isSensitiveLocked in its output, sourced from source.isSensitiveLocked.
- WHEN
isSensitiveLocked is null or undefined on the source object (e.g., old data before migration), THE converters SHALL default the output value to false.
- THE
GET /api/v1/entrances/:id response SHALL include the isSensitiveLocked field.
- THE
GET /api/v1/massifs/:id response (via toMassif) SHALL include the isSensitiveLocked field.
Requirement 3: Entrance update respects lock state
User Story: As a caver, I want the API to reject my attempt to change isSensitive on a locked entrance, so that administrator-locked sensitivity cannot be overridden.
Acceptance Criteria
- IF an entrance has
isSensitiveLocked set to true and the request sender is not an Administrator, THEN THE PUT /api/v1/entrances/:id endpoint SHALL respond with 403 Forbidden when the request body contains an isSensitive value different from the current persisted value.
- IF an entrance has
isSensitiveLocked set to true and the request sender is an Administrator, THEN THE PUT /api/v1/entrances/:id endpoint SHALL allow changes to isSensitive.
- IF an entrance has
isSensitiveLocked set to false or undefined, THEN THE existing sensitivity permission logic SHALL apply unchanged (non-admins can mark as sensitive but cannot unmark).
- THE
PUT /api/v1/entrances/:id endpoint SHALL allow administrators to set isSensitiveLocked to true or false.
- THE
PUT /api/v1/entrances/:id endpoint SHALL reject (ignore or 403) attempts by non-administrators to change isSensitiveLocked.
Requirement 4: Entrance creation respects isSensitiveLocked
User Story: As a developer, I want the entrance creation flow to accept isSensitiveLocked from administrators, so that administrators can lock sensitivity at creation time.
Acceptance Criteria
- WHEN an Administrator sends
isSensitiveLocked: true in a POST /api/v1/entrances request, THE created entrance SHALL have isSensitiveLocked set to true.
- WHEN a non-Administrator sends
isSensitiveLocked: true in a POST /api/v1/entrances request, THE API SHALL ignore the field and create the entrance with isSensitiveLocked set to false.
- WHEN
isSensitiveLocked is omitted from the request body, THE created entrance SHALL default to isSensitiveLocked: false.
Requirement 5: Massif sensitivity lock enforcement
User Story: As an administrator, I want the massif sensitivity endpoints to respect the lock state, so that the sensitivity of a locked massif cannot be changed until it is unlocked.
Acceptance Criteria
- IF a massif has
isSensitiveLocked set to true, THEN THE POST /api/v1/massifs/:id/mark-sensitive endpoint SHALL respond with 403 Forbidden with a message indicating the massif sensitivity is locked.
- IF a massif has
isSensitiveLocked set to true, THEN THE POST /api/v1/massifs/:id/unmark-sensitive endpoint SHALL respond with 403 Forbidden with a message indicating the massif sensitivity is locked.
- THE
PUT /api/v1/massifs/:id endpoint SHALL allow administrators to set isSensitiveLocked to true or false.
- THE
PUT /api/v1/massifs/:id endpoint SHALL strip isSensitiveLocked from the update payload if the request sender is not an Administrator (same pattern as isSensitive being stripped).
Requirement 6: Massif cascade skips locked entrances
User Story: As an administrator performing a massif cascade, I want the cascade to skip entrances whose sensitivity is locked, so that manually locked entrances remain unchanged.
Acceptance Criteria
- WHEN
MassifService.propagateSensitivityToEntrances runs, THE query SHALL exclude entrances where is_sensitive_locked = true from the set of entrances to update.
- THE
POST /api/v1/massifs/:id/mark-sensitive response SHALL include a skippedLockedCount field indicating how many entrances were skipped due to their sensitivity being locked.
- THE
POST /api/v1/massifs/:id/mark-sensitive response SHALL continue to include the count field indicating how many entrances were actually updated.
- WHEN entrance auto-inheritance is triggered during entrance creation (point falls within a sensitive massif), THE auto-inheritance SHALL still apply regardless of the entrance's lock state because the entrance is new and has no prior lock.
Requirement 7: Preview endpoint reports locked entrance count
User Story: As an administrator previewing a cascade, I want to know how many entrances are locked and will be skipped, so that I can make an informed decision before confirming.
Acceptance Criteria
- THE
GET /api/v1/massifs/:id/preview-sensitive response SHALL include a lockedCount field indicating how many entrances within the massif have isSensitiveLocked = true and isSensitive = false (i.e., locked in non-sensitive state and thus would be skipped).
- THE
GET /api/v1/massifs/:id/preview-sensitive response SHALL continue to include the count field indicating the number of non-sensitive, non-locked entrances that would be affected.
- IF all non-sensitive entrances in the massif are locked, THE
count field SHALL be 0 and lockedCount SHALL reflect the total locked non-sensitive entrance count.
Requirement 8: Massif update endpoint handles isSensitiveLocked
User Story: As an administrator, I want to set or unset the sensitivity lock on a massif via the update endpoint, so that I can control whether the massif's sensitivity can be changed.
Acceptance Criteria
- WHEN an Administrator sends
isSensitiveLocked in the PUT /api/v1/massifs/:id request body, THE endpoint SHALL persist the value to the is_sensitive_locked column.
- WHEN a non-Administrator sends
isSensitiveLocked in the PUT /api/v1/massifs/:id request body, THE endpoint SHALL strip the field (not persist it).
- THE
MassifService.getConvertedDataFromClientRequest SHALL extract isSensitiveLocked from the request using the same coerceBool pattern used for isSensitive.
Requirement 9: Update Swagger/OpenAPI specification
User Story: As a frontend developer or API consumer, I want the OpenAPI spec to document the isSensitiveLocked field, so that I can understand the API contract without reading the source.
Acceptance Criteria
- THE
assets/swaggerV1.yaml SHALL document isSensitiveLocked as a boolean field in the entrance request and response schemas.
- THE
assets/swaggerV1.yaml SHALL document isSensitiveLocked as a boolean field in the massif request and response schemas.
- THE
assets/swaggerV1.yaml SHALL document the skippedLockedCount field in the mark-sensitive response schema.
- THE
assets/swaggerV1.yaml SHALL document the lockedCount field in the preview-sensitive response schema.
- THE
assets/swaggerV1.yaml SHALL document the 403 response for mark-sensitive and unmark-sensitive when the massif sensitivity is locked.
Requirements Document
Introduction
Add an
isSensitiveLockedboolean field to both entrances and massifs in the GrottoCenter API. When an administrator locks the sensitivity of an entrance or massif, only administrators can subsequently change itsisSensitivevalue. The lock is a deliberate, symmetric admin action — it can freeze sensitivity in either state (on or off). Additionally, massif cascade operations (marking a massif as sensitive) must skip entrances whose sensitivity is locked.Glossary
api/models/TEntrance.js) representing a cave entrance in thet_entrancetable.api/models/TMassif.js) representing a massif in thet_massiftable.api/services/EntranceService.js) containing entrance business logic including data coercion and creation.api/services/MassifService.js) containing massif business logic including sensitivity cascade propagation.api/controllers/v1/massif/mark-sensitive.js) that sets a massif as sensitive and cascades to child entrances.api/controllers/v1/massif/unmark-sensitive.js) that removes sensitivity from a massif (no cascade to entrances).api/controllers/v1/massif/preview-sensitive.js) that returns a count of entrances that would be affected by a sensitivity cascade.t_entranceandt_massif. Whentrue, only administrators can change theisSensitivevalue of that entity. Defaults tofalse(unlocked).isSensitive = trueto all non-sensitive entrances geographically within the massif polygon.ADMINISTRATORrole as checked byRightService.hasGroup.Requirements
Requirement 1: Add isSensitiveLocked column to entrance and massif tables
User Story: As a database administrator, I want
is_sensitive_lockedcolumns ont_entranceandt_massif, so that the lock state is persisted and queryable.Acceptance Criteria
t_entrancetable SHALL have a columnis_sensitive_lockedof typeboolean,NOT NULL, with a default value offalse.t_massiftable SHALL have a columnis_sensitive_lockedof typeboolean,NOT NULL, with a default value offalse.IF NOT EXISTSor equivalent safe patterns).isSensitiveLockedattribute mapped to theis_sensitive_lockedcolumn withtype: 'boolean',allowNull: false,defaultsTo: false.isSensitiveLockedattribute mapped to theis_sensitive_lockedcolumn withtype: 'boolean',allowNull: false,defaultsTo: false.Requirement 2: Return isSensitiveLocked in API responses
User Story: As a frontend developer, I want the API to include
isSensitiveLockedin entrance and massif responses, so that the UI can render lock state indicators and enforce client-side logic.Acceptance Criteria
toEntranceconverter SHALL includeisSensitiveLockedin its output, sourced fromsource.isSensitiveLockedorsource.is_sensitive_locked.toMassifconverter SHALL includeisSensitiveLockedin its output, sourced fromsource.isSensitiveLocked.isSensitiveLockedisnullorundefinedon the source object (e.g., old data before migration), THE converters SHALL default the output value tofalse.GET /api/v1/entrances/:idresponse SHALL include theisSensitiveLockedfield.GET /api/v1/massifs/:idresponse (viatoMassif) SHALL include theisSensitiveLockedfield.Requirement 3: Entrance update respects lock state
User Story: As a caver, I want the API to reject my attempt to change
isSensitiveon a locked entrance, so that administrator-locked sensitivity cannot be overridden.Acceptance Criteria
isSensitiveLockedset totrueand the request sender is not an Administrator, THEN THEPUT /api/v1/entrances/:idendpoint SHALL respond with403 Forbiddenwhen the request body contains anisSensitivevalue different from the current persisted value.isSensitiveLockedset totrueand the request sender is an Administrator, THEN THEPUT /api/v1/entrances/:idendpoint SHALL allow changes toisSensitive.isSensitiveLockedset tofalseorundefined, THEN THE existing sensitivity permission logic SHALL apply unchanged (non-admins can mark as sensitive but cannot unmark).PUT /api/v1/entrances/:idendpoint SHALL allow administrators to setisSensitiveLockedtotrueorfalse.PUT /api/v1/entrances/:idendpoint SHALL reject (ignore or 403) attempts by non-administrators to changeisSensitiveLocked.Requirement 4: Entrance creation respects isSensitiveLocked
User Story: As a developer, I want the entrance creation flow to accept
isSensitiveLockedfrom administrators, so that administrators can lock sensitivity at creation time.Acceptance Criteria
isSensitiveLocked: truein aPOST /api/v1/entrancesrequest, THE created entrance SHALL haveisSensitiveLockedset totrue.isSensitiveLocked: truein aPOST /api/v1/entrancesrequest, THE API SHALL ignore the field and create the entrance withisSensitiveLockedset tofalse.isSensitiveLockedis omitted from the request body, THE created entrance SHALL default toisSensitiveLocked: false.Requirement 5: Massif sensitivity lock enforcement
User Story: As an administrator, I want the massif sensitivity endpoints to respect the lock state, so that the sensitivity of a locked massif cannot be changed until it is unlocked.
Acceptance Criteria
isSensitiveLockedset totrue, THEN THEPOST /api/v1/massifs/:id/mark-sensitiveendpoint SHALL respond with403 Forbiddenwith a message indicating the massif sensitivity is locked.isSensitiveLockedset totrue, THEN THEPOST /api/v1/massifs/:id/unmark-sensitiveendpoint SHALL respond with403 Forbiddenwith a message indicating the massif sensitivity is locked.PUT /api/v1/massifs/:idendpoint SHALL allow administrators to setisSensitiveLockedtotrueorfalse.PUT /api/v1/massifs/:idendpoint SHALL stripisSensitiveLockedfrom the update payload if the request sender is not an Administrator (same pattern asisSensitivebeing stripped).Requirement 6: Massif cascade skips locked entrances
User Story: As an administrator performing a massif cascade, I want the cascade to skip entrances whose sensitivity is locked, so that manually locked entrances remain unchanged.
Acceptance Criteria
MassifService.propagateSensitivityToEntrancesruns, THE query SHALL exclude entrances whereis_sensitive_locked = truefrom the set of entrances to update.POST /api/v1/massifs/:id/mark-sensitiveresponse SHALL include askippedLockedCountfield indicating how many entrances were skipped due to their sensitivity being locked.POST /api/v1/massifs/:id/mark-sensitiveresponse SHALL continue to include thecountfield indicating how many entrances were actually updated.Requirement 7: Preview endpoint reports locked entrance count
User Story: As an administrator previewing a cascade, I want to know how many entrances are locked and will be skipped, so that I can make an informed decision before confirming.
Acceptance Criteria
GET /api/v1/massifs/:id/preview-sensitiveresponse SHALL include alockedCountfield indicating how many entrances within the massif haveisSensitiveLocked = trueandisSensitive = false(i.e., locked in non-sensitive state and thus would be skipped).GET /api/v1/massifs/:id/preview-sensitiveresponse SHALL continue to include thecountfield indicating the number of non-sensitive, non-locked entrances that would be affected.countfield SHALL be0andlockedCountSHALL reflect the total locked non-sensitive entrance count.Requirement 8: Massif update endpoint handles isSensitiveLocked
User Story: As an administrator, I want to set or unset the sensitivity lock on a massif via the update endpoint, so that I can control whether the massif's sensitivity can be changed.
Acceptance Criteria
isSensitiveLockedin thePUT /api/v1/massifs/:idrequest body, THE endpoint SHALL persist the value to theis_sensitive_lockedcolumn.isSensitiveLockedin thePUT /api/v1/massifs/:idrequest body, THE endpoint SHALL strip the field (not persist it).MassifService.getConvertedDataFromClientRequestSHALL extractisSensitiveLockedfrom the request using the samecoerceBoolpattern used forisSensitive.Requirement 9: Update Swagger/OpenAPI specification
User Story: As a frontend developer or API consumer, I want the OpenAPI spec to document the
isSensitiveLockedfield, so that I can understand the API contract without reading the source.Acceptance Criteria
assets/swaggerV1.yamlSHALL documentisSensitiveLockedas a boolean field in the entrance request and response schemas.assets/swaggerV1.yamlSHALL documentisSensitiveLockedas a boolean field in the massif request and response schemas.assets/swaggerV1.yamlSHALL document theskippedLockedCountfield in themark-sensitiveresponse schema.assets/swaggerV1.yamlSHALL document thelockedCountfield in thepreview-sensitiveresponse schema.assets/swaggerV1.yamlSHALL document the403response formark-sensitiveandunmark-sensitivewhen the massif sensitivity is locked.