Add Self-Serve section to OpenAPI spec and implement related CRUD routes with department-level access control
This commit is contained in:
+202
@@ -68,6 +68,8 @@ tags:
|
||||
description: Branding options management
|
||||
- name: Roles
|
||||
description: Role and permission management
|
||||
- name: Self-Serve
|
||||
description: Self-serve lane operations and questions
|
||||
|
||||
paths:
|
||||
# Authentication Endpoints
|
||||
@@ -1262,6 +1264,171 @@ paths:
|
||||
items:
|
||||
$ref: '#/components/schemas/Department'
|
||||
|
||||
/department/selfserve/questions:
|
||||
get:
|
||||
tags:
|
||||
- Self-Serve
|
||||
summary: List self-serve questions
|
||||
description: Retrieve a list of self-serve questions for a department, lane, or product.
|
||||
operationId: listSelfserveQuestions
|
||||
parameters:
|
||||
- name: id
|
||||
in: query
|
||||
description: Filter by question ID
|
||||
schema:
|
||||
type: integer
|
||||
- name: department
|
||||
in: query
|
||||
description: Filter by department ID
|
||||
schema:
|
||||
type: integer
|
||||
- name: lane
|
||||
in: query
|
||||
description: Filter by lane ID
|
||||
schema:
|
||||
type: integer
|
||||
- name: product
|
||||
in: query
|
||||
description: Filter by product ID
|
||||
schema:
|
||||
type: integer
|
||||
- $ref: '#/components/parameters/PageParam'
|
||||
- $ref: '#/components/parameters/PerPageParam'
|
||||
- $ref: '#/components/parameters/SearchParam'
|
||||
- $ref: '#/components/parameters/FiltersParam'
|
||||
responses:
|
||||
'200':
|
||||
description: Successfully retrieved questions
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/DepartmentSelfserveQuestion'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
post:
|
||||
tags:
|
||||
- Self-Serve
|
||||
summary: Add self-serve question
|
||||
description: Add a new self-serve question.
|
||||
operationId: addSelfserveQuestion
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- department
|
||||
- lane
|
||||
- product
|
||||
- question
|
||||
- description
|
||||
properties:
|
||||
department:
|
||||
type: integer
|
||||
lane:
|
||||
type: integer
|
||||
product:
|
||||
type: integer
|
||||
question:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
image:
|
||||
type: integer
|
||||
nullable: true
|
||||
order_priority:
|
||||
type: integer
|
||||
default: 0
|
||||
responses:
|
||||
'200':
|
||||
description: Successfully added question
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DepartmentSelfserveQuestion'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
|
||||
put:
|
||||
tags:
|
||||
- Self-Serve
|
||||
summary: Update self-serve question
|
||||
description: Update an existing self-serve question.
|
||||
operationId: updateSelfserveQuestion
|
||||
parameters:
|
||||
- name: id
|
||||
in: query
|
||||
required: true
|
||||
description: Question ID
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
department:
|
||||
type: integer
|
||||
lane:
|
||||
type: integer
|
||||
product:
|
||||
type: integer
|
||||
question:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
image:
|
||||
type: integer
|
||||
nullable: true
|
||||
order_priority:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: Successfully updated question
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DepartmentSelfserveQuestion'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
delete:
|
||||
tags:
|
||||
- Self-Serve
|
||||
summary: Delete self-serve question
|
||||
description: Delete a self-serve question by ID.
|
||||
operationId: deleteSelfserveQuestion
|
||||
parameters:
|
||||
- name: id
|
||||
in: query
|
||||
required: true
|
||||
description: Question ID
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: Successfully deleted question
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: string
|
||||
example: Question deleted
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
# Products Endpoints
|
||||
/products:
|
||||
get:
|
||||
@@ -4178,6 +4345,41 @@ components:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
DepartmentSelfserveQuestion:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description: Question ID
|
||||
department:
|
||||
type: integer
|
||||
description: Department ID
|
||||
lane:
|
||||
type: integer
|
||||
description: Lane ID
|
||||
product:
|
||||
type: integer
|
||||
description: Product ID
|
||||
image:
|
||||
type: integer
|
||||
description: Image ID
|
||||
nullable: true
|
||||
question:
|
||||
type: string
|
||||
description: Question text
|
||||
description:
|
||||
type: string
|
||||
description: Question description
|
||||
order_priority:
|
||||
type: integer
|
||||
description: Display order priority (lower numbers shown first)
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
OrderCreate:
|
||||
type: object
|
||||
required:
|
||||
|
||||
@@ -36,25 +36,39 @@ class departmentSelfserveQuestionsRoute
|
||||
if ($questions_o->exists()) {
|
||||
if (!in_array((int)$questions_o->department->value(), $authorized_department_ids)) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
return;
|
||||
}
|
||||
$response->success($questions_o->asArray());
|
||||
return;
|
||||
} else {
|
||||
$response->error('Question not found', 404);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$filters = [];
|
||||
if (self::isParametersSet(['department'])) {
|
||||
$requested_department = (int)self::getParameter('department');
|
||||
if (!in_array($requested_department, $authorized_department_ids)) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
}
|
||||
$filters['department'] = $requested_department;
|
||||
} else {
|
||||
$filters['department'] = $authorized_department_ids;
|
||||
}
|
||||
|
||||
if (self::isParametersSet(['lane'])) {
|
||||
$filters['lane'] = (int)self::getParameter('lane');
|
||||
}
|
||||
|
||||
if (self::isParametersSet(['product'])) {
|
||||
$filters['product'] = (int)self::getParameter('product');
|
||||
}
|
||||
|
||||
$response->success(
|
||||
$questions_o->setSearchableFields(['id', 'department', 'lane', 'product', 'question', 'description'])
|
||||
->listObjectsWithPaginationIfSet(function ($question) {
|
||||
$q = new department_selfserve_questions_o();
|
||||
$q->select((int)$question['id']);
|
||||
return $q->asArray();
|
||||
}, $questions_o->forceRestrictFilters([
|
||||
'department' => $authorized_department_ids
|
||||
]))
|
||||
}, $questions_o->forceRestrictFilters($filters))
|
||||
);
|
||||
} else {
|
||||
$response->error('Invalid session', 400);
|
||||
@@ -81,13 +95,11 @@ class departmentSelfserveQuestionsRoute
|
||||
|
||||
if (!$department || !$lane || !$product || !$question || !$description) {
|
||||
$response->error('Missing required fields', 400);
|
||||
return;
|
||||
}
|
||||
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!in_array($department, $authorized_department_ids)) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -127,20 +139,17 @@ class departmentSelfserveQuestionsRoute
|
||||
|
||||
if (!$question_o->exists()) {
|
||||
$response->error('Question not found', 404);
|
||||
return;
|
||||
}
|
||||
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!in_array((int)$question_o->department->value(), $authorized_department_ids)) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self::isParametersSet(['department'])) {
|
||||
$new_department = (int)self::getParameter('department');
|
||||
if (!in_array($new_department, $authorized_department_ids)) {
|
||||
$response->error('You do not have access to the new department', 403);
|
||||
return;
|
||||
}
|
||||
$question_o->department->set($new_department);
|
||||
}
|
||||
@@ -187,13 +196,11 @@ class departmentSelfserveQuestionsRoute
|
||||
|
||||
if (!$question_o->exists()) {
|
||||
$response->error('Question not found', 404);
|
||||
return;
|
||||
}
|
||||
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!in_array((int)$question_o->department->value(), $authorized_department_ids)) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
return;
|
||||
}
|
||||
|
||||
$question_o->delete();
|
||||
|
||||
Reference in New Issue
Block a user