Skip to main content

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 OperationPrivate DeploymentSaaS
InstallationOrganization Level: RegisterInstance Level: Register
Organization Level: Register
UninstallationOrganization Level: UnregisterInstance Level: Unregister
Organization Level: Unregister

Manhour Validator Implementation

Backend Implementation

  1. 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:

FieldTypeMeaningNotes
teamUUIDstringTeam UUID
userUUIDstringRequesting user UUID
reqManhourRequestManhour-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
}
FieldTypeMeaningNotes
typestringManhour classification"recorded" for recorded hours
"estimated" for estimated hours
"remaining" for remaining hours
Based on the current operation's manhour type in the frontend
actionstringManhour operation behavioradd, 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
modestringManhour mode"simple" for simple mode
"detailed" for detailed mode
optionsOptionsOperation scenario"import_task" for task import, empty for other scenarios
manhour_info[]*ManhourInfoArray 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
}
FieldTypeMeaningNotes
task*TaskSpecific task informationSee Task structure description
assess_manhour*AssessManhourSpecific estimated manhour informationSee AssessManhour structure description
remaining_manhour*RemainingHourSpecific remaining manhour informationSee RemainingHour structure description
record_manhour*RecordManhourSpecific recorded manhour informationSee RecordManhour structure description

Task Structure

FieldTypeMeaningNotes
UUIDstringTask UUID
TeamUUIDstringTeam UUID
OwnerstringTask owner
AssignstringTask assignee
ProjectUUIDstringProject UUID
IssueTypeUUIDstringIssue type UUID
ParentUUIDstringParent task UUID
SubIssueTypeUUIDstringSub-issue type UUID
IsCreateTaskboolWhether it's a new task

AssessManhour Structure

FieldTypeMeaningNotes
UUIDstringEstimated manhour UUID
Fromint64Start time
Toint64End time
HoursFormatstringManhour format
IncludeNonWorkingDayint64Whether to include non-working days
Hoursfloat64Manhour amount
OwnerstringManhour owner
StartTimefloat64Start timeFilled when modifying estimated manhours in detailed mode

RemainingHour Structure

FieldTypeMeaningNotes
UUIDstringRemaining manhour UUID
Hoursfloat64Remaining manhour amount
OwnerstringManhour owner

RecordManhour Structure

FieldTypeMeaningNotes
UUIDstringRecorded manhour UUID
StartTimefloat64Start time
DescriptionstringManhour description
Hoursfloat64Manhour amount
OwnerstringManhour owner

Options Structure

type Options struct {
// Use scene to determine if permission validation is needed
Scene string `json:"scene"`
}

Best Practices

  1. 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.
  2. 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.
  3. Error code structure:
// Service internal exceptions can be returned directly.
error: {
reason: "This is an error message", // Error message
level: "error" // Error level
}