Merge pull request #135

Implement department gates and relays objects, CRUD routes and OpenAPI docs
This commit is contained in:
Jeppe B
2026-03-03 12:06:13 +01:00
committed by GitHub
4 changed files with 677 additions and 4 deletions
+290
View File
@@ -2767,6 +2767,174 @@ paths:
'404':
$ref: '#/components/responses/NotFound'
/department/gates:
get:
tags:
- Departments
summary: List department gates
description: Retrieve department gates, optionally filtered by id
operationId: listDepartmentGates
parameters:
- name: id
in: query
required: false
schema:
type: integer
minimum: 1
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
- $ref: '#/components/parameters/SearchParam'
responses:
'200':
description: Department gates retrieved successfully
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/DepartmentGate'
- type: array
items:
$ref: '#/components/schemas/DepartmentGate'
'401':
$ref: '#/components/responses/Unauthorized'
post:
tags:
- Departments
summary: Create department gate
operationId: createDepartmentGate
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentGateCreate'
responses:
'201':
description: Department gate created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentGate'
put:
tags:
- Departments
summary: Update department gate
operationId: updateDepartmentGate
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentGateUpdate'
responses:
'200':
description: Department gate updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentGate'
'404':
$ref: '#/components/responses/NotFound'
delete:
tags:
- Departments
summary: Delete department gate
operationId: deleteDepartmentGate
parameters:
- name: id
in: query
required: true
schema:
type: integer
minimum: 1
responses:
'200':
description: Department gate deleted
/department/relays:
get:
tags:
- Departments
summary: List department relays
description: Retrieve department relays, optionally filtered by id
operationId: listDepartmentRelays
parameters:
- name: id
in: query
required: false
schema:
type: integer
minimum: 1
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PerPageParam'
- $ref: '#/components/parameters/SearchParam'
responses:
'200':
description: Department relays retrieved successfully
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/DepartmentRelay'
- type: array
items:
$ref: '#/components/schemas/DepartmentRelay'
'401':
$ref: '#/components/responses/Unauthorized'
post:
tags:
- Departments
summary: Create department relay
operationId: createDepartmentRelay
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentRelayCreate'
responses:
'201':
description: Department relay created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentRelay'
put:
tags:
- Departments
summary: Update department relay
operationId: updateDepartmentRelay
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentRelayUpdate'
responses:
'200':
description: Department relay updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/DepartmentRelay'
'404':
$ref: '#/components/responses/NotFound'
delete:
tags:
- Departments
summary: Delete department relay
operationId: deleteDepartmentRelay
parameters:
- name: id
in: query
required: true
schema:
type: integer
minimum: 1
responses:
'200':
description: Department relay deleted
/department/lanes/dynamic-image:
get:
tags:
@@ -8876,6 +9044,128 @@ components:
nullable: true
minimum: 1
DepartmentGate:
type: object
properties:
id:
type: integer
department:
type: integer
is_entrance:
type: boolean
is_exit:
type: boolean
name:
type: string
config:
type: object
additionalProperties: true
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
DepartmentGateCreate:
type: object
required:
- department
- is_entrance
- is_exit
- name
- config
properties:
department:
type: integer
is_entrance:
type: boolean
is_exit:
type: boolean
name:
type: string
config:
type: object
additionalProperties: true
DepartmentGateUpdate:
type: object
required:
- id
properties:
id:
type: integer
is_entrance:
type: boolean
is_exit:
type: boolean
name:
type: string
config:
type: object
additionalProperties: true
DepartmentRelay:
type: object
properties:
id:
type: integer
department:
type: integer
relay_id:
type: string
name:
type: string
type:
type: string
config:
type: object
additionalProperties: true
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
DepartmentRelayCreate:
type: object
required:
- department
- relay_id
- name
- type
- config
properties:
department:
type: integer
relay_id:
type: string
name:
type: string
type:
type: string
config:
type: object
additionalProperties: true
DepartmentRelayUpdate:
type: object
required:
- id
properties:
id:
type: integer
relay_id:
type: string
name:
type: string
type:
type: string
config:
type: object
additionalProperties: true
Product:
type: object
properties:
@@ -39,12 +39,16 @@ class department_gates_o extends db
*/
public function add(departments_o $department, bool $is_entrance, bool $is_exit, string $name, array $config): department_gates_o
{
global /** @var db $db */ $db;
// Sanitize the input
$department->requireSelected();
$name = trim($name);
$name = trim($db->escape_string($name));
if (empty($name)) {
throw new Exception('Gate name cannot be empty');
}
if (!$is_entrance && !$is_exit) {
throw new Exception('Gate must be either entrance, exit or both');
}
// Add the object
$tmp_id = self::add_object([
'department' => (int)$department->id,
@@ -97,4 +101,28 @@ class department_gates_o extends db
'updated_at' => (string)$this->updated_at->value(),
];
}
/**
* @throws Exception
* @return department_gates_o[]
*/
public function getDepartmentGates(int $department_id): array
{
$gates = [];
$department_gates = self::getFieldsWhere(
[
'department' => $department_id,
'deleted_at' => null,
],
[
'id',
],
);
foreach ($department_gates as $department_gate) {
$gates[] = (new department_gates_o())->select((int)$department_gate['id']);
}
return $gates;
}
}
@@ -39,17 +39,18 @@ class department_relays_o extends db
*/
public function add(departments_o $department, string $relay_id, string $name, string $type, array $config): department_relays_o
{
global /** @var db $db */ $db;
// Sanitize the input
$department->requireSelected();
$relay_id = trim($relay_id);
$relay_id = trim($db->escape_string($relay_id));
if (empty($relay_id)) {
throw new Exception('Relay ID cannot be empty');
}
$name = trim($name);
$name = trim($db->escape_string($name));
if (empty($name)) {
throw new Exception('Relay name cannot be empty');
}
$type = trim($type);
$type = trim($db->escape_string($type));
if (empty($type)) {
throw new Exception('Relay type cannot be empty');
}
@@ -104,4 +105,29 @@ class department_relays_o extends db
'updated_at' => (string)$this->updated_at->value(),
];
}
/**
* @throws Exception
* @return department_relays_o[]
*/
public function getDepartmentRelays(int $department_id): array
{
$relays = [];
$department_relays = self::getFieldsWhere(
[
'department' => $department_id,
'deleted_at' => null,
],
[
'id',
],
);
foreach ($department_relays as $department_relay) {
$relays[] = (new department_relays_o())->select((int)$department_relay['id']);
}
return $relays;
}
}
@@ -0,0 +1,329 @@
<?php
namespace routes;
use classes\authentication;
use classes\response;
use objects\department_gates_o;
use objects\department_relays_o;
use objects\departments_o;
use objects\logs_o;
use traits\route_t;
class departmentGatesRelaysRoute
{
use route_t;
public function run(): void
{
$this->get('/department/gates', function () {
global /** @var response $response */ $response;
$this->requirePermission('list_department_gates');
$user = (new authentication())->get_user();
if (!$user) {
$response->error('Invalid session', 400);
}
$gates_o = new department_gates_o();
if (self::isParametersSet(['id'])) {
self::requireType(self::getParameter('id'), self::type_int());
self::requireMinValue((int)self::getParameter('id'), 1);
$gate = (new department_gates_o())->select((int)self::getParameter('id'));
if (!$gate->exists()) {
$response->error('Department gate not found', 404);
}
self::requireDepartmentAccess((int)$gate->department->value());
$response->success($gate->asArray());
}
$result = $gates_o->listObjectsWithPaginationIfSet(
function ($tmp_object_array) {
return (new department_gates_o())->select((int)$tmp_object_array['id'])->asArray();
},
$gates_o->forceRestrictFilters([
'department' => $user->getGroup()->getDepartments(),
])
);
(new logs_o())->add('department_gates', 'global', 1, $user->id, 'LIST_DEPARTMENT_GATES', 'User listed department gates');
$response->success($result);
}, [
'list_department_gates' => 'List department gates in departments the user has access to',
]);
$this->post('/department/gates', function () {
global /** @var response $response */ $response;
$this->requirePermission('add_department_gate');
$user = (new authentication())->get_user();
if (!$user) {
$response->error('Invalid session', 400);
}
self::requireParameters(['department', 'is_entrance', 'is_exit', 'name', 'config']);
self::requireType(self::getParameter('department'), self::type_int());
self::requireMinValue((int)self::getParameter('department'), 1);
self::requireType(self::getParameter('is_entrance'), self::type_bool());
self::requireType(self::getParameter('is_exit'), self::type_bool());
self::requireType(self::getParameter('name'), self::type_string());
self::requireTypeIn(self::getParameter('config'), ['array', 'object']);
self::requireDepartmentAccess((int)self::getParameter('department'));
$department = (new departments_o())->select((int)self::getParameter('department'));
if (!$department->exists()) {
$response->error('Department not found', 404);
}
$config = self::getParameter('config');
$config = is_array($config) ? $config : (array)$config;
$gate = (new department_gates_o())->add(
$department,
(bool)self::getParameter('is_entrance'),
(bool)self::getParameter('is_exit'),
(string)self::getParameter('name'),
$config,
);
(new logs_o())->add('department_gates', 'global', 1, $user->id, 'ADD_DEPARTMENT_GATE', 'User added department gate');
$response->success($gate->asArray(), 201);
}, [
'add_department_gate' => 'Add department gate in departments the user has access to',
]);
$this->put('/department/gates', function () {
global /** @var response $response */ $response;
$this->requirePermission('update_department_gate');
$user = (new authentication())->get_user();
if (!$user) {
$response->error('Invalid session', 400);
}
self::requireParameters(['id']);
self::requireType(self::getParameter('id'), self::type_int());
self::requireMinValue((int)self::getParameter('id'), 1);
$gate = (new department_gates_o())->select((int)self::getParameter('id'));
if (!$gate->exists()) {
$response->error('Department gate not found', 404);
}
self::requireDepartmentAccess((int)$gate->department->value());
$data = [];
if (self::isParametersSet(['is_entrance'])) {
self::requireType(self::getParameter('is_entrance'), self::type_bool());
$data['is_entrance'] = (bool)self::getParameter('is_entrance');
}
if (self::isParametersSet(['is_exit'])) {
self::requireType(self::getParameter('is_exit'), self::type_bool());
$data['is_exit'] = (bool)self::getParameter('is_exit');
}
if (self::isParametersSet(['name'])) {
self::requireType(self::getParameter('name'), self::type_string());
$data['name'] = (string)self::getParameter('name');
}
if (self::isParametersSet(['config'])) {
self::requireTypeIn(self::getParameter('config'), ['array', 'object']);
$config = self::getParameter('config');
$data['config'] = is_array($config) ? $config : (array)$config;
}
$nextIsEntrance = $data['is_entrance'] ?? (bool)$gate->is_entrance->value();
$nextIsExit = $data['is_exit'] ?? (bool)$gate->is_exit->value();
if (!$nextIsEntrance && !$nextIsExit) {
$response->error('Gate must be either entrance, exit or both', 400);
}
if (empty($data)) {
$response->error('No data to update', 400);
}
$gate->update($data);
(new logs_o())->add('department_gates', 'global', 1, $user->id, 'UPDATE_DEPARTMENT_GATE', 'User updated department gate');
$response->success($gate->asArray());
}, [
'update_department_gate' => 'Update department gate in departments the user has access to',
]);
$this->delete('/department/gates', function () {
global /** @var response $response */ $response;
$this->requirePermission('delete_department_gate');
$user = (new authentication())->get_user();
if (!$user) {
$response->error('Invalid session', 400);
}
self::requireParameters(['id']);
self::requireType(self::getParameter('id'), self::type_int());
self::requireMinValue((int)self::getParameter('id'), 1);
$gate = (new department_gates_o())->select((int)self::getParameter('id'));
if (!$gate->exists()) {
$response->error('Department gate not found', 404);
}
self::requireDepartmentAccess((int)$gate->department->value());
$gate->delete();
(new logs_o())->add('department_gates', 'global', 1, $user->id, 'DELETE_DEPARTMENT_GATE', 'User deleted department gate');
$response->success('Department gate deleted');
}, [
'delete_department_gate' => 'Delete department gate in departments the user has access to',
]);
$this->get('/department/relays', function () {
global /** @var response $response */ $response;
$this->requirePermission('list_department_relays');
$user = (new authentication())->get_user();
if (!$user) {
$response->error('Invalid session', 400);
}
$relays_o = new department_relays_o();
if (self::isParametersSet(['id'])) {
self::requireType(self::getParameter('id'), self::type_int());
self::requireMinValue((int)self::getParameter('id'), 1);
$relay = (new department_relays_o())->select((int)self::getParameter('id'));
if (!$relay->exists()) {
$response->error('Department relay not found', 404);
}
self::requireDepartmentAccess((int)$relay->department->value());
$response->success($relay->asArray());
}
$result = $relays_o->listObjectsWithPaginationIfSet(
function ($tmp_object_array) {
return (new department_relays_o())->select((int)$tmp_object_array['id'])->asArray();
},
$relays_o->forceRestrictFilters([
'department' => $user->getGroup()->getDepartments(),
])
);
(new logs_o())->add('department_relays', 'global', 1, $user->id, 'LIST_DEPARTMENT_RELAYS', 'User listed department relays');
$response->success($result);
}, [
'list_department_relays' => 'List department relays in departments the user has access to',
]);
$this->post('/department/relays', function () {
global /** @var response $response */ $response;
$this->requirePermission('add_department_relay');
$user = (new authentication())->get_user();
if (!$user) {
$response->error('Invalid session', 400);
}
self::requireParameters(['department', 'relay_id', 'name', 'type', 'config']);
self::requireType(self::getParameter('department'), self::type_int());
self::requireMinValue((int)self::getParameter('department'), 1);
self::requireType(self::getParameter('relay_id'), self::type_string());
self::requireType(self::getParameter('name'), self::type_string());
self::requireType(self::getParameter('type'), self::type_string());
self::requireTypeIn(self::getParameter('config'), ['array', 'object']);
self::requireDepartmentAccess((int)self::getParameter('department'));
$department = (new departments_o())->select((int)self::getParameter('department'));
if (!$department->exists()) {
$response->error('Department not found', 404);
}
$config = self::getParameter('config');
$config = is_array($config) ? $config : (array)$config;
$relay = (new department_relays_o())->add(
$department,
(string)self::getParameter('relay_id'),
(string)self::getParameter('name'),
(string)self::getParameter('type'),
$config,
);
(new logs_o())->add('department_relays', 'global', 1, $user->id, 'ADD_DEPARTMENT_RELAY', 'User added department relay');
$response->success($relay->asArray(), 201);
}, [
'add_department_relay' => 'Add department relay in departments the user has access to',
]);
$this->put('/department/relays', function () {
global /** @var response $response */ $response;
$this->requirePermission('update_department_relay');
$user = (new authentication())->get_user();
if (!$user) {
$response->error('Invalid session', 400);
}
self::requireParameters(['id']);
self::requireType(self::getParameter('id'), self::type_int());
self::requireMinValue((int)self::getParameter('id'), 1);
$relay = (new department_relays_o())->select((int)self::getParameter('id'));
if (!$relay->exists()) {
$response->error('Department relay not found', 404);
}
self::requireDepartmentAccess((int)$relay->department->value());
$data = [];
if (self::isParametersSet(['relay_id'])) {
self::requireType(self::getParameter('relay_id'), self::type_string());
$data['relay_id'] = (string)self::getParameter('relay_id');
}
if (self::isParametersSet(['name'])) {
self::requireType(self::getParameter('name'), self::type_string());
$data['name'] = (string)self::getParameter('name');
}
if (self::isParametersSet(['type'])) {
self::requireType(self::getParameter('type'), self::type_string());
$data['type'] = (string)self::getParameter('type');
}
if (self::isParametersSet(['config'])) {
self::requireTypeIn(self::getParameter('config'), ['array', 'object']);
$config = self::getParameter('config');
$data['config'] = is_array($config) ? $config : (array)$config;
}
if (empty($data)) {
$response->error('No data to update', 400);
}
$relay->update($data);
(new logs_o())->add('department_relays', 'global', 1, $user->id, 'UPDATE_DEPARTMENT_RELAY', 'User updated department relay');
$response->success($relay->asArray());
}, [
'update_department_relay' => 'Update department relay in departments the user has access to',
]);
$this->delete('/department/relays', function () {
global /** @var response $response */ $response;
$this->requirePermission('delete_department_relay');
$user = (new authentication())->get_user();
if (!$user) {
$response->error('Invalid session', 400);
}
self::requireParameters(['id']);
self::requireType(self::getParameter('id'), self::type_int());
self::requireMinValue((int)self::getParameter('id'), 1);
$relay = (new department_relays_o())->select((int)self::getParameter('id'));
if (!$relay->exists()) {
$response->error('Department relay not found', 404);
}
self::requireDepartmentAccess((int)$relay->department->value());
$relay->delete();
(new logs_o())->add('department_relays', 'global', 1, $user->id, 'DELETE_DEPARTMENT_RELAY', 'User deleted department relay');
$response->success('Department relay deleted');
}, [
'delete_department_relay' => 'Delete department relay in departments the user has access to',
]);
}
}