@wavyx/pdcli 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +7 -1
- package/oclif.manifest.json +6369 -1598
- package/package.json +29 -2
- package/src/commands/activity/type/list.js +36 -0
- package/src/commands/deal/convert.js +112 -0
- package/src/commands/deal/follower/add.js +31 -0
- package/src/commands/deal/follower/list.js +38 -0
- package/src/commands/deal/follower/remove.js +44 -0
- package/src/commands/deal/list.js +7 -4
- package/src/commands/deal/participant/add.js +31 -0
- package/src/commands/deal/participant/list.js +46 -0
- package/src/commands/deal/participant/remove.js +53 -0
- package/src/commands/deal/summary.js +69 -0
- package/src/commands/field/create.js +72 -0
- package/src/commands/field/delete.js +56 -0
- package/src/commands/field/get.js +10 -1
- package/src/commands/field/list.js +10 -1
- package/src/commands/field/option/add.js +49 -0
- package/src/commands/field/option/remove.js +72 -0
- package/src/commands/field/update.js +53 -0
- package/src/commands/file/delete.js +41 -0
- package/src/commands/file/update.js +39 -0
- package/src/commands/filter/delete.js +41 -0
- package/src/commands/lead/convert.js +103 -0
- package/src/commands/note/comment/add.js +31 -0
- package/src/commands/note/comment/delete.js +51 -0
- package/src/commands/note/comment/list.js +43 -0
- package/src/commands/note/comment/update.js +37 -0
- package/src/commands/org/follower/add.js +31 -0
- package/src/commands/org/follower/list.js +41 -0
- package/src/commands/org/follower/remove.js +50 -0
- package/src/commands/org/relationship/add.js +44 -0
- package/src/commands/org/relationship/list.js +52 -0
- package/src/commands/org/relationship/remove.js +46 -0
- package/src/commands/person/follower/add.js +31 -0
- package/src/commands/person/follower/list.js +38 -0
- package/src/commands/person/follower/remove.js +48 -0
- package/src/commands/project/list.js +10 -4
- package/src/commands/search.js +66 -9
- package/src/commands/task/create.js +44 -0
- package/src/commands/task/delete.js +41 -0
- package/src/commands/task/get.js +26 -0
- package/src/commands/task/list.js +60 -0
- package/src/commands/task/update.js +72 -0
- package/src/lib/client.js +19 -0
- package/src/lib/fields.js +43 -12
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core'
|
|
2
|
+
import BaseCommand from '../../base-command.js'
|
|
3
|
+
import { buildWriteBody } from '../../lib/input.js'
|
|
4
|
+
import { outputRecord } from '../../lib/entity-view.js'
|
|
5
|
+
import { CliError } from '../../lib/errors.js'
|
|
6
|
+
|
|
7
|
+
export default class TaskUpdateCommand extends BaseCommand {
|
|
8
|
+
static description = 'Update a task (v2 PATCH — only provided fields change)'
|
|
9
|
+
|
|
10
|
+
static examples = [
|
|
11
|
+
'<%= config.bin %> task update 7 --title "Renamed"',
|
|
12
|
+
'<%= config.bin %> task update 7 --done',
|
|
13
|
+
'<%= config.bin %> task update 7 --assignee 9',
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
static args = {
|
|
17
|
+
id: Args.integer({ required: true, description: 'Task ID' }),
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static flags = {
|
|
21
|
+
...BaseCommand.baseFlags,
|
|
22
|
+
title: Flags.string({ description: 'Task title' }),
|
|
23
|
+
project: Flags.integer({ description: 'Project ID' }),
|
|
24
|
+
description: Flags.string({ description: 'Task description' }),
|
|
25
|
+
assignee: Flags.integer({ description: 'Assignee (user) ID' }),
|
|
26
|
+
'due-date': Flags.string({ description: 'Due date (YYYY-MM-DD)' }),
|
|
27
|
+
parent: Flags.integer({ description: 'Parent task ID' }),
|
|
28
|
+
done: Flags.boolean({
|
|
29
|
+
description: 'Mark the task as done',
|
|
30
|
+
exclusive: ['undone'],
|
|
31
|
+
}),
|
|
32
|
+
undone: Flags.boolean({
|
|
33
|
+
description: 'Mark the task as not done',
|
|
34
|
+
exclusive: ['done'],
|
|
35
|
+
}),
|
|
36
|
+
body: Flags.string({ description: 'Raw JSON body to merge (flags win)' }),
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async run() {
|
|
40
|
+
const { args, flags } = await this.parse(TaskUpdateCommand)
|
|
41
|
+
|
|
42
|
+
// The v2 task body takes `done` as an integer enum (0 = not done, 1 = done).
|
|
43
|
+
let done
|
|
44
|
+
if (flags.done) done = 1
|
|
45
|
+
else if (flags.undone) done = 0
|
|
46
|
+
|
|
47
|
+
const body = buildWriteBody({
|
|
48
|
+
typed: {
|
|
49
|
+
title: flags.title,
|
|
50
|
+
project_id: flags.project,
|
|
51
|
+
description: flags.description,
|
|
52
|
+
assignee_id: flags.assignee,
|
|
53
|
+
due_date: flags['due-date'],
|
|
54
|
+
parent_task_id: flags.parent,
|
|
55
|
+
done,
|
|
56
|
+
},
|
|
57
|
+
rawBody: flags.body,
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
if (Object.keys(body).length === 0) {
|
|
61
|
+
throw new CliError(
|
|
62
|
+
'Nothing to update — pass at least one field flag or --body',
|
|
63
|
+
{ exitCode: 64 },
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const res = await this.apiClient.patch(`/api/v2/tasks/${args.id}`, {
|
|
68
|
+
body,
|
|
69
|
+
})
|
|
70
|
+
await outputRecord(this, res.data)
|
|
71
|
+
}
|
|
72
|
+
}
|
package/src/lib/client.js
CHANGED
|
@@ -307,6 +307,24 @@ export function createClient({
|
|
|
307
307
|
})
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
+
/**
|
|
311
|
+
* PUT application/x-www-form-urlencoded (v1 form endpoints, e.g.
|
|
312
|
+
* /api/v1/files/:id — JSON is not accepted there).
|
|
313
|
+
* @param {string} path
|
|
314
|
+
* @param {Record<string, unknown>} fields Null/undefined values are omitted.
|
|
315
|
+
*/
|
|
316
|
+
async function putForm(path, fields = {}) {
|
|
317
|
+
const params = new URLSearchParams()
|
|
318
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
319
|
+
if (v != null) params.set(k, String(v))
|
|
320
|
+
}
|
|
321
|
+
return transport('PUT', lockedUrl(path), {
|
|
322
|
+
path,
|
|
323
|
+
makeBody: () => params.toString(),
|
|
324
|
+
extraHeaders: { 'content-type': 'application/x-www-form-urlencoded' },
|
|
325
|
+
})
|
|
326
|
+
}
|
|
327
|
+
|
|
310
328
|
return {
|
|
311
329
|
get: (path, opts) => request('GET', path, opts),
|
|
312
330
|
post: (path, opts) => request('POST', path, opts),
|
|
@@ -316,6 +334,7 @@ export function createClient({
|
|
|
316
334
|
download,
|
|
317
335
|
postMultipart,
|
|
318
336
|
postForm,
|
|
337
|
+
putForm,
|
|
319
338
|
pageV1,
|
|
320
339
|
pageV2,
|
|
321
340
|
}
|
package/src/lib/fields.js
CHANGED
|
@@ -13,19 +13,35 @@ const ENTITY_FIELDS = {
|
|
|
13
13
|
activity: 'activityFields',
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Entities whose fields live ONLY on v1 — offset-paginated and returning the
|
|
18
|
+
* legacy key/name shape that getFields normalizes to field_code/field_name.
|
|
19
|
+
*/
|
|
20
|
+
const V1_ENTITY_FIELDS = {
|
|
21
|
+
lead: 'leadFields',
|
|
22
|
+
note: 'noteFields',
|
|
23
|
+
}
|
|
24
|
+
|
|
16
25
|
/**
|
|
17
26
|
* @param {string} entity deal | person | org(anization) | product | activity
|
|
18
|
-
*
|
|
27
|
+
* | lead | note
|
|
28
|
+
* @returns {string} fields endpoint path (v2 for core entities, v1 for
|
|
29
|
+
* lead/note)
|
|
19
30
|
*/
|
|
20
31
|
export function entityToFieldsPath(entity) {
|
|
21
|
-
const
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
const v2 = ENTITY_FIELDS[entity]
|
|
33
|
+
if (v2) return `/api/v2/${v2}`
|
|
34
|
+
|
|
35
|
+
const v1 = V1_ENTITY_FIELDS[entity]
|
|
36
|
+
if (v1) return `/api/v1/${v1}`
|
|
37
|
+
|
|
38
|
+
throw new CliError(
|
|
39
|
+
`Unknown entity "${entity}". Use one of: ${[
|
|
40
|
+
...Object.keys(ENTITY_FIELDS),
|
|
41
|
+
...Object.keys(V1_ENTITY_FIELDS),
|
|
42
|
+
].join(', ')}`,
|
|
43
|
+
{ exitCode: 64 },
|
|
44
|
+
)
|
|
29
45
|
}
|
|
30
46
|
|
|
31
47
|
/** @type {Map<string, object[]>} per-run field-definition cache */
|
|
@@ -35,9 +51,22 @@ export function clearFieldsCache() {
|
|
|
35
51
|
cache.clear()
|
|
36
52
|
}
|
|
37
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Normalize a v1 field definition (key/name) to the v2 shape
|
|
56
|
+
* (field_code/field_name) so callers can treat both alike.
|
|
57
|
+
* @param {object} def
|
|
58
|
+
*/
|
|
59
|
+
function normalizeV1Field(def) {
|
|
60
|
+
const { key, name, ...rest } = def
|
|
61
|
+
return { ...rest, field_code: key, field_name: name }
|
|
62
|
+
}
|
|
63
|
+
|
|
38
64
|
/**
|
|
39
65
|
* Fetch (and memoize for this run) all field definitions for an entity.
|
|
40
|
-
*
|
|
66
|
+
* Core entities use the v2 cursor pager; lead/note use the v1 offset pager
|
|
67
|
+
* and are normalized to the v2 field_code/field_name shape.
|
|
68
|
+
* @param {{ pageV2: (path: string) => AsyncGenerator<object>,
|
|
69
|
+
* pageV1: (path: string) => AsyncGenerator<object> }} client
|
|
41
70
|
* @param {string} entity
|
|
42
71
|
* @returns {Promise<object[]>}
|
|
43
72
|
*/
|
|
@@ -45,10 +74,12 @@ export async function getFields(client, entity) {
|
|
|
45
74
|
const path = entityToFieldsPath(entity)
|
|
46
75
|
if (cache.has(path)) return cache.get(path)
|
|
47
76
|
|
|
77
|
+
const isV1 = path.startsWith('/api/v1/')
|
|
48
78
|
debug('fetching field definitions: %s', path)
|
|
49
79
|
const defs = []
|
|
50
|
-
|
|
51
|
-
|
|
80
|
+
const pager = isV1 ? client.pageV1(path) : client.pageV2(path)
|
|
81
|
+
for await (const def of pager) {
|
|
82
|
+
defs.push(isV1 ? normalizeV1Field(def) : def)
|
|
52
83
|
}
|
|
53
84
|
cache.set(path, defs)
|
|
54
85
|
return defs
|