@danypops/tickets 0.5.0 → 0.5.1
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/package.json +1 -1
- package/src/adapters/jira.ts +18 -0
- package/src/application/service.ts +8 -0
- package/src/cli/index.ts +9 -0
- package/src/daemon/ops.ts +4 -0
- package/src/daemon/server.ts +1 -0
- package/src/ports/repository.ts +12 -0
- package/src/vehicle/tickets-vehicle.ts +7 -0
package/package.json
CHANGED
package/src/adapters/jira.ts
CHANGED
|
@@ -295,6 +295,24 @@ export class JiraRepository {
|
|
|
295
295
|
return result.jql;
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
+
/**
|
|
299
|
+
* BoardFilterDiscoverable -- resolves a board's own real base scope: its saved
|
|
300
|
+
* filter's JQL, via `GET /rest/agile/1.0/board/{boardId}/configuration` (filter
|
|
301
|
+
* id) then `GET /rest/api/2/filter/{id}` (that filter's JQL). This is the actual
|
|
302
|
+
* project/query scope a board tracks -- assuming a board scopes to a single named
|
|
303
|
+
* project (e.g. matching a URL path segment) is a guess, not a fact; a project can
|
|
304
|
+
* be renamed or a board's filter can span multiple projects, so this is always
|
|
305
|
+
* resolved from Jira, never inferred from a project key string.
|
|
306
|
+
*/
|
|
307
|
+
async discoverBoardFilterJql(boardId: number): Promise<string> {
|
|
308
|
+
const config = await this.call<{ filter?: { id?: string } }>(() => this.agileClient().board.getConfiguration({ boardId }));
|
|
309
|
+
const filterId = config?.filter?.id;
|
|
310
|
+
if (!filterId) throw new Error(`jira: board ${boardId} has no configured filter`);
|
|
311
|
+
const filter = await this.call<{ jql?: string }>(() => this.client.filters.getFilter({ id: Number(filterId) }));
|
|
312
|
+
if (!filter?.jql) throw new Error(`jira: filter ${filterId} (board ${boardId}'s own filter) has no JQL`);
|
|
313
|
+
return filter.jql;
|
|
314
|
+
}
|
|
315
|
+
|
|
298
316
|
private agileClientInstance?: AgileClient;
|
|
299
317
|
|
|
300
318
|
private agileClient(): AgileClient {
|
|
@@ -8,6 +8,7 @@ import type { Comment, CreateInput, Issue, ListFilter, UpdateInput } from "../do
|
|
|
8
8
|
import { parseRef } from "../domain/issue.js";
|
|
9
9
|
import type { Template } from "../domain/template.js";
|
|
10
10
|
import {
|
|
11
|
+
hasBoardFilterDiscovery,
|
|
11
12
|
hasBoardQuickFilterDiscovery,
|
|
12
13
|
hasComments,
|
|
13
14
|
hasFieldDiscovery,
|
|
@@ -133,4 +134,11 @@ export class TicketService {
|
|
|
133
134
|
if (!hasBoardQuickFilterDiscovery(repo)) throw new NotSupportedError(backend, "board quick filter discovery");
|
|
134
135
|
return repo.discoverBoardQuickFilterJql(boardId, quickFilterId);
|
|
135
136
|
}
|
|
137
|
+
|
|
138
|
+
/** Resolves a Jira board's own real base scope (its saved filter's JQL) -- never assume a board tracks one named project. */
|
|
139
|
+
async discoverBoardFilterJql(backend: string, boardId: number): Promise<string> {
|
|
140
|
+
const repo = this.repo(backend);
|
|
141
|
+
if (!hasBoardFilterDiscovery(repo)) throw new NotSupportedError(backend, "board filter discovery");
|
|
142
|
+
return repo.discoverBoardFilterJql(boardId);
|
|
143
|
+
}
|
|
136
144
|
}
|
package/src/cli/index.ts
CHANGED
|
@@ -230,6 +230,15 @@ discoverCmd
|
|
|
230
230
|
);
|
|
231
231
|
});
|
|
232
232
|
|
|
233
|
+
discoverCmd
|
|
234
|
+
.command("board_filter")
|
|
235
|
+
.description("resolve a Jira board's own real base scope -- its saved filter's JQL -- instead of assuming it tracks one named project")
|
|
236
|
+
.requiredOption("-b, --backend <name>", "backend name")
|
|
237
|
+
.requiredOption("--board <id>", "board id", (v) => Number.parseInt(v, 10))
|
|
238
|
+
.action(async (opts) => {
|
|
239
|
+
await withClient((client) => client.call("discover.board_filter", { backend: opts.backend, boardId: opts.board }));
|
|
240
|
+
});
|
|
241
|
+
|
|
233
242
|
const queryCmd = program.command("query").description("save and run named raw backend queries (Jira JQL) -- e.g. a board's sprint or backlog view");
|
|
234
243
|
|
|
235
244
|
queryCmd
|
package/src/daemon/ops.ts
CHANGED
|
@@ -30,6 +30,7 @@ export type TicketOperation =
|
|
|
30
30
|
| "discover.statuses"
|
|
31
31
|
| "discover.template"
|
|
32
32
|
| "discover.board_quickfilter"
|
|
33
|
+
| "discover.board_filter"
|
|
33
34
|
| "query.save"
|
|
34
35
|
| "query.list"
|
|
35
36
|
| "query.remove"
|
|
@@ -57,6 +58,7 @@ export interface TicketOpInputs extends Record<TicketOperation, unknown> {
|
|
|
57
58
|
"discover.statuses": { backend: string };
|
|
58
59
|
"discover.template": { backend: string; project: string; issueType: string; sampleSize?: number };
|
|
59
60
|
"discover.board_quickfilter": { backend: string; boardId: number; quickFilterId: number };
|
|
61
|
+
"discover.board_filter": { backend: string; boardId: number };
|
|
60
62
|
"query.save": { name: string; backend: string; query: string; description?: string };
|
|
61
63
|
"query.list": Record<string, never>;
|
|
62
64
|
"query.remove": { name: string };
|
|
@@ -85,6 +87,7 @@ export interface TicketOpOutputs extends Record<TicketOperation, unknown> {
|
|
|
85
87
|
"discover.statuses": { mappings: Record<string, string> };
|
|
86
88
|
"discover.template": { template: Template | null };
|
|
87
89
|
"discover.board_quickfilter": { jql: string };
|
|
90
|
+
"discover.board_filter": { jql: string };
|
|
88
91
|
"query.save": { query: SavedQuery };
|
|
89
92
|
"query.list": { queries: SavedQuery[] };
|
|
90
93
|
"query.remove": { removed: boolean };
|
|
@@ -113,6 +116,7 @@ export const TICKET_OPERATIONS: TicketOperation[] = [
|
|
|
113
116
|
"discover.statuses",
|
|
114
117
|
"discover.template",
|
|
115
118
|
"discover.board_quickfilter",
|
|
119
|
+
"discover.board_filter",
|
|
116
120
|
"query.save",
|
|
117
121
|
"query.list",
|
|
118
122
|
"query.remove",
|
package/src/daemon/server.ts
CHANGED
|
@@ -94,6 +94,7 @@ export const TICKET_OP_HANDLERS: { [Op in TicketOperation]: Handler<Op> } = {
|
|
|
94
94
|
"discover.board_quickfilter": async (deps, input) => ({
|
|
95
95
|
jql: await deps.service.discoverBoardQuickFilterJql(input.backend, input.boardId, input.quickFilterId),
|
|
96
96
|
}),
|
|
97
|
+
"discover.board_filter": async (deps, input) => ({ jql: await deps.service.discoverBoardFilterJql(input.backend, input.boardId) }),
|
|
97
98
|
"query.save": async (deps, input) => ({ query: deps.queries.save(input.name, input.backend, input.query, input.description) }),
|
|
98
99
|
"query.list": async (deps) => ({ queries: deps.queries.list() }),
|
|
99
100
|
"query.remove": async (deps, input) => ({ removed: deps.queries.remove(input.name) }),
|
package/src/ports/repository.ts
CHANGED
|
@@ -88,3 +88,15 @@ export interface BoardQuickFilterDiscoverable {
|
|
|
88
88
|
export function hasBoardQuickFilterDiscovery(repo: IssueRepository): repo is IssueRepository & BoardQuickFilterDiscoverable {
|
|
89
89
|
return typeof (repo as Partial<BoardQuickFilterDiscoverable>).discoverBoardQuickFilterJql === "function";
|
|
90
90
|
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Optional capability — resolves a Jira board's own real base scope (its saved
|
|
94
|
+
* filter's JQL) rather than assuming a board tracks one named project. Jira only.
|
|
95
|
+
*/
|
|
96
|
+
export interface BoardFilterDiscoverable {
|
|
97
|
+
discoverBoardFilterJql(boardId: number): Promise<string>;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function hasBoardFilterDiscovery(repo: IssueRepository): repo is IssueRepository & BoardFilterDiscoverable {
|
|
101
|
+
return typeof (repo as Partial<BoardFilterDiscoverable>).discoverBoardFilterJql === "function";
|
|
102
|
+
}
|
|
@@ -112,6 +112,13 @@ const OPERATIONS: readonly OperationSpec[] = [
|
|
|
112
112
|
properties: { backend: stringProp, boardId: numberProp, quickFilterId: numberProp },
|
|
113
113
|
required: ["backend", "boardId", "quickFilterId"],
|
|
114
114
|
},
|
|
115
|
+
{
|
|
116
|
+
action: "discover.board_filter",
|
|
117
|
+
description: "Resolves a Jira board's own real base scope -- its saved filter's JQL -- rather than assuming it tracks one named project.",
|
|
118
|
+
effect: "read",
|
|
119
|
+
properties: { backend: stringProp, boardId: numberProp },
|
|
120
|
+
required: ["backend", "boardId"],
|
|
121
|
+
},
|
|
115
122
|
{
|
|
116
123
|
action: "query.save",
|
|
117
124
|
description: "Saves a raw backend query (Jira JQL) under a name, so it can be run again later without retyping it -- e.g. a board's sprint or backlog view.",
|