109 lines
4.5 KiB
Markdown
109 lines
4.5 KiB
Markdown
|
|
# service-project-members
|
||
|
|
|
||
|
|
## Purpose
|
||
|
|
|
||
|
|
Service-to-service API для upsert участника проекта Merakomis без impersonation сотрудника (только API-ключ).
|
||
|
|
|
||
|
|
## Requirements
|
||
|
|
|
||
|
|
### Requirement: Service upsert project team member
|
||
|
|
|
||
|
|
The system SHALL expose `PUT /api/service/project-members` that creates or updates a row in `tMerakomisTeamMember` for the given `project_id` and `emp_id`.
|
||
|
|
|
||
|
|
The endpoint MUST resolve `team` from `tMerakomisProject.team` for `project_id`.
|
||
|
|
|
||
|
|
If a row with the same `(team, emp)` already exists, the system MUST UPDATE it; otherwise MUST INSERT.
|
||
|
|
|
||
|
|
The request MUST require `X-Api-Key` (or `Authorization: Bearer`) when the server API key is configured.
|
||
|
|
|
||
|
|
The request MUST NOT require `X-Acting-Emp-Id`.
|
||
|
|
|
||
|
|
The system MUST NOT apply portal `Rules::isRwMember` / `can_write_project_member` checks for this endpoint.
|
||
|
|
|
||
|
|
Required JSON fields: `project_id`, `emp_id`, `section_id`, `role`.
|
||
|
|
|
||
|
|
Optional JSON fields: `active` (default `true`), `text` (default empty string).
|
||
|
|
|
||
|
|
The response MUST include `ok: true` and a member object with the same fields as one item of `GET /api/project-members` (`member_id`, `emp_id`, `emp_name`, `project_id`, `project_code`, `project_name`, `step_name`, `status`, `status_name`, `archive`, `archive_date`, `section_id`, `section_name`, `role`, `active`).
|
||
|
|
|
||
|
|
#### Scenario: Add new member with API key only
|
||
|
|
|
||
|
|
- **WHEN** a valid API key is provided
|
||
|
|
- **AND** `X-Acting-Emp-Id` is omitted
|
||
|
|
- **AND** no `tMerakomisTeamMember` row exists for `(team, emp)`
|
||
|
|
- **AND** `section_id` is allowed for `project_id`
|
||
|
|
- **AND** `emp_id` refers to a non-archived, non-removed employee
|
||
|
|
- **AND** `role` is a valid member role
|
||
|
|
- **THEN** system inserts a new TeamMember row
|
||
|
|
- **AND** returns `200` with `ok: true` and the new member object including `member_id`
|
||
|
|
|
||
|
|
#### Scenario: Update existing member with API key only
|
||
|
|
|
||
|
|
- **WHEN** a TeamMember row already exists for `(team, emp)`
|
||
|
|
- **AND** a valid API key is provided without acting header
|
||
|
|
- **THEN** system updates `section`, `role`, `active`, and `text`
|
||
|
|
- **AND** returns `200` with updated member object (same `member_id`)
|
||
|
|
|
||
|
|
#### Scenario: Deactivate member via service endpoint
|
||
|
|
|
||
|
|
- **WHEN** client sends `PUT /api/service/project-members` with `active: false`
|
||
|
|
- **AND** a valid API key is provided
|
||
|
|
- **THEN** system sets `active` to 0
|
||
|
|
- **AND** member no longer appears in `GET /api/project-members?active_only=true`
|
||
|
|
|
||
|
|
#### Scenario: Missing API key rejected
|
||
|
|
|
||
|
|
- **WHEN** server has `USER_READER_API_KEY` configured
|
||
|
|
- **AND** client calls `PUT /api/service/project-members` without a valid key
|
||
|
|
- **THEN** system returns `401 Unauthorized`
|
||
|
|
|
||
|
|
### Requirement: Service endpoint reuses portal write validation
|
||
|
|
|
||
|
|
The system MUST apply the same business validation as `PUT /api/project-members` for project existence, employee existence and eligibility, section membership in the project, role validity, and presence of a project team.
|
||
|
|
|
||
|
|
#### Scenario: Invalid section for project
|
||
|
|
|
||
|
|
- **WHEN** `section_id` is not in `tMerakomisProjectSection` for `project_id`
|
||
|
|
- **THEN** system returns `400` with a structured error indicating invalid section
|
||
|
|
|
||
|
|
#### Scenario: Project has no configured sections
|
||
|
|
|
||
|
|
- **WHEN** `tMerakomisProjectSection` has no rows for `project_id`
|
||
|
|
- **AND** client attempts to add a member via the service endpoint
|
||
|
|
- **THEN** system returns `400` indicating sections must be configured on the project first
|
||
|
|
|
||
|
|
#### Scenario: Archived employee rejected
|
||
|
|
|
||
|
|
- **WHEN** `emp_id` refers to an employee with `archive=1` or `removed=1`
|
||
|
|
- **THEN** system returns `400` with validation error for employee
|
||
|
|
|
||
|
|
#### Scenario: Unknown employee
|
||
|
|
|
||
|
|
- **WHEN** `emp_id` does not exist in the employee table
|
||
|
|
- **THEN** system returns `404`
|
||
|
|
|
||
|
|
#### Scenario: Unknown project
|
||
|
|
|
||
|
|
- **WHEN** `project_id` does not exist
|
||
|
|
- **THEN** system returns `404`
|
||
|
|
|
||
|
|
#### Scenario: Invalid role
|
||
|
|
|
||
|
|
- **WHEN** `role` is 0, omitted, or not a valid member role
|
||
|
|
- **THEN** system returns `400` with validation error for role
|
||
|
|
|
||
|
|
### Requirement: Portal project-members write remains unchanged
|
||
|
|
|
||
|
|
The system MUST continue to require `X-Acting-Emp-Id` and `isRwMember` authorization for `PUT /api/project-members`.
|
||
|
|
|
||
|
|
#### Scenario: Portal PUT still requires acting
|
||
|
|
|
||
|
|
- **WHEN** client calls `PUT /api/project-members` without `X-Acting-Emp-Id`
|
||
|
|
- **THEN** system returns an error indicating missing acting identity (same behavior as before this change)
|
||
|
|
|
||
|
|
#### Scenario: Portal PUT still enforces permissions
|
||
|
|
|
||
|
|
- **WHEN** acting employee does not meet `isRwMember` conditions
|
||
|
|
- **AND** client calls `PUT /api/project-members`
|
||
|
|
- **THEN** system returns `403` with structured error `forbidden`
|