@wavyx/pdcli 0.1.0 → 0.2.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 +21 -1
- package/README.md +46 -11
- package/oclif.manifest.json +2523 -240
- package/package.json +2 -1
- package/src/base-command.js +35 -5
- package/src/commands/activity/create.js +63 -0
- package/src/commands/activity/delete.js +39 -0
- package/src/commands/activity/get.js +2 -17
- package/src/commands/activity/update.js +89 -0
- package/src/commands/auth/login.js +102 -7
- package/src/commands/auth/logout.js +2 -1
- package/src/commands/auth/status.js +61 -13
- package/src/commands/deal/create.js +65 -0
- package/src/commands/deal/delete.js +39 -0
- package/src/commands/deal/get.js +2 -19
- package/src/commands/deal/update.js +79 -0
- package/src/commands/org/create.js +42 -0
- package/src/commands/org/delete.js +39 -0
- package/src/commands/org/get.js +2 -17
- package/src/commands/org/update.js +57 -0
- package/src/commands/person/create.js +54 -0
- package/src/commands/person/delete.js +39 -0
- package/src/commands/person/get.js +2 -17
- package/src/commands/person/update.js +69 -0
- package/src/commands/product/create.js +62 -0
- package/src/commands/product/delete.js +39 -0
- package/src/commands/product/get.js +26 -0
- package/src/commands/product/list.js +45 -0
- package/src/commands/product/update.js +77 -0
- package/src/lib/auth.js +227 -3
- package/src/lib/client.js +29 -6
- package/src/lib/confirm.js +10 -0
- package/src/lib/entity-view.js +37 -0
- package/src/lib/input.js +110 -0
- package/src/lib/keychain.js +47 -0
package/src/commands/deal/get.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Args } from '@oclif/core'
|
|
2
2
|
import BaseCommand from '../../base-command.js'
|
|
3
|
-
import {
|
|
4
|
-
import { flattenRecord } from '../../lib/output/record.js'
|
|
3
|
+
import { outputRecord } from '../../lib/entity-view.js'
|
|
5
4
|
|
|
6
5
|
export default class DealGetCommand extends BaseCommand {
|
|
7
6
|
static description = 'Get a deal by ID'
|
|
@@ -22,22 +21,6 @@ export default class DealGetCommand extends BaseCommand {
|
|
|
22
21
|
async run() {
|
|
23
22
|
const { args } = await this.parse(DealGetCommand)
|
|
24
23
|
const body = await this.apiClient.get(`/api/v2/deals/${args.id}`)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (this.resolveFormat() === 'table') {
|
|
28
|
-
// Table view resolves custom-field hash keys to names and option IDs
|
|
29
|
-
// to labels; JSON output stays raw for scripting.
|
|
30
|
-
if (record.custom_fields && Object.keys(record.custom_fields).length) {
|
|
31
|
-
const defs = await getFields(this.apiClient, 'deal')
|
|
32
|
-
record = makeResolver(defs).resolveCustomFields(record)
|
|
33
|
-
}
|
|
34
|
-
await this.outputResults(flattenRecord(record), {
|
|
35
|
-
field: { header: 'Field' },
|
|
36
|
-
value: { header: 'Value' },
|
|
37
|
-
})
|
|
38
|
-
return
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
await this.outputResults(record, {})
|
|
24
|
+
await outputRecord(this, body.data, 'deal')
|
|
42
25
|
}
|
|
43
26
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core'
|
|
2
|
+
import BaseCommand from '../../base-command.js'
|
|
3
|
+
import { buildWriteBody } from '../../lib/input.js'
|
|
4
|
+
import { defsForFields, outputRecord } from '../../lib/entity-view.js'
|
|
5
|
+
import { CliError } from '../../lib/errors.js'
|
|
6
|
+
|
|
7
|
+
export default class DealUpdateCommand extends BaseCommand {
|
|
8
|
+
static description = 'Update a deal (v2 PATCH — only provided fields change)'
|
|
9
|
+
|
|
10
|
+
static examples = [
|
|
11
|
+
'<%= config.bin %> deal update 42 --stage 5',
|
|
12
|
+
'<%= config.bin %> deal update 42 --status won',
|
|
13
|
+
'<%= config.bin %> deal update 42 --field "Deal Size=Large"',
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
static args = {
|
|
17
|
+
id: Args.integer({ required: true, description: 'Deal ID' }),
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static flags = {
|
|
21
|
+
...BaseCommand.baseFlags,
|
|
22
|
+
title: Flags.string({ description: 'Deal title' }),
|
|
23
|
+
value: Flags.integer({ description: 'Deal value' }),
|
|
24
|
+
currency: Flags.string({ description: 'Deal currency (e.g. EUR)' }),
|
|
25
|
+
status: Flags.string({
|
|
26
|
+
description: 'Deal status',
|
|
27
|
+
options: ['open', 'won', 'lost'],
|
|
28
|
+
}),
|
|
29
|
+
stage: Flags.integer({ description: 'Stage ID' }),
|
|
30
|
+
pipeline: Flags.integer({ description: 'Pipeline ID' }),
|
|
31
|
+
person: Flags.integer({ description: 'Linked person ID' }),
|
|
32
|
+
org: Flags.integer({ description: 'Linked organization ID' }),
|
|
33
|
+
owner: Flags.integer({ description: 'Owner (user) ID' }),
|
|
34
|
+
probability: Flags.integer({ description: 'Success probability (0-100)' }),
|
|
35
|
+
'expected-close-date': Flags.string({
|
|
36
|
+
description: 'Expected close date (YYYY-MM-DD)',
|
|
37
|
+
}),
|
|
38
|
+
field: Flags.string({
|
|
39
|
+
multiple: true,
|
|
40
|
+
description: 'Custom/standard field as "Name=Value" (repeatable)',
|
|
41
|
+
}),
|
|
42
|
+
body: Flags.string({ description: 'Raw JSON body to merge (flags win)' }),
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async run() {
|
|
46
|
+
const { args, flags } = await this.parse(DealUpdateCommand)
|
|
47
|
+
|
|
48
|
+
const body = buildWriteBody({
|
|
49
|
+
typed: {
|
|
50
|
+
title: flags.title,
|
|
51
|
+
value: flags.value,
|
|
52
|
+
currency: flags.currency,
|
|
53
|
+
status: flags.status,
|
|
54
|
+
stage_id: flags.stage,
|
|
55
|
+
pipeline_id: flags.pipeline,
|
|
56
|
+
person_id: flags.person,
|
|
57
|
+
org_id: flags.org,
|
|
58
|
+
owner_id: flags.owner,
|
|
59
|
+
probability: flags.probability,
|
|
60
|
+
expected_close_date: flags['expected-close-date'],
|
|
61
|
+
},
|
|
62
|
+
fields: flags.field,
|
|
63
|
+
rawBody: flags.body,
|
|
64
|
+
defs: await defsForFields(this, 'deal', flags.field),
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
if (Object.keys(body).length === 0) {
|
|
68
|
+
throw new CliError(
|
|
69
|
+
'Nothing to update — pass at least one field flag, --field, or --body',
|
|
70
|
+
{ exitCode: 64 },
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const res = await this.apiClient.patch(`/api/v2/deals/${args.id}`, {
|
|
75
|
+
body,
|
|
76
|
+
})
|
|
77
|
+
await outputRecord(this, res.data, 'deal')
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core'
|
|
2
|
+
import BaseCommand from '../../base-command.js'
|
|
3
|
+
import { buildWriteBody } from '../../lib/input.js'
|
|
4
|
+
import { defsForFields, outputRecord } from '../../lib/entity-view.js'
|
|
5
|
+
|
|
6
|
+
export default class OrgCreateCommand extends BaseCommand {
|
|
7
|
+
static description = 'Create an organization'
|
|
8
|
+
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> org create --name "Acme Corp"',
|
|
11
|
+
'<%= config.bin %> org create --name "Tiered" --field "Tier=Gold"',
|
|
12
|
+
'<%= config.bin %> org create --name "Raw" --body \'{"visible_to":3}\'',
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
static flags = {
|
|
16
|
+
...BaseCommand.baseFlags,
|
|
17
|
+
name: Flags.string({ required: true, description: 'Organization name' }),
|
|
18
|
+
owner: Flags.integer({ description: 'Owner (user) ID' }),
|
|
19
|
+
field: Flags.string({
|
|
20
|
+
multiple: true,
|
|
21
|
+
description: 'Custom/standard field as "Name=Value" (repeatable)',
|
|
22
|
+
}),
|
|
23
|
+
body: Flags.string({ description: 'Raw JSON body to merge (flags win)' }),
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async run() {
|
|
27
|
+
const { flags } = await this.parse(OrgCreateCommand)
|
|
28
|
+
|
|
29
|
+
const body = buildWriteBody({
|
|
30
|
+
typed: {
|
|
31
|
+
name: flags.name,
|
|
32
|
+
owner_id: flags.owner,
|
|
33
|
+
},
|
|
34
|
+
fields: flags.field,
|
|
35
|
+
rawBody: flags.body,
|
|
36
|
+
defs: await defsForFields(this, 'org', flags.field),
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const res = await this.apiClient.post('/api/v2/organizations', { body })
|
|
40
|
+
await outputRecord(this, res.data, 'org')
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core'
|
|
2
|
+
import chalk from 'chalk'
|
|
3
|
+
import BaseCommand from '../../base-command.js'
|
|
4
|
+
import { confirmAction } from '../../lib/confirm.js'
|
|
5
|
+
import { CliError } from '../../lib/errors.js'
|
|
6
|
+
|
|
7
|
+
export default class OrgDeleteCommand extends BaseCommand {
|
|
8
|
+
static description = 'Delete an organization'
|
|
9
|
+
|
|
10
|
+
static examples = [
|
|
11
|
+
'<%= config.bin %> org delete 7',
|
|
12
|
+
'<%= config.bin %> org delete 7 --yes',
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
static args = {
|
|
16
|
+
id: Args.integer({ required: true, description: 'Organization ID' }),
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static flags = {
|
|
20
|
+
...BaseCommand.baseFlags,
|
|
21
|
+
yes: Flags.boolean({
|
|
22
|
+
char: 'y',
|
|
23
|
+
description: 'Skip the confirmation prompt',
|
|
24
|
+
default: false,
|
|
25
|
+
}),
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async run() {
|
|
29
|
+
const { args, flags } = await this.parse(OrgDeleteCommand)
|
|
30
|
+
|
|
31
|
+
const ok = await confirmAction(`Delete organization ${args.id}?`, flags.yes)
|
|
32
|
+
if (!ok) {
|
|
33
|
+
throw new CliError('Aborted', { exitCode: 1 })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
await this.apiClient.del(`/api/v2/organizations/${args.id}`)
|
|
37
|
+
this.log(chalk.green(`Deleted organization ${args.id}`))
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/commands/org/get.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Args } from '@oclif/core'
|
|
2
2
|
import BaseCommand from '../../base-command.js'
|
|
3
|
-
import {
|
|
4
|
-
import { flattenRecord } from '../../lib/output/record.js'
|
|
3
|
+
import { outputRecord } from '../../lib/entity-view.js'
|
|
5
4
|
|
|
6
5
|
export default class OrgGetCommand extends BaseCommand {
|
|
7
6
|
static description = 'Get an organization by ID'
|
|
@@ -22,20 +21,6 @@ export default class OrgGetCommand extends BaseCommand {
|
|
|
22
21
|
async run() {
|
|
23
22
|
const { args } = await this.parse(OrgGetCommand)
|
|
24
23
|
const body = await this.apiClient.get(`/api/v2/organizations/${args.id}`)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (this.resolveFormat() === 'table') {
|
|
28
|
-
if (record.custom_fields && Object.keys(record.custom_fields).length) {
|
|
29
|
-
const defs = await getFields(this.apiClient, 'org')
|
|
30
|
-
record = makeResolver(defs).resolveCustomFields(record)
|
|
31
|
-
}
|
|
32
|
-
await this.outputResults(flattenRecord(record), {
|
|
33
|
-
field: { header: 'Field' },
|
|
34
|
-
value: { header: 'Value' },
|
|
35
|
-
})
|
|
36
|
-
return
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
await this.outputResults(record, {})
|
|
24
|
+
await outputRecord(this, body.data, 'org')
|
|
40
25
|
}
|
|
41
26
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core'
|
|
2
|
+
import BaseCommand from '../../base-command.js'
|
|
3
|
+
import { buildWriteBody } from '../../lib/input.js'
|
|
4
|
+
import { defsForFields, outputRecord } from '../../lib/entity-view.js'
|
|
5
|
+
import { CliError } from '../../lib/errors.js'
|
|
6
|
+
|
|
7
|
+
export default class OrgUpdateCommand extends BaseCommand {
|
|
8
|
+
static description =
|
|
9
|
+
'Update an organization (v2 PATCH — only provided fields change)'
|
|
10
|
+
|
|
11
|
+
static examples = [
|
|
12
|
+
'<%= config.bin %> org update 7 --name "Acme Inc"',
|
|
13
|
+
'<%= config.bin %> org update 7 --owner 9',
|
|
14
|
+
'<%= config.bin %> org update 7 --field "Tier=Gold"',
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
static args = {
|
|
18
|
+
id: Args.integer({ required: true, description: 'Organization ID' }),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static flags = {
|
|
22
|
+
...BaseCommand.baseFlags,
|
|
23
|
+
name: Flags.string({ description: 'Organization name' }),
|
|
24
|
+
owner: Flags.integer({ description: 'Owner (user) ID' }),
|
|
25
|
+
field: Flags.string({
|
|
26
|
+
multiple: true,
|
|
27
|
+
description: 'Custom/standard field as "Name=Value" (repeatable)',
|
|
28
|
+
}),
|
|
29
|
+
body: Flags.string({ description: 'Raw JSON body to merge (flags win)' }),
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async run() {
|
|
33
|
+
const { args, flags } = await this.parse(OrgUpdateCommand)
|
|
34
|
+
|
|
35
|
+
const body = buildWriteBody({
|
|
36
|
+
typed: {
|
|
37
|
+
name: flags.name,
|
|
38
|
+
owner_id: flags.owner,
|
|
39
|
+
},
|
|
40
|
+
fields: flags.field,
|
|
41
|
+
rawBody: flags.body,
|
|
42
|
+
defs: await defsForFields(this, 'org', flags.field),
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
if (Object.keys(body).length === 0) {
|
|
46
|
+
throw new CliError(
|
|
47
|
+
'Nothing to update — pass at least one field flag, --field, or --body',
|
|
48
|
+
{ exitCode: 64 },
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const res = await this.apiClient.patch(`/api/v2/organizations/${args.id}`, {
|
|
53
|
+
body,
|
|
54
|
+
})
|
|
55
|
+
await outputRecord(this, res.data, 'org')
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core'
|
|
2
|
+
import BaseCommand from '../../base-command.js'
|
|
3
|
+
import { buildWriteBody } from '../../lib/input.js'
|
|
4
|
+
import { defsForFields, outputRecord } from '../../lib/entity-view.js'
|
|
5
|
+
|
|
6
|
+
export default class PersonCreateCommand extends BaseCommand {
|
|
7
|
+
static description = 'Create a person'
|
|
8
|
+
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> person create --name "Jane Doe" --email jane@acme.com',
|
|
11
|
+
'<%= config.bin %> person create --name "Jane" --field "Segment=Enterprise"',
|
|
12
|
+
'<%= config.bin %> person create --name "Raw" --body \'{"visible_to":"3"}\'',
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
static flags = {
|
|
16
|
+
...BaseCommand.baseFlags,
|
|
17
|
+
name: Flags.string({ required: true, description: 'Person name' }),
|
|
18
|
+
email: Flags.string({
|
|
19
|
+
multiple: true,
|
|
20
|
+
description: 'Email address (repeatable; first is primary)',
|
|
21
|
+
}),
|
|
22
|
+
phone: Flags.string({
|
|
23
|
+
multiple: true,
|
|
24
|
+
description: 'Phone number (repeatable; first is primary)',
|
|
25
|
+
}),
|
|
26
|
+
org: Flags.integer({ description: 'Linked organization ID' }),
|
|
27
|
+
owner: Flags.integer({ description: 'Owner (user) ID' }),
|
|
28
|
+
field: Flags.string({
|
|
29
|
+
multiple: true,
|
|
30
|
+
description: 'Custom/standard field as "Name=Value" (repeatable)',
|
|
31
|
+
}),
|
|
32
|
+
body: Flags.string({ description: 'Raw JSON body to merge (flags win)' }),
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async run() {
|
|
36
|
+
const { flags } = await this.parse(PersonCreateCommand)
|
|
37
|
+
|
|
38
|
+
const body = buildWriteBody({
|
|
39
|
+
typed: {
|
|
40
|
+
name: flags.name,
|
|
41
|
+
org_id: flags.org,
|
|
42
|
+
owner_id: flags.owner,
|
|
43
|
+
emails: flags.email?.map((value, i) => ({ value, primary: i === 0 })),
|
|
44
|
+
phones: flags.phone?.map((value, i) => ({ value, primary: i === 0 })),
|
|
45
|
+
},
|
|
46
|
+
fields: flags.field,
|
|
47
|
+
rawBody: flags.body,
|
|
48
|
+
defs: await defsForFields(this, 'person', flags.field),
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const res = await this.apiClient.post('/api/v2/persons', { body })
|
|
52
|
+
await outputRecord(this, res.data, 'person')
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core'
|
|
2
|
+
import chalk from 'chalk'
|
|
3
|
+
import BaseCommand from '../../base-command.js'
|
|
4
|
+
import { confirmAction } from '../../lib/confirm.js'
|
|
5
|
+
import { CliError } from '../../lib/errors.js'
|
|
6
|
+
|
|
7
|
+
export default class PersonDeleteCommand extends BaseCommand {
|
|
8
|
+
static description = 'Delete a person'
|
|
9
|
+
|
|
10
|
+
static examples = [
|
|
11
|
+
'<%= config.bin %> person delete 42',
|
|
12
|
+
'<%= config.bin %> person delete 42 --yes',
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
static args = {
|
|
16
|
+
id: Args.integer({ required: true, description: 'Person ID' }),
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static flags = {
|
|
20
|
+
...BaseCommand.baseFlags,
|
|
21
|
+
yes: Flags.boolean({
|
|
22
|
+
char: 'y',
|
|
23
|
+
description: 'Skip the confirmation prompt',
|
|
24
|
+
default: false,
|
|
25
|
+
}),
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async run() {
|
|
29
|
+
const { args, flags } = await this.parse(PersonDeleteCommand)
|
|
30
|
+
|
|
31
|
+
const ok = await confirmAction(`Delete person ${args.id}?`, flags.yes)
|
|
32
|
+
if (!ok) {
|
|
33
|
+
throw new CliError('Aborted', { exitCode: 1 })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
await this.apiClient.del(`/api/v2/persons/${args.id}`)
|
|
37
|
+
this.log(chalk.green(`Deleted person ${args.id}`))
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Args } from '@oclif/core'
|
|
2
2
|
import BaseCommand from '../../base-command.js'
|
|
3
|
-
import {
|
|
4
|
-
import { flattenRecord } from '../../lib/output/record.js'
|
|
3
|
+
import { outputRecord } from '../../lib/entity-view.js'
|
|
5
4
|
|
|
6
5
|
export default class PersonGetCommand extends BaseCommand {
|
|
7
6
|
static description = 'Get a person by ID'
|
|
@@ -22,20 +21,6 @@ export default class PersonGetCommand extends BaseCommand {
|
|
|
22
21
|
async run() {
|
|
23
22
|
const { args } = await this.parse(PersonGetCommand)
|
|
24
23
|
const body = await this.apiClient.get(`/api/v2/persons/${args.id}`)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (this.resolveFormat() === 'table') {
|
|
28
|
-
if (record.custom_fields && Object.keys(record.custom_fields).length) {
|
|
29
|
-
const defs = await getFields(this.apiClient, 'person')
|
|
30
|
-
record = makeResolver(defs).resolveCustomFields(record)
|
|
31
|
-
}
|
|
32
|
-
await this.outputResults(flattenRecord(record), {
|
|
33
|
-
field: { header: 'Field' },
|
|
34
|
-
value: { header: 'Value' },
|
|
35
|
-
})
|
|
36
|
-
return
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
await this.outputResults(record, {})
|
|
24
|
+
await outputRecord(this, body.data, 'person')
|
|
40
25
|
}
|
|
41
26
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core'
|
|
2
|
+
import BaseCommand from '../../base-command.js'
|
|
3
|
+
import { buildWriteBody } from '../../lib/input.js'
|
|
4
|
+
import { defsForFields, outputRecord } from '../../lib/entity-view.js'
|
|
5
|
+
import { CliError } from '../../lib/errors.js'
|
|
6
|
+
|
|
7
|
+
export default class PersonUpdateCommand extends BaseCommand {
|
|
8
|
+
static description =
|
|
9
|
+
'Update a person (v2 PATCH — only provided fields change)'
|
|
10
|
+
|
|
11
|
+
static examples = [
|
|
12
|
+
'<%= config.bin %> person update 42 --name "New name"',
|
|
13
|
+
'<%= config.bin %> person update 42 --email new@acme.com',
|
|
14
|
+
'<%= config.bin %> person update 42 --field "Segment=Enterprise"',
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
static args = {
|
|
18
|
+
id: Args.integer({ required: true, description: 'Person ID' }),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static flags = {
|
|
22
|
+
...BaseCommand.baseFlags,
|
|
23
|
+
name: Flags.string({ description: 'Person name' }),
|
|
24
|
+
email: Flags.string({
|
|
25
|
+
multiple: true,
|
|
26
|
+
description: 'Email address (repeatable; first is primary)',
|
|
27
|
+
}),
|
|
28
|
+
phone: Flags.string({
|
|
29
|
+
multiple: true,
|
|
30
|
+
description: 'Phone number (repeatable; first is primary)',
|
|
31
|
+
}),
|
|
32
|
+
org: Flags.integer({ description: 'Linked organization ID' }),
|
|
33
|
+
owner: Flags.integer({ description: 'Owner (user) ID' }),
|
|
34
|
+
field: Flags.string({
|
|
35
|
+
multiple: true,
|
|
36
|
+
description: 'Custom/standard field as "Name=Value" (repeatable)',
|
|
37
|
+
}),
|
|
38
|
+
body: Flags.string({ description: 'Raw JSON body to merge (flags win)' }),
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async run() {
|
|
42
|
+
const { args, flags } = await this.parse(PersonUpdateCommand)
|
|
43
|
+
|
|
44
|
+
const body = buildWriteBody({
|
|
45
|
+
typed: {
|
|
46
|
+
name: flags.name,
|
|
47
|
+
org_id: flags.org,
|
|
48
|
+
owner_id: flags.owner,
|
|
49
|
+
emails: flags.email?.map((value, i) => ({ value, primary: i === 0 })),
|
|
50
|
+
phones: flags.phone?.map((value, i) => ({ value, primary: i === 0 })),
|
|
51
|
+
},
|
|
52
|
+
fields: flags.field,
|
|
53
|
+
rawBody: flags.body,
|
|
54
|
+
defs: await defsForFields(this, 'person', flags.field),
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
if (Object.keys(body).length === 0) {
|
|
58
|
+
throw new CliError(
|
|
59
|
+
'Nothing to update — pass at least one field flag, --field, or --body',
|
|
60
|
+
{ exitCode: 64 },
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const res = await this.apiClient.patch(`/api/v2/persons/${args.id}`, {
|
|
65
|
+
body,
|
|
66
|
+
})
|
|
67
|
+
await outputRecord(this, res.data, 'person')
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core'
|
|
2
|
+
import BaseCommand from '../../base-command.js'
|
|
3
|
+
import { buildWriteBody } from '../../lib/input.js'
|
|
4
|
+
import { defsForFields, outputRecord } from '../../lib/entity-view.js'
|
|
5
|
+
|
|
6
|
+
export default class ProductCreateCommand extends BaseCommand {
|
|
7
|
+
static description = 'Create a product'
|
|
8
|
+
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> product create --name "Widget" --code W-1 --price 9.99 --currency EUR',
|
|
11
|
+
'<%= config.bin %> product create --name "Sized" --field "Material=Steel"',
|
|
12
|
+
'<%= config.bin %> product create --name "Raw" --body \'{"tax":19}\'',
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
static flags = {
|
|
16
|
+
...BaseCommand.baseFlags,
|
|
17
|
+
name: Flags.string({ required: true, description: 'Product name' }),
|
|
18
|
+
code: Flags.string({ description: 'Product code (SKU)' }),
|
|
19
|
+
unit: Flags.string({ description: 'Unit of measure' }),
|
|
20
|
+
description: Flags.string({ description: 'Product description' }),
|
|
21
|
+
owner: Flags.integer({ description: 'Owner (user) ID' }),
|
|
22
|
+
price: Flags.string({
|
|
23
|
+
description: 'Unit price (requires --currency)',
|
|
24
|
+
dependsOn: ['currency'],
|
|
25
|
+
}),
|
|
26
|
+
currency: Flags.string({
|
|
27
|
+
description: 'Price currency (requires --price)',
|
|
28
|
+
dependsOn: ['price'],
|
|
29
|
+
}),
|
|
30
|
+
field: Flags.string({
|
|
31
|
+
multiple: true,
|
|
32
|
+
description: 'Custom/standard field as "Name=Value" (repeatable)',
|
|
33
|
+
}),
|
|
34
|
+
body: Flags.string({ description: 'Raw JSON body to merge (flags win)' }),
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async run() {
|
|
38
|
+
const { flags } = await this.parse(ProductCreateCommand)
|
|
39
|
+
|
|
40
|
+
const prices =
|
|
41
|
+
flags.price !== undefined && flags.currency !== undefined
|
|
42
|
+
? [{ price: Number(flags.price), currency: flags.currency }]
|
|
43
|
+
: undefined
|
|
44
|
+
|
|
45
|
+
const body = buildWriteBody({
|
|
46
|
+
typed: {
|
|
47
|
+
name: flags.name,
|
|
48
|
+
code: flags.code,
|
|
49
|
+
unit: flags.unit,
|
|
50
|
+
description: flags.description,
|
|
51
|
+
owner_id: flags.owner,
|
|
52
|
+
prices,
|
|
53
|
+
},
|
|
54
|
+
fields: flags.field,
|
|
55
|
+
rawBody: flags.body,
|
|
56
|
+
defs: await defsForFields(this, 'product', flags.field),
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
const res = await this.apiClient.post('/api/v2/products', { body })
|
|
60
|
+
await outputRecord(this, res.data, 'product')
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core'
|
|
2
|
+
import chalk from 'chalk'
|
|
3
|
+
import BaseCommand from '../../base-command.js'
|
|
4
|
+
import { confirmAction } from '../../lib/confirm.js'
|
|
5
|
+
import { CliError } from '../../lib/errors.js'
|
|
6
|
+
|
|
7
|
+
export default class ProductDeleteCommand extends BaseCommand {
|
|
8
|
+
static description = 'Delete a product'
|
|
9
|
+
|
|
10
|
+
static examples = [
|
|
11
|
+
'<%= config.bin %> product delete 7',
|
|
12
|
+
'<%= config.bin %> product delete 7 --yes',
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
static args = {
|
|
16
|
+
id: Args.integer({ required: true, description: 'Product ID' }),
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static flags = {
|
|
20
|
+
...BaseCommand.baseFlags,
|
|
21
|
+
yes: Flags.boolean({
|
|
22
|
+
char: 'y',
|
|
23
|
+
description: 'Skip the confirmation prompt',
|
|
24
|
+
default: false,
|
|
25
|
+
}),
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async run() {
|
|
29
|
+
const { args, flags } = await this.parse(ProductDeleteCommand)
|
|
30
|
+
|
|
31
|
+
const ok = await confirmAction(`Delete product ${args.id}?`, flags.yes)
|
|
32
|
+
if (!ok) {
|
|
33
|
+
throw new CliError('Aborted', { exitCode: 1 })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
await this.apiClient.del(`/api/v2/products/${args.id}`)
|
|
37
|
+
this.log(chalk.green(`Deleted product ${args.id}`))
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Args } from '@oclif/core'
|
|
2
|
+
import BaseCommand from '../../base-command.js'
|
|
3
|
+
import { outputRecord } from '../../lib/entity-view.js'
|
|
4
|
+
|
|
5
|
+
export default class ProductGetCommand extends BaseCommand {
|
|
6
|
+
static description = 'Get a product by ID'
|
|
7
|
+
|
|
8
|
+
static examples = [
|
|
9
|
+
'<%= config.bin %> product get 7',
|
|
10
|
+
'<%= config.bin %> product get 7 --output json',
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
static flags = {
|
|
14
|
+
...BaseCommand.baseFlags,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static args = {
|
|
18
|
+
id: Args.integer({ required: true, description: 'Product ID' }),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async run() {
|
|
22
|
+
const { args } = await this.parse(ProductGetCommand)
|
|
23
|
+
const body = await this.apiClient.get(`/api/v2/products/${args.id}`)
|
|
24
|
+
await outputRecord(this, body.data, 'product')
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core'
|
|
2
|
+
import BaseCommand from '../../base-command.js'
|
|
3
|
+
import { collectPages } from '../../lib/pagination.js'
|
|
4
|
+
|
|
5
|
+
const columns = {
|
|
6
|
+
id: { header: 'ID' },
|
|
7
|
+
name: { header: 'Name' },
|
|
8
|
+
code: { header: 'Code' },
|
|
9
|
+
unit: { header: 'Unit' },
|
|
10
|
+
price: {
|
|
11
|
+
header: 'Price',
|
|
12
|
+
get: (row) =>
|
|
13
|
+
row.prices?.[0] ? `${row.prices[0].price} ${row.prices[0].currency}` : '',
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default class ProductListCommand extends BaseCommand {
|
|
18
|
+
static description = 'List products'
|
|
19
|
+
|
|
20
|
+
static examples = [
|
|
21
|
+
'<%= config.bin %> product list',
|
|
22
|
+
'<%= config.bin %> product list --owner 3 --output json',
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
static flags = {
|
|
26
|
+
...BaseCommand.baseFlags,
|
|
27
|
+
owner: Flags.integer({ description: 'Filter by owner (user) ID' }),
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async run() {
|
|
31
|
+
const { flags } = await this.parse(ProductListCommand)
|
|
32
|
+
const limit = flags.limit ?? 100
|
|
33
|
+
|
|
34
|
+
const query = {
|
|
35
|
+
owner_id: flags.owner,
|
|
36
|
+
limit: Math.min(limit, 100),
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const items = await collectPages(
|
|
40
|
+
this.apiClient.pageV2('/api/v2/products', query),
|
|
41
|
+
limit,
|
|
42
|
+
)
|
|
43
|
+
await this.outputResults(items, columns)
|
|
44
|
+
}
|
|
45
|
+
}
|