@wavyx/pdcli 0.1.0 → 0.3.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 +36 -1
- package/README.md +60 -11
- package/oclif.manifest.json +7101 -354
- package/package.json +4 -1
- package/src/base-command.js +65 -7
- 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/api.js +6 -2
- 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/backup.js +53 -0
- 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/file/download.js +35 -0
- package/src/commands/file/get.js +26 -0
- package/src/commands/file/list.js +40 -0
- package/src/commands/file/upload.js +42 -0
- package/src/commands/filter/get.js +26 -0
- package/src/commands/filter/list.js +43 -0
- package/src/commands/goal/list.js +37 -0
- package/src/commands/lead/create.js +58 -0
- package/src/commands/lead/delete.js +39 -0
- package/src/commands/lead/get.js +26 -0
- package/src/commands/lead/list.js +50 -0
- package/src/commands/lead/update.js +71 -0
- package/src/commands/note/create.js +42 -0
- package/src/commands/note/delete.js +39 -0
- package/src/commands/note/get.js +26 -0
- package/src/commands/note/list.js +49 -0
- package/src/commands/note/update.js +45 -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/pipeline/get.js +26 -0
- package/src/commands/pipeline/list.js +37 -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/commands/project/create.js +48 -0
- package/src/commands/project/delete.js +39 -0
- package/src/commands/project/get.js +26 -0
- package/src/commands/project/list.js +39 -0
- package/src/commands/project/update.js +63 -0
- package/src/commands/stage/get.js +26 -0
- package/src/commands/stage/list.js +41 -0
- package/src/commands/webhook/create.js +75 -0
- package/src/commands/webhook/delete.js +39 -0
- package/src/commands/webhook/list.js +33 -0
- package/src/lib/auth.js +227 -3
- package/src/lib/backup.js +122 -0
- package/src/lib/client.js +96 -6
- package/src/lib/confirm.js +10 -0
- package/src/lib/entity-view.js +42 -0
- package/src/lib/input.js +110 -0
- package/src/lib/keychain.js +47 -0
- package/src/lib/output/csv.js +26 -0
- package/src/lib/output/index.js +9 -1
- package/src/lib/output/yaml.js +9 -0
|
@@ -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,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 PipelineGetCommand extends BaseCommand {
|
|
6
|
+
static description = 'Get a pipeline by ID'
|
|
7
|
+
|
|
8
|
+
static examples = [
|
|
9
|
+
'<%= config.bin %> pipeline get 1',
|
|
10
|
+
'<%= config.bin %> pipeline get 1 --output json',
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
static flags = {
|
|
14
|
+
...BaseCommand.baseFlags,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static args = {
|
|
18
|
+
id: Args.integer({ required: true, description: 'Pipeline ID' }),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async run() {
|
|
22
|
+
const { args } = await this.parse(PipelineGetCommand)
|
|
23
|
+
const body = await this.apiClient.get(`/api/v2/pipelines/${args.id}`)
|
|
24
|
+
await outputRecord(this, body.data)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import BaseCommand from '../../base-command.js'
|
|
2
|
+
import { collectPages } from '../../lib/pagination.js'
|
|
3
|
+
|
|
4
|
+
const columns = {
|
|
5
|
+
id: { header: 'ID' },
|
|
6
|
+
name: { header: 'Name' },
|
|
7
|
+
is_deal_probability_enabled: { header: 'Probability' },
|
|
8
|
+
order_nr: { header: 'Order' },
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default class PipelineListCommand extends BaseCommand {
|
|
12
|
+
static description = 'List pipelines'
|
|
13
|
+
|
|
14
|
+
static examples = [
|
|
15
|
+
'<%= config.bin %> pipeline list',
|
|
16
|
+
'<%= config.bin %> pipeline list --output json',
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
static flags = {
|
|
20
|
+
...BaseCommand.baseFlags,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async run() {
|
|
24
|
+
const { flags } = await this.parse(PipelineListCommand)
|
|
25
|
+
const limit = flags.limit ?? 100
|
|
26
|
+
|
|
27
|
+
const query = {
|
|
28
|
+
limit: Math.min(limit, 100),
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const items = await collectPages(
|
|
32
|
+
this.apiClient.pageV2('/api/v2/pipelines', query),
|
|
33
|
+
limit,
|
|
34
|
+
)
|
|
35
|
+
await this.outputResults(items, columns)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
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 ProductUpdateCommand extends BaseCommand {
|
|
8
|
+
static description =
|
|
9
|
+
'Update a product (v2 PATCH — only provided fields change)'
|
|
10
|
+
|
|
11
|
+
static examples = [
|
|
12
|
+
'<%= config.bin %> product update 7 --name "New name"',
|
|
13
|
+
'<%= config.bin %> product update 7 --price 12.50 --currency USD',
|
|
14
|
+
'<%= config.bin %> product update 7 --field "Material=Steel"',
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
static args = {
|
|
18
|
+
id: Args.integer({ required: true, description: 'Product ID' }),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static flags = {
|
|
22
|
+
...BaseCommand.baseFlags,
|
|
23
|
+
name: Flags.string({ description: 'Product name' }),
|
|
24
|
+
code: Flags.string({ description: 'Product code (SKU)' }),
|
|
25
|
+
unit: Flags.string({ description: 'Unit of measure' }),
|
|
26
|
+
description: Flags.string({ description: 'Product description' }),
|
|
27
|
+
owner: Flags.integer({ description: 'Owner (user) ID' }),
|
|
28
|
+
price: Flags.string({
|
|
29
|
+
description: 'Unit price (requires --currency)',
|
|
30
|
+
dependsOn: ['currency'],
|
|
31
|
+
}),
|
|
32
|
+
currency: Flags.string({
|
|
33
|
+
description: 'Price currency (requires --price)',
|
|
34
|
+
dependsOn: ['price'],
|
|
35
|
+
}),
|
|
36
|
+
field: Flags.string({
|
|
37
|
+
multiple: true,
|
|
38
|
+
description: 'Custom/standard field as "Name=Value" (repeatable)',
|
|
39
|
+
}),
|
|
40
|
+
body: Flags.string({ description: 'Raw JSON body to merge (flags win)' }),
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async run() {
|
|
44
|
+
const { args, flags } = await this.parse(ProductUpdateCommand)
|
|
45
|
+
|
|
46
|
+
const prices =
|
|
47
|
+
flags.price !== undefined && flags.currency !== undefined
|
|
48
|
+
? [{ price: Number(flags.price), currency: flags.currency }]
|
|
49
|
+
: undefined
|
|
50
|
+
|
|
51
|
+
const body = buildWriteBody({
|
|
52
|
+
typed: {
|
|
53
|
+
name: flags.name,
|
|
54
|
+
code: flags.code,
|
|
55
|
+
unit: flags.unit,
|
|
56
|
+
description: flags.description,
|
|
57
|
+
owner_id: flags.owner,
|
|
58
|
+
prices,
|
|
59
|
+
},
|
|
60
|
+
fields: flags.field,
|
|
61
|
+
rawBody: flags.body,
|
|
62
|
+
defs: await defsForFields(this, 'product', flags.field),
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
if (Object.keys(body).length === 0) {
|
|
66
|
+
throw new CliError(
|
|
67
|
+
'Nothing to update — pass at least one field flag, --field, or --body',
|
|
68
|
+
{ exitCode: 64 },
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const res = await this.apiClient.patch(`/api/v2/products/${args.id}`, {
|
|
73
|
+
body,
|
|
74
|
+
})
|
|
75
|
+
await outputRecord(this, res.data, 'product')
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { 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
|
+
|
|
6
|
+
export default class ProjectCreateCommand extends BaseCommand {
|
|
7
|
+
static description = 'Create a project'
|
|
8
|
+
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> project create --title "Launch"',
|
|
11
|
+
'<%= config.bin %> project create --title "Launch" --owner 3 --status open',
|
|
12
|
+
'<%= config.bin %> project create --title "Raw" --body \'{"deal_ids":[1,2]}\'',
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
static flags = {
|
|
16
|
+
...BaseCommand.baseFlags,
|
|
17
|
+
title: Flags.string({ required: true, description: 'Project title' }),
|
|
18
|
+
description: Flags.string({ description: 'Project description' }),
|
|
19
|
+
status: Flags.string({ description: 'Project status' }),
|
|
20
|
+
'start-date': Flags.string({ description: 'Start date (YYYY-MM-DD)' }),
|
|
21
|
+
'end-date': Flags.string({ description: 'End date (YYYY-MM-DD)' }),
|
|
22
|
+
owner: Flags.integer({ description: 'Owner (user) ID' }),
|
|
23
|
+
board: Flags.integer({ description: 'Board ID' }),
|
|
24
|
+
phase: Flags.integer({ description: 'Phase ID' }),
|
|
25
|
+
body: Flags.string({ description: 'Raw JSON body to merge (flags win)' }),
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async run() {
|
|
29
|
+
const { flags } = await this.parse(ProjectCreateCommand)
|
|
30
|
+
|
|
31
|
+
const body = buildWriteBody({
|
|
32
|
+
typed: {
|
|
33
|
+
title: flags.title,
|
|
34
|
+
description: flags.description,
|
|
35
|
+
status: flags.status,
|
|
36
|
+
start_date: flags['start-date'],
|
|
37
|
+
end_date: flags['end-date'],
|
|
38
|
+
owner_id: flags.owner,
|
|
39
|
+
board_id: flags.board,
|
|
40
|
+
phase_id: flags.phase,
|
|
41
|
+
},
|
|
42
|
+
rawBody: flags.body,
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const res = await this.apiClient.post('/api/v2/projects', { body })
|
|
46
|
+
await outputRecord(this, res.data)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -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 ProjectDeleteCommand extends BaseCommand {
|
|
8
|
+
static description = 'Delete a project'
|
|
9
|
+
|
|
10
|
+
static examples = [
|
|
11
|
+
'<%= config.bin %> project delete 7',
|
|
12
|
+
'<%= config.bin %> project delete 7 --yes',
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
static args = {
|
|
16
|
+
id: Args.integer({ required: true, description: 'Project 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(ProjectDeleteCommand)
|
|
30
|
+
|
|
31
|
+
const ok = await confirmAction(`Delete project ${args.id}?`, flags.yes)
|
|
32
|
+
if (!ok) {
|
|
33
|
+
throw new CliError('Aborted', { exitCode: 1 })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
await this.apiClient.del(`/api/v2/projects/${args.id}`)
|
|
37
|
+
this.log(chalk.green(`Deleted project ${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 ProjectGetCommand extends BaseCommand {
|
|
6
|
+
static description = 'Get a project by ID'
|
|
7
|
+
|
|
8
|
+
static examples = [
|
|
9
|
+
'<%= config.bin %> project get 3',
|
|
10
|
+
'<%= config.bin %> project get 3 --output json',
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
static flags = {
|
|
14
|
+
...BaseCommand.baseFlags,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static args = {
|
|
18
|
+
id: Args.integer({ required: true, description: 'Project ID' }),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async run() {
|
|
22
|
+
const { args } = await this.parse(ProjectGetCommand)
|
|
23
|
+
const body = await this.apiClient.get(`/api/v2/projects/${args.id}`)
|
|
24
|
+
await outputRecord(this, body.data)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import BaseCommand from '../../base-command.js'
|
|
2
|
+
import { collectPages } from '../../lib/pagination.js'
|
|
3
|
+
|
|
4
|
+
const columns = {
|
|
5
|
+
id: { header: 'ID' },
|
|
6
|
+
title: { header: 'Title' },
|
|
7
|
+
status: { header: 'Status' },
|
|
8
|
+
owner_id: { header: 'Owner' },
|
|
9
|
+
start_date: { header: 'Start' },
|
|
10
|
+
end_date: { header: 'End' },
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default class ProjectListCommand extends BaseCommand {
|
|
14
|
+
static description = 'List projects'
|
|
15
|
+
|
|
16
|
+
static examples = [
|
|
17
|
+
'<%= config.bin %> project list',
|
|
18
|
+
'<%= config.bin %> project list --output json',
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
static flags = {
|
|
22
|
+
...BaseCommand.baseFlags,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async run() {
|
|
26
|
+
const { flags } = await this.parse(ProjectListCommand)
|
|
27
|
+
const limit = flags.limit ?? 100
|
|
28
|
+
|
|
29
|
+
const query = {
|
|
30
|
+
limit: Math.min(limit, 100),
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const items = await collectPages(
|
|
34
|
+
this.apiClient.pageV2('/api/v2/projects', query),
|
|
35
|
+
limit,
|
|
36
|
+
)
|
|
37
|
+
await this.outputResults(items, columns)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
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 ProjectUpdateCommand extends BaseCommand {
|
|
8
|
+
static description =
|
|
9
|
+
'Update a project (v2 PATCH — only provided fields change)'
|
|
10
|
+
|
|
11
|
+
static examples = [
|
|
12
|
+
'<%= config.bin %> project update 7 --title "Relaunch"',
|
|
13
|
+
'<%= config.bin %> project update 7 --status closed',
|
|
14
|
+
'<%= config.bin %> project update 7 --owner 9',
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
static args = {
|
|
18
|
+
id: Args.integer({ required: true, description: 'Project ID' }),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static flags = {
|
|
22
|
+
...BaseCommand.baseFlags,
|
|
23
|
+
title: Flags.string({ description: 'Project title' }),
|
|
24
|
+
description: Flags.string({ description: 'Project description' }),
|
|
25
|
+
status: Flags.string({ description: 'Project status' }),
|
|
26
|
+
'start-date': Flags.string({ description: 'Start date (YYYY-MM-DD)' }),
|
|
27
|
+
'end-date': Flags.string({ description: 'End date (YYYY-MM-DD)' }),
|
|
28
|
+
owner: Flags.integer({ description: 'Owner (user) ID' }),
|
|
29
|
+
board: Flags.integer({ description: 'Board ID' }),
|
|
30
|
+
phase: Flags.integer({ description: 'Phase ID' }),
|
|
31
|
+
body: Flags.string({ description: 'Raw JSON body to merge (flags win)' }),
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async run() {
|
|
35
|
+
const { args, flags } = await this.parse(ProjectUpdateCommand)
|
|
36
|
+
|
|
37
|
+
const body = buildWriteBody({
|
|
38
|
+
typed: {
|
|
39
|
+
title: flags.title,
|
|
40
|
+
description: flags.description,
|
|
41
|
+
status: flags.status,
|
|
42
|
+
start_date: flags['start-date'],
|
|
43
|
+
end_date: flags['end-date'],
|
|
44
|
+
owner_id: flags.owner,
|
|
45
|
+
board_id: flags.board,
|
|
46
|
+
phase_id: flags.phase,
|
|
47
|
+
},
|
|
48
|
+
rawBody: flags.body,
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
if (Object.keys(body).length === 0) {
|
|
52
|
+
throw new CliError(
|
|
53
|
+
'Nothing to update — pass at least one field flag or --body',
|
|
54
|
+
{ exitCode: 64 },
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const res = await this.apiClient.patch(`/api/v2/projects/${args.id}`, {
|
|
59
|
+
body,
|
|
60
|
+
})
|
|
61
|
+
await outputRecord(this, res.data)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -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 StageGetCommand extends BaseCommand {
|
|
6
|
+
static description = 'Get a stage by ID'
|
|
7
|
+
|
|
8
|
+
static examples = [
|
|
9
|
+
'<%= config.bin %> stage get 5',
|
|
10
|
+
'<%= config.bin %> stage get 5 --output json',
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
static flags = {
|
|
14
|
+
...BaseCommand.baseFlags,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static args = {
|
|
18
|
+
id: Args.integer({ required: true, description: 'Stage ID' }),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async run() {
|
|
22
|
+
const { args } = await this.parse(StageGetCommand)
|
|
23
|
+
const body = await this.apiClient.get(`/api/v2/stages/${args.id}`)
|
|
24
|
+
await outputRecord(this, body.data)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
pipeline_id: { header: 'Pipeline' },
|
|
9
|
+
deal_probability: { header: 'Probability' },
|
|
10
|
+
order_nr: { header: 'Order' },
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default class StageListCommand extends BaseCommand {
|
|
14
|
+
static description = 'List stages'
|
|
15
|
+
|
|
16
|
+
static examples = [
|
|
17
|
+
'<%= config.bin %> stage list',
|
|
18
|
+
'<%= config.bin %> stage list --pipeline 1 --output json',
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
static flags = {
|
|
22
|
+
...BaseCommand.baseFlags,
|
|
23
|
+
pipeline: Flags.integer({ description: 'Filter by pipeline ID' }),
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async run() {
|
|
27
|
+
const { flags } = await this.parse(StageListCommand)
|
|
28
|
+
const limit = flags.limit ?? 100
|
|
29
|
+
|
|
30
|
+
const query = {
|
|
31
|
+
pipeline_id: flags.pipeline,
|
|
32
|
+
limit: Math.min(limit, 100),
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const items = await collectPages(
|
|
36
|
+
this.apiClient.pageV2('/api/v2/stages', query),
|
|
37
|
+
limit,
|
|
38
|
+
)
|
|
39
|
+
await this.outputResults(items, columns)
|
|
40
|
+
}
|
|
41
|
+
}
|