- The web pings the live connection, reconnects when it goes quiet and reloads what each screen shows after reconnecting. - Dropping a card on a column it cannot go to puts it back. - Owners and admins get a notice when a proposal arrives. - The proposal limit lives in Redis, so it holds across instances. - REST tells whether I voted; pasted images leave the attachment list. - Deleting a project removes its Git integration; no lint warnings left. - Reactions and the blocked badge are components of their own. - Docs: what is done and what is left (chapter 10), Word and PDF. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1414 lines
29 KiB
GraphQL
1414 lines
29 KiB
GraphQL
# ------------------------------------------------------
|
||
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
|
||
# ------------------------------------------------------
|
||
|
||
type Activity {
|
||
actor: UserRef!
|
||
id: ID!
|
||
issueId: ID
|
||
issueKey: String
|
||
occurredAt: String!
|
||
projectId: ID!
|
||
summary: String!
|
||
type: String!
|
||
}
|
||
|
||
input AddChecklistItemInput {
|
||
issueId: ID!
|
||
text: String!
|
||
}
|
||
|
||
type AreaOption {
|
||
archived: Boolean!
|
||
id: String!
|
||
name: String!
|
||
}
|
||
|
||
input AreaOptionInput {
|
||
archived: Boolean! = false
|
||
id: String!
|
||
name: String!
|
||
}
|
||
|
||
"""
|
||
A file on a ticket. Download it from the REST API: GET /api/issues/{issueId}/attachments/{id}
|
||
"""
|
||
type Attachment {
|
||
"""The comment it was attached to, if any"""
|
||
commentId: ID
|
||
fileName: String!
|
||
id: ID!
|
||
mimeType: String!
|
||
|
||
"""Bytes"""
|
||
size: Int!
|
||
uploadedAt: String!
|
||
uploadedBy: UserRef!
|
||
}
|
||
|
||
"""When this happens to a ticket, if it is like this, do that"""
|
||
type Automation {
|
||
action: AutomationActionSpec!
|
||
active: Boolean!
|
||
conditions: AutomationConditions!
|
||
createdAt: String!
|
||
createdBy: UserRef!
|
||
id: ID!
|
||
|
||
"""Why the last run did nothing"""
|
||
lastError: String
|
||
lastRunAt: String
|
||
name: String!
|
||
projectId: ID!
|
||
runs: Int!
|
||
trigger: AutomationTriggerSpec!
|
||
}
|
||
|
||
"""What a rule does to the ticket"""
|
||
enum AutomationAction {
|
||
ADD_COMMENT
|
||
ADD_LABEL
|
||
ADD_TO_ACTIVE_SPRINT
|
||
ASSIGN
|
||
ASSIGN_REPORTER
|
||
SET_PRIORITY
|
||
SET_STATUS
|
||
}
|
||
|
||
input AutomationActionInput {
|
||
kind: AutomationAction!
|
||
value: String
|
||
}
|
||
|
||
type AutomationActionSpec {
|
||
kind: AutomationAction!
|
||
|
||
"""Status, member id, label, priority or comment text"""
|
||
value: String
|
||
}
|
||
|
||
type AutomationConditions {
|
||
areas: [String!]
|
||
label: String
|
||
priorities: [IssuePriority!]
|
||
types: [String!]
|
||
}
|
||
|
||
input AutomationConditionsInput {
|
||
areas: [String!]
|
||
label: String
|
||
priorities: [IssuePriority!]
|
||
types: [String!]
|
||
}
|
||
|
||
"""What starts a rule"""
|
||
enum AutomationTrigger {
|
||
COMMENT_ADDED
|
||
ISSUE_ASSIGNED
|
||
ISSUE_CREATED
|
||
PULL_REQUEST_MERGED
|
||
STATUS_CHANGED
|
||
}
|
||
|
||
input AutomationTriggerInput {
|
||
kind: AutomationTrigger!
|
||
status: String
|
||
}
|
||
|
||
type AutomationTriggerSpec {
|
||
kind: AutomationTrigger!
|
||
|
||
"""STATUS_CHANGED: only into this status"""
|
||
status: String
|
||
}
|
||
|
||
type BoardColumn {
|
||
issues: [Issue!]!
|
||
status: String!
|
||
total: Int!
|
||
}
|
||
|
||
type BulkIssueResult {
|
||
error: String
|
||
issueId: ID!
|
||
ok: Boolean!
|
||
}
|
||
|
||
type BulkResult {
|
||
results: [BulkIssueResult!]!
|
||
updated: Int!
|
||
}
|
||
|
||
input BulkUpdateIssuesInput {
|
||
addLabels: [String!]
|
||
assigneeId: ID
|
||
issueIds: [ID!]!
|
||
|
||
"""Out of their release"""
|
||
noRelease: Boolean
|
||
|
||
"""Back to the backlog"""
|
||
noSprint: Boolean
|
||
priority: IssuePriority
|
||
releaseId: ID
|
||
sprintId: ID
|
||
status: String
|
||
|
||
"""Unassign everything"""
|
||
unassign: Boolean
|
||
}
|
||
|
||
input ChangeIssueStatusInput {
|
||
issueId: ID!
|
||
|
||
"""Required by statuses with requiresReason"""
|
||
reason: String
|
||
|
||
"""A status id"""
|
||
status: String!
|
||
}
|
||
|
||
"""A step inside a ticket that does not deserve a ticket of its own"""
|
||
type ChecklistItem {
|
||
done: Boolean!
|
||
id: ID!
|
||
text: String!
|
||
}
|
||
|
||
"""How far the tickets hanging from one have got"""
|
||
type ChildProgress {
|
||
"""Done or cancelled"""
|
||
finished: Int!
|
||
finishedStoryPoints: Int!
|
||
percent: Int!
|
||
storyPoints: Int!
|
||
total: Int!
|
||
}
|
||
|
||
"""
|
||
Writes are acknowledged immediately; the read model catches up asynchronously. Wait for a subscription event with version >= this version to see the change.
|
||
"""
|
||
type CommandResult {
|
||
id: ID!
|
||
key: String
|
||
version: Int!
|
||
}
|
||
|
||
type Comment {
|
||
author: UserRef!
|
||
|
||
"""Empty on a deleted comment kept for its replies"""
|
||
body: String!
|
||
createdAt: String!
|
||
|
||
"""Deleted, but kept (without its text) while it has replies"""
|
||
deleted: Boolean!
|
||
editedAt: String
|
||
id: ID!
|
||
issueId: ID!
|
||
|
||
"""In the order they are offered; only the ones somebody chose"""
|
||
reactions: [CommentReaction!]!
|
||
|
||
"""The top-level comment it answers; null for a top-level one"""
|
||
replyTo: ID
|
||
version: Int!
|
||
}
|
||
|
||
type CommentChange {
|
||
action: String!
|
||
actorId: ID!
|
||
|
||
"""Null when deleted"""
|
||
comment: Comment
|
||
commentId: ID!
|
||
issueId: ID!
|
||
version: Int!
|
||
}
|
||
|
||
type CommentPage {
|
||
items: [Comment!]!
|
||
total: Int!
|
||
}
|
||
|
||
"""One emoji on a comment"""
|
||
type CommentReaction {
|
||
emoji: String!
|
||
|
||
"""Who chose it"""
|
||
users: [UserRef!]!
|
||
}
|
||
|
||
type CountByAssignee {
|
||
count: Int!
|
||
|
||
"""Null = unassigned"""
|
||
user: UserRef
|
||
}
|
||
|
||
type CountByKey {
|
||
count: Int!
|
||
key: String!
|
||
}
|
||
|
||
type CountByStatus {
|
||
count: Int!
|
||
|
||
"""Status id"""
|
||
key: String!
|
||
}
|
||
|
||
input CreateAutomationInput {
|
||
action: AutomationActionInput!
|
||
conditions: AutomationConditionsInput
|
||
name: String!
|
||
projectId: ID!
|
||
trigger: AutomationTriggerInput!
|
||
}
|
||
|
||
input CreateIssueInput {
|
||
"""An area id"""
|
||
area: String
|
||
assigneeId: ID
|
||
description: String
|
||
dueDate: String
|
||
|
||
"""Figma frame for UI changes"""
|
||
figmaUrl: String
|
||
labels: [String!]
|
||
|
||
"""Epic or ticket of the same project the new one breaks down"""
|
||
parentId: ID
|
||
priority: IssuePriority
|
||
projectId: ID!
|
||
releaseId: ID
|
||
|
||
"""When work should start (roadmap), YYYY-MM-DD"""
|
||
startDate: String
|
||
storyPoints: Int
|
||
title: String!
|
||
|
||
"""A ticket type id"""
|
||
type: String!
|
||
}
|
||
|
||
input CreateProjectInput {
|
||
description: String
|
||
|
||
"""Readable by anyone, also without signing in"""
|
||
isPublic: Boolean
|
||
|
||
"""2-10 chars, e.g. "TS" → tickets TS-1, TS-2..."""
|
||
key: String!
|
||
name: String!
|
||
}
|
||
|
||
input CreateReleaseInput {
|
||
description: String
|
||
|
||
"""e.g. v1.4.0"""
|
||
name: String!
|
||
projectId: ID!
|
||
targetDate: String
|
||
}
|
||
|
||
input CreateSprintInput {
|
||
"""YYYY-MM-DD, included"""
|
||
endDate: String!
|
||
goal: String
|
||
name: String!
|
||
projectId: ID!
|
||
|
||
"""YYYY-MM-DD"""
|
||
startDate: String!
|
||
}
|
||
|
||
input CreateWebhookInput {
|
||
"""Event names, family.* or *"""
|
||
events: [String!]!
|
||
projectId: ID!
|
||
url: String!
|
||
}
|
||
|
||
type CreatedResolved {
|
||
created: Int!
|
||
resolved: Int!
|
||
|
||
"""Monday of the week (YYYY-MM-DD)"""
|
||
week: String!
|
||
}
|
||
|
||
type CumulativeFlow {
|
||
dates: [String!]!
|
||
series: [FlowSeries!]!
|
||
}
|
||
|
||
"""A commit, branch or pull request that mentions the ticket"""
|
||
type DevLink {
|
||
at: String!
|
||
author: String
|
||
kind: DevLinkKind!
|
||
|
||
"""github, gitea or gitlab"""
|
||
provider: String!
|
||
|
||
"""Short SHA, branch or PR number"""
|
||
ref: String!
|
||
|
||
"""The project's name for the repository it came from"""
|
||
repository: String
|
||
state: DevLinkState
|
||
title: String!
|
||
url: String!
|
||
}
|
||
|
||
enum DevLinkKind {
|
||
BRANCH
|
||
COMMIT
|
||
PULL_REQUEST
|
||
}
|
||
|
||
enum DevLinkState {
|
||
CLOSED
|
||
MERGED
|
||
OPEN
|
||
}
|
||
|
||
type DurationStats {
|
||
averageDays: Float
|
||
count: Int!
|
||
|
||
"""85% of tickets take at most this long"""
|
||
p85Days: Float
|
||
}
|
||
|
||
"""A top-level ticket that groups others, with their progress"""
|
||
type Epic {
|
||
issue: Issue!
|
||
progress: ChildProgress!
|
||
}
|
||
|
||
type FlowSeries {
|
||
counts: [Int!]!
|
||
status: String!
|
||
}
|
||
|
||
"""
|
||
Webhooks from GitHub, Gitea or GitLab: commits, branches and pull requests naming a ticket key are linked to it
|
||
"""
|
||
type GitIntegration {
|
||
"""On while there is at least one repository"""
|
||
enabled: Boolean!
|
||
repositories: [GitRepository!]!
|
||
|
||
"""Path of the webhook on this API, the same for every repository"""
|
||
webhookPath: String!
|
||
}
|
||
|
||
"""A repository whose webhooks the project accepts"""
|
||
type GitRepository {
|
||
createdAt: String!
|
||
id: ID!
|
||
|
||
"""The project's name for it: api, web, admin…"""
|
||
name: String!
|
||
|
||
"""Webhook secret of this repository; only owners and admins see it"""
|
||
secret: String
|
||
}
|
||
|
||
type Issue {
|
||
"""Statuses this ticket can move to next (project's workflow)"""
|
||
allowedTransitions: [String!]!
|
||
|
||
"""An area id"""
|
||
area: String
|
||
assignee: UserRef
|
||
attachments: [Attachment!]!
|
||
|
||
"""
|
||
Open tickets it waits for: the ones that block it and the ones it depends on
|
||
"""
|
||
blockedBy: [IssueParent!]!
|
||
checklist: [ChecklistItem!]!
|
||
commentCount: Int!
|
||
comments(limit: Int! = 50, offset: Int! = 0): CommentPage!
|
||
createdAt: String!
|
||
description: String
|
||
|
||
"""Commits, branches and pull requests that mention it"""
|
||
devLinks: [DevLink!]!
|
||
dueDate: String
|
||
figmaUrl: String
|
||
|
||
"""History of this ticket"""
|
||
history(limit: Int! = 50): [Activity!]!
|
||
id: ID!
|
||
|
||
"""Human key, e.g. TS-42"""
|
||
key: String!
|
||
labels: [String!]!
|
||
|
||
"""Linked tickets I can see"""
|
||
linkedIssues: [Issue!]!
|
||
links: [IssueLink!]!
|
||
number: Int!
|
||
|
||
"""The epic or ticket this one breaks down"""
|
||
parent: IssueParent
|
||
priority: IssuePriority!
|
||
projectId: ID!
|
||
projectKey: String!
|
||
|
||
"""
|
||
Place in the backlog and in its board column (ascending, compare as text)
|
||
"""
|
||
rank: String!
|
||
release: ReleaseRef
|
||
reporter: UserRef!
|
||
sprint: SprintRef
|
||
|
||
"""When work should start (roadmap)"""
|
||
startDate: String
|
||
|
||
"""A status id of the project's settings"""
|
||
status: String!
|
||
statusCategory: StatusCategory!
|
||
storyPoints: Int
|
||
title: String!
|
||
|
||
"""In the trash since then; null when it is not"""
|
||
trashedAt: String
|
||
|
||
"""A ticket type id of the project's settings"""
|
||
type: String!
|
||
updatedAt: String!
|
||
version: Int!
|
||
|
||
"""Whether I voted for it"""
|
||
voted: Boolean!
|
||
|
||
"""How many want it done"""
|
||
votes: Int!
|
||
|
||
"""People following the ticket"""
|
||
watchers: [UserRef!]!
|
||
}
|
||
|
||
type IssueChange {
|
||
action: String!
|
||
actorId: ID!
|
||
issue: Issue!
|
||
version: Int!
|
||
}
|
||
|
||
input IssueFilterInput {
|
||
areas: [String!]
|
||
assigneeId: ID
|
||
categories: [StatusCategory!]
|
||
|
||
"""Due on or after this day (YYYY-MM-DD)"""
|
||
dueFrom: String
|
||
|
||
"""Due on or before this day (YYYY-MM-DD)"""
|
||
dueTo: String
|
||
labels: [String!]
|
||
|
||
"""Only tickets in no sprint"""
|
||
noSprint: Boolean
|
||
|
||
"""Only the tickets hanging from this one"""
|
||
parentId: ID
|
||
priorities: [IssuePriority!]
|
||
projectId: ID!
|
||
releaseId: ID
|
||
|
||
"""Matches key, title or description"""
|
||
search: String
|
||
sort: IssueSort = RECENT
|
||
sprintId: ID
|
||
statuses: [String!]
|
||
types: [String!]
|
||
unassigned: Boolean
|
||
|
||
"""Only the tickets I follow (none when anonymous)"""
|
||
watching: Boolean
|
||
}
|
||
|
||
type IssueLink {
|
||
"""OUTWARD: this issue → other. INWARD: other → this issue."""
|
||
direction: LinkDirection!
|
||
issueId: ID!
|
||
issueKey: String!
|
||
type: IssueLinkType!
|
||
}
|
||
|
||
enum IssueLinkType {
|
||
BLOCKS
|
||
DEPENDS_ON
|
||
DUPLICATES
|
||
RELATES_TO
|
||
TESTS
|
||
}
|
||
|
||
type IssuePage {
|
||
items: [Issue!]!
|
||
total: Int!
|
||
}
|
||
|
||
"""The ticket another one hangs from"""
|
||
type IssueParent {
|
||
id: ID!
|
||
key: String!
|
||
title: String!
|
||
}
|
||
|
||
enum IssuePriority {
|
||
CRITICAL
|
||
HIGH
|
||
LOW
|
||
LOWEST
|
||
MEDIUM
|
||
}
|
||
|
||
"""RECENT: last updated first. RANK: backlog order."""
|
||
enum IssueSort {
|
||
RANK
|
||
RECENT
|
||
}
|
||
|
||
"""What a new ticket of the type starts with"""
|
||
type IssueTemplate {
|
||
"""Steps added to every new ticket"""
|
||
checklist: [String!]!
|
||
|
||
"""Used when the ticket comes without a description"""
|
||
description: String
|
||
}
|
||
|
||
input IssueTemplateInput {
|
||
checklist: [String!]! = []
|
||
description: String
|
||
}
|
||
|
||
type IssueTypeOption {
|
||
archived: Boolean!
|
||
color: String!
|
||
icon: String!
|
||
id: String!
|
||
name: String!
|
||
template: IssueTemplate
|
||
}
|
||
|
||
input IssueTypeOptionInput {
|
||
archived: Boolean! = false
|
||
color: String!
|
||
icon: String!
|
||
id: String!
|
||
name: String!
|
||
template: IssueTemplateInput
|
||
}
|
||
|
||
enum LinkDirection {
|
||
INWARD
|
||
OUTWARD
|
||
}
|
||
|
||
input LinkIssuesInput {
|
||
issueId: ID!
|
||
targetIssueId: ID!
|
||
type: IssueLinkType!
|
||
}
|
||
|
||
type Me {
|
||
"""Roles inside the task-stream OAuth client"""
|
||
appRoles: [String!]!
|
||
email: String!
|
||
id: ID!
|
||
isActive: Boolean!
|
||
name: String!
|
||
|
||
"""Global roles in Pireon"""
|
||
roles: [String!]!
|
||
}
|
||
|
||
type Mutation {
|
||
"""Add a step to a ticket's checklist"""
|
||
addChecklistItem(input: AddChecklistItemInput!): CommandResult!
|
||
|
||
"""
|
||
Comment on a ticket, or answer a comment (replyTo; one level of replies)
|
||
"""
|
||
addComment(body: String!, issueId: ID!, replyTo: ID): CommandResult!
|
||
|
||
"""
|
||
Add a repository with its own secret (the first turns the integration on)
|
||
"""
|
||
addGitRepository(name: String!, projectId: ID!): GitRepository!
|
||
|
||
"""Add a Pireon user to the project"""
|
||
addProjectMember(input: ProjectMemberInput!): CommandResult!
|
||
archiveRelease(releaseId: ID!): CommandResult!
|
||
|
||
"""Assign (or unassign with null) a project member"""
|
||
assignIssue(assigneeId: ID, issueId: ID!): CommandResult!
|
||
|
||
"""
|
||
The same change on up to 100 tickets; each goes through the normal rules and may refuse on its own
|
||
"""
|
||
bulkUpdateIssues(input: BulkUpdateIssuesInput!): BulkResult!
|
||
|
||
"""Move a ticket through the workflow (incl. QA)"""
|
||
changeIssueStatus(input: ChangeIssueStatusInput!): CommandResult!
|
||
changeProjectMemberRole(input: ProjectMemberInput!): CommandResult!
|
||
|
||
"""
|
||
Close the sprint under way. Unfinished tickets move to carryOverToSprintId, or back to the backlog when it is null.
|
||
"""
|
||
closeSprint(carryOverToSprintId: ID, sprintId: ID!): CommandResult!
|
||
|
||
"""
|
||
Add a rule. It acts as TaskStream, within the workflow, and what it does triggers no other rule.
|
||
"""
|
||
createAutomation(input: CreateAutomationInput!): Automation!
|
||
createIssue(input: CreateIssueInput!): CommandResult!
|
||
createProject(input: CreateProjectInput!): CommandResult!
|
||
createRelease(input: CreateReleaseInput!): CommandResult!
|
||
|
||
"""Plan a sprint (owners and admins)"""
|
||
createSprint(input: CreateSprintInput!): CommandResult!
|
||
createWebhook(input: CreateWebhookInput!): WebhookCreated!
|
||
deleteAutomation(automationId: ID!): Boolean!
|
||
|
||
"""Author or project admin"""
|
||
deleteComment(commentId: ID!): CommandResult!
|
||
|
||
"""
|
||
Delete the project (owner only), with its tickets, comments and releases. `confirmKey` must be the project key.
|
||
"""
|
||
deleteProject(confirmKey: String!, projectId: ID!): CommandResult!
|
||
|
||
"""Delete a view: whoever saved it, or an admin if it is shared"""
|
||
deleteSavedFilter(id: ID!): Boolean!
|
||
|
||
"""Delete a sprint that never started; its tickets go back to the backlog"""
|
||
deleteSprint(sprintId: ID!): CommandResult!
|
||
deleteWebhook(webhookId: ID!): Boolean!
|
||
|
||
"""
|
||
A new ticket like this one: what it says, not where it stands (the key comes in the result)
|
||
"""
|
||
duplicateIssue(issueId: ID!): CommandResult!
|
||
editComment(body: String!, commentId: ID!): CommandResult!
|
||
|
||
"""Relate tickets, e.g. FE DEPENDS_ON BE, QA TESTS FE"""
|
||
linkIssues(input: LinkIssuesInput!): CommandResult!
|
||
|
||
"""Mark these as read, or all of them when `ids` is omitted"""
|
||
markNotificationsRead(ids: [ID!]): Boolean!
|
||
|
||
"""
|
||
Ship the release. Fails while it has unfinished tickets unless force = true.
|
||
"""
|
||
publishRelease(force: Boolean! = false, releaseId: ID!): CommandResult!
|
||
|
||
"""Move a ticket in the backlog, between two others of the same project"""
|
||
rankIssue(input: RankIssueInput!): CommandResult!
|
||
|
||
"""React (👍 👎 😄 🎉 😕 ❤️ 🚀 👀) or take it back (on: false)"""
|
||
reactToComment(commentId: ID!, emoji: String!, on: Boolean!): CommandResult!
|
||
|
||
"""
|
||
Remove a file from a ticket (whoever uploaded it, or an owner/admin). Upload with the REST API.
|
||
"""
|
||
removeAttachment(attachmentId: ID!, issueId: ID!): CommandResult!
|
||
removeChecklistItem(issueId: ID!, itemId: ID!): CommandResult!
|
||
|
||
"""Remove a repository; what it linked stays on the tickets"""
|
||
removeGitRepository(projectId: ID!, repositoryId: ID!): Boolean!
|
||
removeProjectMember(projectId: ID!, userId: ID!): CommandResult!
|
||
|
||
"""Back from the trash"""
|
||
restoreIssue(issueId: ID!): CommandResult!
|
||
|
||
"""A new secret for a repository; the old one stops working"""
|
||
rotateGitRepositorySecret(projectId: ID!, repositoryId: ID!): GitRepository!
|
||
|
||
"""Save the current board filters under a name (members of the project)"""
|
||
saveFilter(input: SaveFilterInput!): ID!
|
||
|
||
"""
|
||
Place a ticket under an epic or another ticket of the same project (null: back to the top). Three levels at most: epic → ticket → subtask.
|
||
"""
|
||
setIssueParent(issueId: ID!, parentId: ID): CommandResult!
|
||
|
||
"""Put a ticket in a release (null removes it)"""
|
||
setIssueRelease(issueId: ID!, releaseId: ID): CommandResult!
|
||
|
||
"""Put a ticket in a sprint (null: back to the backlog)"""
|
||
setIssueSprint(issueId: ID!, sprintId: ID): CommandResult!
|
||
|
||
"""Start a planned sprint (only one under way per project)"""
|
||
startSprint(sprintId: ID!): CommandResult!
|
||
|
||
"""Send a ping now and tell how it went"""
|
||
testWebhook(webhookId: ID!): WebhookDelivery!
|
||
|
||
"""
|
||
To the trash (whoever opened it, or an owner/admin): hidden everywhere, deleted for good after 30 days
|
||
"""
|
||
trashIssue(issueId: ID!): CommandResult!
|
||
unlinkIssues(input: LinkIssuesInput!): CommandResult!
|
||
|
||
"""Stop following a ticket"""
|
||
unwatchIssue(issueId: ID!): CommandResult!
|
||
|
||
"""Rename, pause or resume"""
|
||
updateAutomation(active: Boolean, automationId: ID!, name: String): Automation!
|
||
|
||
"""Reword, tick or untick a checklist step"""
|
||
updateChecklistItem(input: UpdateChecklistItemInput!): CommandResult!
|
||
updateIssue(input: UpdateIssueInput!): CommandResult!
|
||
updateNotificationSettings(email: Boolean!): NotificationSettings!
|
||
updateProject(input: UpdateProjectInput!): CommandResult!
|
||
|
||
"""
|
||
Replace statuses, transitions, ticket types and areas (owners and admins). Values can be archived, not deleted.
|
||
"""
|
||
updateProjectSettings(projectId: ID!, settings: ProjectSettingsInput!): CommandResult!
|
||
updateRelease(input: UpdateReleaseInput!): CommandResult!
|
||
updateSprint(input: UpdateSprintInput!): CommandResult!
|
||
updateWebhook(input: UpdateWebhookInput!): Webhook!
|
||
|
||
"""
|
||
Vote for a ticket (on) or take the vote back: anyone signed in on a public project, members on a private one
|
||
"""
|
||
voteIssue(issueId: ID!, on: Boolean!): CommandResult!
|
||
|
||
"""
|
||
Follow a ticket: be told when it moves, is reassigned or gets a comment
|
||
"""
|
||
watchIssue(issueId: ID!): CommandResult!
|
||
|
||
"""
|
||
Write the release notes (Markdown; null removes them). Also after shipping.
|
||
"""
|
||
writeReleaseNotes(notes: String, releaseId: ID!): CommandResult!
|
||
}
|
||
|
||
type MyNotifications {
|
||
items: [Notification!]!
|
||
|
||
"""All my unread ones, not just these"""
|
||
unread: Int!
|
||
}
|
||
|
||
type Notification {
|
||
actor: UserRef!
|
||
createdAt: String!
|
||
id: ID!
|
||
issueId: ID
|
||
issueKey: String
|
||
kind: NotificationKind!
|
||
projectId: ID!
|
||
projectKey: String!
|
||
|
||
"""Null: unread"""
|
||
readAt: String
|
||
summary: String!
|
||
}
|
||
|
||
"""Why someone is told"""
|
||
enum NotificationKind {
|
||
ADDED_TO_PROJECT
|
||
ASSIGNED
|
||
COMMENTED
|
||
MENTIONED
|
||
PROPOSED
|
||
QA_FAILED
|
||
REMINDER
|
||
REPLIED
|
||
STATUS_CHANGED
|
||
WATCHED
|
||
}
|
||
|
||
type NotificationSettings {
|
||
"""Copies by e-mail"""
|
||
email: Boolean!
|
||
|
||
"""Whether this installation can send e-mail at all"""
|
||
emailAvailable: Boolean!
|
||
}
|
||
|
||
type Project {
|
||
"""Takes bug reports and ideas from anyone (while public)"""
|
||
acceptsProposals: Boolean!
|
||
|
||
"""
|
||
Statistics of the last `days` days (7-365): created vs resolved, cumulative flow, cycle/lead time, throughput and QA
|
||
"""
|
||
analytics(days: Int! = 90): ProjectAnalytics!
|
||
|
||
"""Kanban board, one column per status"""
|
||
board(
|
||
area: String
|
||
assigneeId: ID
|
||
labels: [String!]
|
||
perColumn: Int! = 50
|
||
priorities: [IssuePriority!]
|
||
releaseId: ID
|
||
|
||
"""Matches key, title or description"""
|
||
search: String
|
||
sprintId: ID
|
||
types: [String!]
|
||
unassigned: Boolean
|
||
): [BoardColumn!]!
|
||
createdAt: String!
|
||
description: String
|
||
id: ID!
|
||
|
||
"""Anyone can read it, also without signing in"""
|
||
isPublic: Boolean!
|
||
key: String!
|
||
members: [ProjectMember!]!
|
||
name: String!
|
||
owner: UserRef!
|
||
releases: [Release!]!
|
||
settings: ProjectSettings!
|
||
|
||
"""Project tracking dashboard"""
|
||
stats: ProjectStats!
|
||
updatedAt: String!
|
||
version: Int!
|
||
}
|
||
|
||
type ProjectAnalytics {
|
||
createdVsResolved: [CreatedResolved!]!
|
||
|
||
"""Tickets in each status at the end of every day"""
|
||
cumulativeFlow: CumulativeFlow!
|
||
|
||
"""Start of work → done"""
|
||
cycleTime: DurationStats!
|
||
from: String!
|
||
|
||
"""Creation → done"""
|
||
leadTime: DurationStats!
|
||
qa: QaStats!
|
||
|
||
"""Finished per person and week"""
|
||
throughput: [Throughput!]!
|
||
to: String!
|
||
}
|
||
|
||
type ProjectChange {
|
||
action: String!
|
||
actorId: ID!
|
||
project: Project!
|
||
version: Int!
|
||
}
|
||
|
||
type ProjectMember {
|
||
role: ProjectRole!
|
||
user: UserRef!
|
||
}
|
||
|
||
input ProjectMemberInput {
|
||
projectId: ID!
|
||
role: ProjectRole! = MEMBER
|
||
|
||
"""Pireon user id"""
|
||
userId: ID!
|
||
}
|
||
|
||
enum ProjectRole {
|
||
ADMIN
|
||
MEMBER
|
||
OWNER
|
||
VIEWER
|
||
}
|
||
|
||
"""Statuses (board order), transitions, ticket types and areas"""
|
||
type ProjectSettings {
|
||
areas: [AreaOption!]!
|
||
issueTypes: [IssueTypeOption!]!
|
||
statuses: [WorkflowStatus!]!
|
||
transitions: [StatusTransitions!]!
|
||
}
|
||
|
||
"""
|
||
The whole configuration (replaces the previous one). Statuses go in board order.
|
||
"""
|
||
input ProjectSettingsInput {
|
||
areas: [AreaOptionInput!]!
|
||
issueTypes: [IssueTypeOptionInput!]!
|
||
statuses: [WorkflowStatusInput!]!
|
||
transitions: [StatusTransitionsInput!]!
|
||
}
|
||
|
||
type ProjectStats {
|
||
byArea: [CountByKey!]!
|
||
|
||
"""Open tickets per person"""
|
||
byAssignee: [CountByAssignee!]!
|
||
byPriority: [CountByKey!]!
|
||
byStatus: [CountByStatus!]!
|
||
byType: [CountByKey!]!
|
||
open: Int!
|
||
total: Int!
|
||
}
|
||
|
||
type QaStats {
|
||
failed: Int!
|
||
failureRate: Float
|
||
passed: Int!
|
||
}
|
||
|
||
type Query {
|
||
"""Project activity feed (newest first)"""
|
||
activity(
|
||
"""ISO date cursor"""
|
||
before: String
|
||
issueId: ID
|
||
limit: Int! = 50
|
||
projectId: ID!
|
||
): [Activity!]!
|
||
automations(projectId: ID!): [Automation!]!
|
||
|
||
"""
|
||
A project's epics: top-level tickets that group others, with the progress of those
|
||
"""
|
||
epics(projectId: ID!): [Epic!]!
|
||
gitIntegration(projectId: ID!): GitIntegration!
|
||
issue(
|
||
"""Issue id or key (e.g. "TS-42")"""
|
||
idOrKey: String!
|
||
): Issue!
|
||
issues(filter: IssueFilterInput!, limit: Int! = 50, offset: Int! = 0): IssuePage!
|
||
|
||
"""The authenticated user"""
|
||
me: Me!
|
||
|
||
"""
|
||
Tickets across all my projects: assigned to me (default) or any (e.g. categories [QA])
|
||
"""
|
||
myIssues(
|
||
assignedToMe: Boolean! = true
|
||
categories: [StatusCategory!]
|
||
limit: Int! = 50
|
||
offset: Int! = 0
|
||
|
||
"""Matches key, title or description (global search)"""
|
||
search: String
|
||
statuses: [String!]
|
||
|
||
"""Only the tickets I follow"""
|
||
watching: Boolean! = false
|
||
): IssuePage!
|
||
|
||
"""My notifications, newest first, and how many are unread"""
|
||
myNotifications(limit: Int! = 30, unreadOnly: Boolean! = false): MyNotifications!
|
||
|
||
"""Projects where I am a member"""
|
||
myProjects: [Project!]!
|
||
notificationSettings: NotificationSettings!
|
||
|
||
"""A project of mine, or any public one (no sign-in needed)"""
|
||
project(
|
||
"""Project id or key (e.g. "TS")"""
|
||
idOrKey: String!
|
||
): Project!
|
||
|
||
"""Projects anyone can read, also without signing in"""
|
||
publicProjects: [Project!]!
|
||
release(id: ID!): Release!
|
||
|
||
"""My views of the project's board, then the shared ones"""
|
||
savedFilters(projectId: ID!): [SavedFilter!]!
|
||
sprint(id: ID!): Sprint!
|
||
|
||
"""
|
||
A project's sprints: the one under way, the planned ones, then the closed ones
|
||
"""
|
||
sprints(projectId: ID!): [Sprint!]!
|
||
|
||
"""A project's tickets in the trash, newest first"""
|
||
trash(projectId: ID!): [Issue!]!
|
||
|
||
"""Planned releases of all my projects, soonest target date first"""
|
||
upcomingReleases(limit: Int! = 10): [Release!]!
|
||
user(id: String!): User
|
||
|
||
"""Search users by name or email (to assign / add members)"""
|
||
users(limit: Int! = 20, search: String! = ""): [User!]!
|
||
|
||
"""Event names a webhook can subscribe to (also family.* and *)"""
|
||
webhookEvents: [String!]!
|
||
webhooks(projectId: ID!): [Webhook!]!
|
||
}
|
||
|
||
input RankIssueInput {
|
||
"""The ticket that ends up right above (null: the top)"""
|
||
aboveIssueId: ID
|
||
|
||
"""The ticket that ends up right below (null: the bottom)"""
|
||
belowIssueId: ID
|
||
issueId: ID!
|
||
}
|
||
|
||
type Release {
|
||
"""Scope vs finished tickets, day by day"""
|
||
burnup: ReleaseBurnup!
|
||
createdAt: String!
|
||
description: String
|
||
id: ID!
|
||
|
||
"""Tickets included in this release"""
|
||
issues(limit: Int! = 100, offset: Int! = 0): IssuePage!
|
||
name: String!
|
||
|
||
"""What shipped, told for people (Markdown)"""
|
||
notes: String
|
||
|
||
"""
|
||
A Markdown draft of the notes from its finished tickets, by type (not saved)
|
||
"""
|
||
notesDraft: String!
|
||
progress: ReleaseProgress!
|
||
projectId: ID!
|
||
releasedAt: String
|
||
status: ReleaseStatus!
|
||
targetDate: String
|
||
version: Int!
|
||
}
|
||
|
||
type ReleaseBurnup {
|
||
dates: [String!]!
|
||
done: [Int!]!
|
||
releaseId: ID!
|
||
|
||
"""Tickets in the release (not cancelled)"""
|
||
scope: [Int!]!
|
||
targetDate: String
|
||
}
|
||
|
||
type ReleaseChange {
|
||
action: String!
|
||
actorId: ID!
|
||
release: Release!
|
||
version: Int!
|
||
}
|
||
|
||
type ReleaseProgress {
|
||
byStatus: [CountByStatus!]!
|
||
finished: Int!
|
||
percent: Int!
|
||
total: Int!
|
||
}
|
||
|
||
type ReleaseRef {
|
||
id: ID!
|
||
name: String!
|
||
}
|
||
|
||
enum ReleaseStatus {
|
||
ARCHIVED
|
||
PLANNED
|
||
RELEASED
|
||
}
|
||
|
||
input SaveFilterInput {
|
||
criteria: SavedFilterCriteriaInput!
|
||
name: String!
|
||
projectId: ID!
|
||
|
||
"""Visible to the whole project (members, not viewers, share)"""
|
||
shared: Boolean! = false
|
||
}
|
||
|
||
"""
|
||
A named view of the board: private to whoever saved it, or shared with the project
|
||
"""
|
||
type SavedFilter {
|
||
createdAt: String!
|
||
criteria: SavedFilterCriteria!
|
||
id: ID!
|
||
name: String!
|
||
owner: UserRef!
|
||
projectId: ID!
|
||
shared: Boolean!
|
||
}
|
||
|
||
"""What a saved view narrows the board to; empty: everything"""
|
||
type SavedFilterCriteria {
|
||
areas: [String!]
|
||
assigneeId: ID
|
||
labels: [String!]
|
||
priorities: [IssuePriority!]
|
||
releaseId: ID
|
||
search: String
|
||
types: [String!]
|
||
unassigned: Boolean
|
||
}
|
||
|
||
input SavedFilterCriteriaInput {
|
||
areas: [String!]
|
||
assigneeId: ID
|
||
labels: [String!]
|
||
priorities: [IssuePriority!]
|
||
releaseId: ID
|
||
search: String
|
||
types: [String!]
|
||
unassigned: Boolean
|
||
}
|
||
|
||
"""A time-boxed iteration: the tickets the team commits to"""
|
||
type Sprint {
|
||
burndown: SprintBurndown!
|
||
closedAt: String
|
||
createdAt: String!
|
||
|
||
"""Last day (included), YYYY-MM-DD"""
|
||
endDate: String!
|
||
goal: String
|
||
id: ID!
|
||
|
||
"""Tickets in this sprint, in backlog order"""
|
||
issues(limit: Int! = 100, offset: Int! = 0): IssuePage!
|
||
name: String!
|
||
progress: SprintProgress!
|
||
projectId: ID!
|
||
|
||
"""First day, YYYY-MM-DD"""
|
||
startDate: String!
|
||
startedAt: String
|
||
status: SprintStatus!
|
||
version: Int!
|
||
}
|
||
|
||
"""
|
||
Per day of the sprint: tickets in it, still open, and their points (null: days to come). `ideal` falls from the first day to zero.
|
||
"""
|
||
type SprintBurndown {
|
||
dates: [String!]!
|
||
ideal: [Float!]!
|
||
remaining: [Int]!
|
||
remainingPoints: [Int]!
|
||
scope: [Int]!
|
||
sprintId: ID!
|
||
}
|
||
|
||
type SprintChange {
|
||
action: String!
|
||
actorId: ID!
|
||
|
||
"""Null: deleted"""
|
||
sprint: Sprint
|
||
sprintId: ID!
|
||
version: Int!
|
||
}
|
||
|
||
type SprintProgress {
|
||
"""Done or cancelled"""
|
||
finished: Int!
|
||
finishedStoryPoints: Int!
|
||
percent: Int!
|
||
storyPoints: Int!
|
||
total: Int!
|
||
}
|
||
|
||
type SprintRef {
|
||
id: ID!
|
||
name: String!
|
||
}
|
||
|
||
"""PLANNED, ACTIVE (one per project at a time) or CLOSED"""
|
||
enum SprintStatus {
|
||
ACTIVE
|
||
CLOSED
|
||
PLANNED
|
||
}
|
||
|
||
"""
|
||
What a status means, whatever each project calls it. DONE and CANCELLED are finished.
|
||
"""
|
||
enum StatusCategory {
|
||
CANCELLED
|
||
DONE
|
||
IN_PROGRESS
|
||
QA
|
||
TODO
|
||
}
|
||
|
||
type StatusTransitions {
|
||
from: String!
|
||
to: [String!]!
|
||
}
|
||
|
||
input StatusTransitionsInput {
|
||
from: String!
|
||
to: [String!]!
|
||
}
|
||
|
||
type Subscription {
|
||
"""Live activity feed"""
|
||
activityAdded(projectId: ID!): Activity!
|
||
commentChanged(issueId: ID!): CommentChange!
|
||
|
||
"""Tickets created/updated/moved in a project (board in realtime)"""
|
||
issueChanged(projectId: ID!): IssueChange!
|
||
|
||
"""Projects I am added to / removed from"""
|
||
myProjectsChanged: ProjectChange!
|
||
|
||
"""My notifications, as they happen"""
|
||
notificationAdded: Notification!
|
||
projectChanged(projectId: ID!): ProjectChange!
|
||
releaseChanged(projectId: ID!): ReleaseChange!
|
||
sprintChanged(projectId: ID!): SprintChange!
|
||
}
|
||
|
||
type Throughput {
|
||
count: Int!
|
||
user: UserRef
|
||
week: String!
|
||
}
|
||
|
||
input UpdateChecklistItemInput {
|
||
done: Boolean
|
||
issueId: ID!
|
||
itemId: ID!
|
||
text: String
|
||
}
|
||
|
||
input UpdateIssueInput {
|
||
"""An area id"""
|
||
area: String
|
||
description: String
|
||
dueDate: String
|
||
|
||
"""Figma frame for UI changes"""
|
||
figmaUrl: String
|
||
issueId: ID!
|
||
labels: [String!]
|
||
priority: IssuePriority
|
||
|
||
"""When work should start (roadmap), YYYY-MM-DD"""
|
||
startDate: String
|
||
storyPoints: Int
|
||
title: String
|
||
type: String
|
||
}
|
||
|
||
input UpdateProjectInput {
|
||
"""
|
||
Take bug reports and ideas from anyone through the public form (while public)
|
||
"""
|
||
acceptsProposals: Boolean
|
||
description: String
|
||
isPublic: Boolean
|
||
name: String
|
||
projectId: ID!
|
||
}
|
||
|
||
input UpdateReleaseInput {
|
||
description: String
|
||
name: String
|
||
releaseId: ID!
|
||
targetDate: String
|
||
}
|
||
|
||
input UpdateSprintInput {
|
||
endDate: String
|
||
goal: String
|
||
name: String
|
||
sprintId: ID!
|
||
startDate: String
|
||
}
|
||
|
||
input UpdateWebhookInput {
|
||
active: Boolean
|
||
events: [String!]
|
||
url: String
|
||
webhookId: ID!
|
||
}
|
||
|
||
"""User managed by the Pireon identity server"""
|
||
type User {
|
||
email: String!
|
||
id: ID!
|
||
isActive: Boolean!
|
||
name: String!
|
||
}
|
||
|
||
type UserRef {
|
||
id: ID!
|
||
name: String!
|
||
}
|
||
|
||
"""
|
||
Where the project tells another system what happened (signed POST, HMAC-SHA256)
|
||
"""
|
||
type Webhook {
|
||
active: Boolean!
|
||
createdAt: String!
|
||
createdBy: UserRef!
|
||
|
||
"""Event names, family.* or *"""
|
||
events: [String!]!
|
||
id: ID!
|
||
lastDelivery: WebhookDelivery
|
||
projectId: ID!
|
||
url: String!
|
||
}
|
||
|
||
type WebhookCreated {
|
||
id: ID!
|
||
|
||
"""Signs every delivery. Shown only now: keep it."""
|
||
secret: String!
|
||
}
|
||
|
||
type WebhookDelivery {
|
||
at: String!
|
||
error: String
|
||
event: String!
|
||
ok: Boolean!
|
||
|
||
"""HTTP status"""
|
||
status: Int
|
||
}
|
||
|
||
type WorkflowStatus {
|
||
"""No new tickets or transitions into it"""
|
||
archived: Boolean!
|
||
category: StatusCategory!
|
||
|
||
"""Colour token: slate, violet, green…"""
|
||
color: String!
|
||
|
||
"""Stable id stored on tickets, e.g. IN_REVIEW"""
|
||
id: String!
|
||
name: String!
|
||
|
||
"""Entering it needs a reason"""
|
||
requiresReason: Boolean!
|
||
|
||
"""
|
||
Work-in-progress limit: most tickets the column should hold (a warning, not a lock)
|
||
"""
|
||
wipLimit: Int
|
||
}
|
||
|
||
input WorkflowStatusInput {
|
||
archived: Boolean! = false
|
||
category: StatusCategory!
|
||
color: String!
|
||
id: String!
|
||
name: String!
|
||
requiresReason: Boolean! = false
|
||
|
||
"""Work-in-progress limit (null: none)"""
|
||
wipLimit: Int
|
||
} |