@nickchristensen/ppls 1.0.5 → 1.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/README.md +102 -70
- package/dist/base-command.d.ts +5 -4
- package/dist/commands/documents/download.d.ts +2 -2
- package/dist/commands/documents/download.js +24 -17
- package/dist/commands/documents/list.d.ts +31 -1
- package/dist/commands/documents/list.js +165 -1
- package/dist/flags/date-like.d.ts +4 -0
- package/dist/flags/date-like.js +11 -0
- package/dist/list-command.d.ts +11 -10
- package/dist/list-command.js +42 -13
- package/oclif.manifest.json +314 -86
- package/package.json +3 -1
- package/dist/paginated-command.d.ts +0 -49
- package/dist/paginated-command.js +0 -102
|
@@ -1,14 +1,178 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { dateLike } from '../../flags/date-like.js';
|
|
3
|
+
import { isDateOnly } from '../../helpers/date-utils.js';
|
|
1
4
|
import { ListCommand } from '../../list-command.js';
|
|
5
|
+
const DATE_RANGE_FLAGS = {
|
|
6
|
+
added: {
|
|
7
|
+
after: 'added-after',
|
|
8
|
+
before: 'added-before',
|
|
9
|
+
},
|
|
10
|
+
created: {
|
|
11
|
+
after: 'created-after',
|
|
12
|
+
before: 'created-before',
|
|
13
|
+
},
|
|
14
|
+
modified: {
|
|
15
|
+
after: 'modified-after',
|
|
16
|
+
before: 'modified-before',
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
const addDateRangeParams = (params, field, flags) => {
|
|
20
|
+
const { after, before } = DATE_RANGE_FLAGS[field];
|
|
21
|
+
const typedFlags = flags;
|
|
22
|
+
const afterValue = typedFlags[after];
|
|
23
|
+
const beforeValue = typedFlags[before];
|
|
24
|
+
if (afterValue) {
|
|
25
|
+
const key = isDateOnly(afterValue) ? `${field}__date__gte` : `${field}__gte`;
|
|
26
|
+
params[key] = afterValue;
|
|
27
|
+
}
|
|
28
|
+
if (beforeValue) {
|
|
29
|
+
const key = isDateOnly(beforeValue) ? `${field}__date__lte` : `${field}__lte`;
|
|
30
|
+
params[key] = beforeValue;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
2
33
|
export default class DocumentsList extends ListCommand {
|
|
3
34
|
static description = 'List documents';
|
|
4
35
|
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
36
|
+
static flags = {
|
|
37
|
+
...ListCommand.baseFlags,
|
|
38
|
+
'added-after': dateLike({
|
|
39
|
+
description: 'Filter by added date (YYYY-MM-DD) or datetime (ISO 8601) >= value',
|
|
40
|
+
exclusive: ['id-in'],
|
|
41
|
+
helpGroup: 'FILTER',
|
|
42
|
+
}),
|
|
43
|
+
'added-before': dateLike({
|
|
44
|
+
description: 'Filter by added date (YYYY-MM-DD) or datetime (ISO 8601) <= value',
|
|
45
|
+
exclusive: ['id-in'],
|
|
46
|
+
helpGroup: 'FILTER',
|
|
47
|
+
}),
|
|
48
|
+
correspondent: Flags.integer({
|
|
49
|
+
delimiter: ',',
|
|
50
|
+
description: 'Filter by correspondent ids (repeatable or comma-separated)',
|
|
51
|
+
exclusive: ['id-in'],
|
|
52
|
+
helpGroup: 'FILTER',
|
|
53
|
+
multiple: true,
|
|
54
|
+
}),
|
|
55
|
+
'correspondent-not': Flags.integer({
|
|
56
|
+
delimiter: ',',
|
|
57
|
+
description: 'Exclude correspondent ids (repeatable or comma-separated)',
|
|
58
|
+
exclusive: ['id-in'],
|
|
59
|
+
helpGroup: 'FILTER',
|
|
60
|
+
multiple: true,
|
|
61
|
+
}),
|
|
62
|
+
'created-after': dateLike({
|
|
63
|
+
description: 'Filter by created date (YYYY-MM-DD) or datetime (ISO 8601) >= value',
|
|
64
|
+
exclusive: ['id-in'],
|
|
65
|
+
helpGroup: 'FILTER',
|
|
66
|
+
}),
|
|
67
|
+
'created-before': dateLike({
|
|
68
|
+
description: 'Filter by created date (YYYY-MM-DD) or datetime (ISO 8601) <= value',
|
|
69
|
+
exclusive: ['id-in'],
|
|
70
|
+
helpGroup: 'FILTER',
|
|
71
|
+
}),
|
|
72
|
+
'document-type': Flags.integer({
|
|
73
|
+
delimiter: ',',
|
|
74
|
+
description: 'Filter by document type ids (repeatable or comma-separated)',
|
|
75
|
+
exclusive: ['id-in'],
|
|
76
|
+
helpGroup: 'FILTER',
|
|
77
|
+
multiple: true,
|
|
78
|
+
}),
|
|
79
|
+
'document-type-not': Flags.integer({
|
|
80
|
+
delimiter: ',',
|
|
81
|
+
description: 'Exclude document type ids (repeatable or comma-separated)',
|
|
82
|
+
exclusive: ['id-in'],
|
|
83
|
+
helpGroup: 'FILTER',
|
|
84
|
+
multiple: true,
|
|
85
|
+
}),
|
|
86
|
+
'modified-after': dateLike({
|
|
87
|
+
description: 'Filter by modified date (YYYY-MM-DD) or datetime (ISO 8601) >= value',
|
|
88
|
+
exclusive: ['id-in'],
|
|
89
|
+
helpGroup: 'FILTER',
|
|
90
|
+
}),
|
|
91
|
+
'modified-before': dateLike({
|
|
92
|
+
description: 'Filter by modified date (YYYY-MM-DD) or datetime (ISO 8601) <= value',
|
|
93
|
+
exclusive: ['id-in'],
|
|
94
|
+
helpGroup: 'FILTER',
|
|
95
|
+
}),
|
|
96
|
+
'no-correspondent': Flags.boolean({
|
|
97
|
+
description: 'Filter documents with no correspondent',
|
|
98
|
+
exclusive: ['correspondent', 'correspondent-not', 'id-in'],
|
|
99
|
+
helpGroup: 'FILTER',
|
|
100
|
+
}),
|
|
101
|
+
'no-document-type': Flags.boolean({
|
|
102
|
+
description: 'Filter documents with no document type',
|
|
103
|
+
exclusive: ['document-type', 'document-type-not', 'id-in'],
|
|
104
|
+
helpGroup: 'FILTER',
|
|
105
|
+
}),
|
|
106
|
+
'no-tag': Flags.boolean({
|
|
107
|
+
description: 'Filter documents with no tags',
|
|
108
|
+
exclusive: ['tag', 'tag-all', 'tag-not', 'id-in'],
|
|
109
|
+
helpGroup: 'FILTER',
|
|
110
|
+
}),
|
|
111
|
+
tag: Flags.integer({
|
|
112
|
+
delimiter: ',',
|
|
113
|
+
description: 'Filter by tag ids (repeatable or comma-separated, OR)',
|
|
114
|
+
exclusive: ['id-in'],
|
|
115
|
+
helpGroup: 'FILTER',
|
|
116
|
+
multiple: true,
|
|
117
|
+
}),
|
|
118
|
+
'tag-all': Flags.integer({
|
|
119
|
+
delimiter: ',',
|
|
120
|
+
description: 'Filter by tag ids (repeatable or comma-separated, AND)',
|
|
121
|
+
exclusive: ['id-in'],
|
|
122
|
+
helpGroup: 'FILTER',
|
|
123
|
+
multiple: true,
|
|
124
|
+
}),
|
|
125
|
+
'tag-not': Flags.integer({
|
|
126
|
+
delimiter: ',',
|
|
127
|
+
description: 'Exclude tag ids (repeatable or comma-separated)',
|
|
128
|
+
exclusive: ['id-in'],
|
|
129
|
+
helpGroup: 'FILTER',
|
|
130
|
+
multiple: true,
|
|
131
|
+
}),
|
|
132
|
+
};
|
|
5
133
|
listPath = '/api/documents/';
|
|
6
134
|
tableAttrs = ['id', 'title', 'created', 'added', 'correspondent', 'document_type', 'tags'];
|
|
135
|
+
extraListFlags(flags) {
|
|
136
|
+
const typedFlags = flags;
|
|
137
|
+
return {
|
|
138
|
+
'added-after': typedFlags['added-after'],
|
|
139
|
+
'added-before': typedFlags['added-before'],
|
|
140
|
+
correspondent: typedFlags.correspondent,
|
|
141
|
+
'correspondent-not': typedFlags['correspondent-not'],
|
|
142
|
+
'created-after': typedFlags['created-after'],
|
|
143
|
+
'created-before': typedFlags['created-before'],
|
|
144
|
+
'document-type': typedFlags['document-type'],
|
|
145
|
+
'document-type-not': typedFlags['document-type-not'],
|
|
146
|
+
'modified-after': typedFlags['modified-after'],
|
|
147
|
+
'modified-before': typedFlags['modified-before'],
|
|
148
|
+
'no-correspondent': typedFlags['no-correspondent'],
|
|
149
|
+
'no-document-type': typedFlags['no-document-type'],
|
|
150
|
+
'no-tag': typedFlags['no-tag'],
|
|
151
|
+
tag: typedFlags.tag,
|
|
152
|
+
'tag-all': typedFlags['tag-all'],
|
|
153
|
+
'tag-not': typedFlags['tag-not'],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
7
156
|
listParams(flags) {
|
|
157
|
+
const typedFlags = flags;
|
|
8
158
|
const params = super.listParams(flags);
|
|
9
159
|
delete params.name__icontains;
|
|
10
|
-
|
|
160
|
+
/* eslint-disable camelcase -- API uses double-underscore field names. */
|
|
11
161
|
params.title__icontains = flags['name-contains'];
|
|
162
|
+
params.tags__id__in = typedFlags.tag;
|
|
163
|
+
params.tags__id__all = typedFlags['tag-all'];
|
|
164
|
+
params.tags__id__none = typedFlags['tag-not'];
|
|
165
|
+
params.is_tagged = typedFlags['no-tag'] ? 'false' : undefined;
|
|
166
|
+
params.correspondent__id__in = typedFlags.correspondent;
|
|
167
|
+
params.correspondent__id__none = typedFlags['correspondent-not'];
|
|
168
|
+
params.correspondent__isnull = typedFlags['no-correspondent'] ? 'true' : undefined;
|
|
169
|
+
params.document_type__id__in = typedFlags['document-type'];
|
|
170
|
+
params.document_type__id__none = typedFlags['document-type-not'];
|
|
171
|
+
params.document_type__isnull = typedFlags['no-document-type'] ? 'true' : undefined;
|
|
172
|
+
/* eslint-enable camelcase */
|
|
173
|
+
addDateRangeParams(params, 'added', typedFlags);
|
|
174
|
+
addDateRangeParams(params, 'created', typedFlags);
|
|
175
|
+
addDateRangeParams(params, 'modified', typedFlags);
|
|
12
176
|
return params;
|
|
13
177
|
}
|
|
14
178
|
plainTemplate(document) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { isDateOnly, isDateTime } from '../helpers/date-utils.js';
|
|
3
|
+
export const dateLike = Flags.custom({
|
|
4
|
+
helpValue: 'YYYY-MM-DD|ISO-8601',
|
|
5
|
+
async parse(input) {
|
|
6
|
+
if (isDateOnly(input) || isDateTime(input)) {
|
|
7
|
+
return input;
|
|
8
|
+
}
|
|
9
|
+
throw new Error('Use YYYY-MM-DD or an ISO 8601 datetime.');
|
|
10
|
+
},
|
|
11
|
+
});
|
package/dist/list-command.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { ApiFlags } from './base-command.js';
|
|
2
|
+
import { BaseCommand } from './base-command.js';
|
|
2
3
|
import { type TableColumn, type TableRow } from './helpers/table.js';
|
|
3
|
-
import { PaginatedCommand } from './paginated-command.js';
|
|
4
4
|
type ListCommandFlags = ApiFlags & {
|
|
5
|
-
'id-in'?: string;
|
|
5
|
+
'id-in'?: string[];
|
|
6
6
|
'name-contains'?: string;
|
|
7
7
|
page?: number;
|
|
8
|
-
'page-size'
|
|
8
|
+
'page-size': number;
|
|
9
9
|
sort?: string;
|
|
10
10
|
};
|
|
11
11
|
type ListOutputFlags = ListCommandFlags & {
|
|
@@ -14,13 +14,13 @@ type ListOutputFlags = ListCommandFlags & {
|
|
|
14
14
|
table?: boolean;
|
|
15
15
|
};
|
|
16
16
|
type TableColumnInput = string | TableColumn;
|
|
17
|
-
export declare abstract class ListCommand<TRaw extends TableRow = TableRow, TOutput extends TableRow = TRaw> extends
|
|
17
|
+
export declare abstract class ListCommand<TRaw extends TableRow = TableRow, TOutput extends TableRow = TRaw> extends BaseCommand {
|
|
18
18
|
static baseFlags: {
|
|
19
|
-
'id-in': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
+
'id-in': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
20
|
'name-contains': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
-
sort: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
22
21
|
page: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
23
|
-
'page-size': import("@oclif/core/interfaces").OptionFlag<number
|
|
22
|
+
'page-size': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
23
|
+
sort: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
24
24
|
'date-format': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
25
25
|
header: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
26
26
|
hostname: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -30,20 +30,21 @@ export declare abstract class ListCommand<TRaw extends TableRow = TableRow, TOut
|
|
|
30
30
|
};
|
|
31
31
|
protected abstract listPath: string;
|
|
32
32
|
protected abstract tableAttrs: TableColumnInput[];
|
|
33
|
+
protected extraListFlags(_flags: Record<string, unknown>): Record<string, unknown>;
|
|
33
34
|
protected fetchListResults<T>(options: {
|
|
34
35
|
flags: ListCommandFlags;
|
|
35
|
-
params?: Record<string, number | string | undefined>;
|
|
36
|
+
params?: Record<string, number | number[] | string | string[] | undefined>;
|
|
36
37
|
path: string;
|
|
37
38
|
}): Promise<T[]>;
|
|
38
|
-
protected listParams(flags: ListCommandFlags): Record<string, number | string | undefined>;
|
|
39
|
+
protected listParams(flags: ListCommandFlags): Record<string, number | number[] | string | string[] | undefined>;
|
|
39
40
|
protected abstract plainTemplate(item: TOutput): null | string | undefined;
|
|
40
41
|
protected renderListOutput(options: {
|
|
41
42
|
flags: ListOutputFlags;
|
|
42
43
|
results: TOutput[];
|
|
43
44
|
}): void;
|
|
44
45
|
run(): Promise<TOutput[]>;
|
|
45
|
-
protected shouldAutoPaginate(flags: ListCommandFlags): boolean;
|
|
46
46
|
protected transformResult(result: TRaw): TOutput;
|
|
47
47
|
protected transformResults(results: TRaw[]): TOutput[];
|
|
48
|
+
private buildListUrl;
|
|
48
49
|
}
|
|
49
50
|
export {};
|
package/dist/list-command.js
CHANGED
|
@@ -1,22 +1,40 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
|
+
import { BaseCommand } from './base-command.js';
|
|
2
3
|
import { createValueFormatter } from './helpers/table.js';
|
|
3
|
-
|
|
4
|
-
export class ListCommand extends PaginatedCommand {
|
|
4
|
+
export class ListCommand extends BaseCommand {
|
|
5
5
|
static baseFlags = {
|
|
6
|
-
...
|
|
6
|
+
...BaseCommand.baseFlags,
|
|
7
7
|
'id-in': Flags.string({
|
|
8
|
-
|
|
8
|
+
delimiter: ',',
|
|
9
|
+
description: 'Filter by id list (repeatable or comma-separated)',
|
|
9
10
|
exclusive: ['name-contains'],
|
|
11
|
+
helpGroup: 'FILTER',
|
|
12
|
+
multiple: true,
|
|
10
13
|
}),
|
|
11
14
|
'name-contains': Flags.string({
|
|
12
15
|
description: 'Filter by name substring',
|
|
13
16
|
exclusive: ['id-in'],
|
|
17
|
+
helpGroup: 'FILTER',
|
|
18
|
+
}),
|
|
19
|
+
page: Flags.integer({
|
|
20
|
+
dependsOn: ['page-size'],
|
|
21
|
+
description: 'Page number to fetch',
|
|
22
|
+
min: 1,
|
|
23
|
+
}),
|
|
24
|
+
'page-size': Flags.integer({
|
|
25
|
+
default: async ({ flags }) => (flags.page === undefined ? Number.MAX_SAFE_INTEGER : undefined),
|
|
26
|
+
defaultHelp: async () => 'disable pagination, all results',
|
|
27
|
+
description: 'Number of results per page',
|
|
28
|
+
min: 1,
|
|
14
29
|
}),
|
|
15
30
|
sort: Flags.string({ description: 'Sort results by the provided field' }),
|
|
16
31
|
};
|
|
32
|
+
extraListFlags(_flags) {
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
17
35
|
async fetchListResults(options) {
|
|
18
36
|
const { flags, params = {}, path } = options;
|
|
19
|
-
const url = this.
|
|
37
|
+
const url = this.buildListUrl({
|
|
20
38
|
flags,
|
|
21
39
|
params: {
|
|
22
40
|
ordering: flags.sort,
|
|
@@ -24,11 +42,14 @@ export class ListCommand extends PaginatedCommand {
|
|
|
24
42
|
},
|
|
25
43
|
path,
|
|
26
44
|
});
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
flags,
|
|
30
|
-
|
|
31
|
-
}
|
|
45
|
+
const spinner = this.startSpinner(`Fetching ${url.pathname}`);
|
|
46
|
+
try {
|
|
47
|
+
const payload = await this.fetchJson(url, flags.token, flags.headers);
|
|
48
|
+
return payload.results ?? [];
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
spinner?.stop();
|
|
52
|
+
}
|
|
32
53
|
}
|
|
33
54
|
listParams(flags) {
|
|
34
55
|
return {
|
|
@@ -67,6 +88,7 @@ export class ListCommand extends PaginatedCommand {
|
|
|
67
88
|
'page-size': flags['page-size'],
|
|
68
89
|
sort: flags.sort,
|
|
69
90
|
token: apiFlags.token,
|
|
91
|
+
...this.extraListFlags(flags),
|
|
70
92
|
};
|
|
71
93
|
const outputFlags = {
|
|
72
94
|
...listFlags,
|
|
@@ -86,13 +108,20 @@ export class ListCommand extends PaginatedCommand {
|
|
|
86
108
|
});
|
|
87
109
|
return results;
|
|
88
110
|
}
|
|
89
|
-
shouldAutoPaginate(flags) {
|
|
90
|
-
return flags.page === undefined && flags['page-size'] === undefined;
|
|
91
|
-
}
|
|
92
111
|
transformResult(result) {
|
|
93
112
|
return result;
|
|
94
113
|
}
|
|
95
114
|
transformResults(results) {
|
|
96
115
|
return results.map((result) => this.transformResult(result));
|
|
97
116
|
}
|
|
117
|
+
buildListUrl(options) {
|
|
118
|
+
const { flags, params = {}, path } = options;
|
|
119
|
+
const { page } = flags;
|
|
120
|
+
const pageSize = flags['page-size'];
|
|
121
|
+
return this.buildApiUrl(flags.hostname, path, {
|
|
122
|
+
...params,
|
|
123
|
+
page,
|
|
124
|
+
'page_size': pageSize,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
98
127
|
}
|