Manhour validator
Requirements
Environment
- Private Deployment
ONES Version
v6.3.0+
Dependency Package Versions
{
"dependencies": {
"@ones-op/fetch": "0.46.12+",
"@ones-open/node-host": "0.4.2+",
"@ones/cli-plugin": "1.29.0+",
"@ones/cli-plugin-template": "1.10.8+",
"@ones-op/sdk": "0.46.7+"
}
}
Additionally, you need to install @ones-open/node-host in the plugin backend:
cd backend/
npm install @ones-open/node-host@0.4.3+
Capability Overview
This document provides the manhour validation capability, allowing customers to intercept manhour operations. This is an organization-level capability. In some scenarios, the same data may receive multiple interception requests, so when using this capability, you need to maintain idempotency. Maintaining idempotency means that even if the same interception request is triggered multiple times, its effect should be as if it was triggered only once, without causing duplicate data processing or abnormal situations.
Manhour Validator and Plugins
How to use the manhour validator in the plugin system?
Add the manhour validator configuration in the plugin's plugin.yaml as follows:
extension:
- manhourValidator: # Use manhour validator extension
provider: manhour # Must be 8 bytes, cannot be modified later. Use letters and numbers for naming, avoid special characters.
funcs:
- name: validate # Must be registered, name cannot be changed
url: validate # Must match the func name in the plugin
Manhour Validator and Plugin Lifecycle
The manhour validator capability is bound to plugin lifecycle operations as shown in the table below:
Lifecycle Operation | Private Deployment | SaaS |
---|---|---|
Installation | Organization Level: Register | Instance Level: Register Organization Level: Register |
Uninstallation | Organization Level: Unregister | Instance Level: Unregister Organization Level: Unregister |
Manhour Validator Implementation
Backend Implementation
- Create file
backend/src/manhour-validator.ts
:
import { FetchAsAdmin } from '@ones-op/fetch'
import { Logger } from '@ones-op/node-logger'
import type { PluginRequest, PluginResponse } from '@ones-op/node-types'
export async function validate(body: any): Promise<PluginResponse> {
Logger.info('body', body)
const haveError = true
if (haveError) {
return {
statusCode: 200,
body: {
error: {
reason: "This is an error message", // Error message
level: "error" // Error level
},
},
}
}
return {
statusCode: 200
}
}
Request Body Description
After registering the capability and implementing the corresponding method, the plugin will receive the following information:
Field | Type | Meaning | Notes |
---|---|---|---|
teamUUID | string | Team UUID | |
userUUID | string | Requesting user UUID | |
req | ManhourRequest | Manhour-related information in the request |
ManhourRequest Structure
type ManhourRequest struct {
Type string `json:"type"` // Classification: estimated, recorded, remaining hours
Action string `json:"action"` // add, update, delete
Mode string `json:"mode"`
Options manhourModel.Options `json:"options"`
ManhourInfo []*ManhourInfo `json:"manhour_info"` // Detailed manhour information
}
Field | Type | Meaning | Notes |
---|---|---|---|
type | string | Manhour classification | "recorded" for recorded hours "estimated" for estimated hours "remaining" for remaining hours Based on the current operation's manhour type in the frontend |
action | string | Manhour operation behavior | add, update, delete This information may not be completely accurate in some scenarios for add and update operations due to current business architecture limitations. Plugins should not rely solely on this field to distinguish between add and update operations |
mode | string | Manhour mode | "simple" for simple mode "detailed" for detailed mode |
options | Options | Operation scenario | "import_task" for task import, empty for other scenarios |
manhour_info | []*ManhourInfo | Array type, detailed fields see table below |
ManhourInfo Structure
type ManhourInfo struct {
task *Task // Specific task information
assess_manhour *AssessManhour // Specific estimated manhour information
remaining_manhour *RemainingHour // Specific remaining manhour information
record_manhour *RecordManhour // Specific recorded manhour information
}
Field | Type | Meaning | Notes |
---|---|---|---|
task | *Task | Specific task information | See Task structure description |
assess_manhour | *AssessManhour | Specific estimated manhour information | See AssessManhour structure description |
remaining_manhour | *RemainingHour | Specific remaining manhour information | See RemainingHour structure description |
record_manhour | *RecordManhour | Specific recorded manhour information | See RecordManhour structure description |
Task Structure
Field | Type | Meaning | Notes |
---|---|---|---|
UUID | string | Task UUID | |
TeamUUID | string | Team UUID | |
Owner | string | Task owner | |
Assign | string | Task assignee | |
ProjectUUID | string | Project UUID | |
IssueTypeUUID | string | Issue type UUID | |
ParentUUID | string | Parent task UUID | |
SubIssueTypeUUID | string | Sub-issue type UUID | |
IsCreateTask | bool | Whether it's a new task |
AssessManhour Structure
Field | Type | Meaning | Notes |
---|---|---|---|
UUID | string | Estimated manhour UUID | |
From | int64 | Start time | |
To | int64 | End time | |
HoursFormat | string | Manhour format | |
IncludeNonWorkingDay | int64 | Whether to include non-working days | |
Hours | float64 | Manhour amount | |
Owner | string | Manhour owner | |
StartTime | float64 | Start time | Filled when modifying estimated manhours in detailed mode |
RemainingHour Structure
Field | Type | Meaning | Notes |
---|---|---|---|
UUID | string | Remaining manhour UUID | |
Hours | float64 | Remaining manhour amount | |
Owner | string | Manhour owner |
RecordManhour Structure
Field | Type | Meaning | Notes |
---|---|---|---|
UUID | string | Recorded manhour UUID | |
StartTime | float64 | Start time | |
Description | string | Manhour description | |
Hours | float64 | Manhour amount | |
Owner | string | Manhour owner |
Options Structure
type Options struct {
// Use scene to determine if permission validation is needed
Scene string `json:"scene"`
}
Best Practices
- Maintain idempotency: Even if the same interception request is triggered multiple times, its effect should be as if it was triggered only once, without causing duplicate data processing or abnormal situations.
- Error handling: When the method's error is not empty, the frontend manhour operation will fail. Errors should only be returned when it's truly necessary to interrupt the operation.
- Error code structure:
// Service internal exceptions can be returned directly.
error: {
reason: "This is an error message", // Error message
level: "error" // Error level
}