|
|
@@ -52,6 +52,9 @@ for (const status of ['In progress', 'In review']) {
|
|
|
if (typeof config.lifecycleActor !== 'string' || !config.lifecycleActor) {
|
|
|
throw new Error('config.lifecycleActor 未设置')
|
|
|
}
|
|
|
+if (typeof config.priorityField !== 'string' || !config.priorityField) {
|
|
|
+ throw new Error('config.priorityField 未设置')
|
|
|
+}
|
|
|
if (typeof config.startDateField !== 'string' || !config.startDateField) {
|
|
|
throw new Error('config.startDateField 未设置')
|
|
|
}
|
|
|
@@ -445,13 +448,16 @@ async function graphql(query, variables) {
|
|
|
return result.data
|
|
|
}
|
|
|
|
|
|
-async function issueSnapshot(number, status = undefined) {
|
|
|
+/**
|
|
|
+ * Read one Issue together with its Project planning values.
|
|
|
+ * @param {number} number Same-repository Issue number.
|
|
|
+ * @param {string|null|undefined} status Optional known Project status.
|
|
|
+ * @returns {Promise<object|null>} Issue snapshot, or null when the number identifies a pull request.
|
|
|
+ */
|
|
|
+export async function issueSnapshot(number, status = undefined) {
|
|
|
const issue = await api(`/repos/${config.organization}/${config.repository}/issues/${number}`)
|
|
|
if (issue.pull_request) return null
|
|
|
- const values = await api(
|
|
|
- `/repos/${config.organization}/${config.repository}/issues/${number}/issue-field-values?per_page=100`,
|
|
|
- )
|
|
|
- const field = (name) => values.find((value) => value.issue_field_name === name)
|
|
|
+ const context = await projectContext(number)
|
|
|
return {
|
|
|
number,
|
|
|
nodeId: issue.node_id,
|
|
|
@@ -460,8 +466,8 @@ async function issueSnapshot(number, status = undefined) {
|
|
|
assignees: issue.assignees.map((assignee) => assignee.login),
|
|
|
labels: issue.labels.map((label) => label.name),
|
|
|
type: issue.type?.name ?? null,
|
|
|
- priority: field(config.priorityField)?.single_select_option?.name ?? null,
|
|
|
- status: status === undefined ? await projectStatus(number) : status,
|
|
|
+ priority: context.item?.priorityValue?.name ?? null,
|
|
|
+ status: status === undefined ? (context.item?.fieldValueByName?.name ?? null) : status,
|
|
|
state: issue.state,
|
|
|
stateReason: issue.state_reason ?? null,
|
|
|
}
|
|
|
@@ -476,6 +482,7 @@ async function projectContext(number, includeStatusActor = false, includeStartDa
|
|
|
$project: Int!
|
|
|
$includeStatusActor: Boolean!
|
|
|
$includeStartDate: Boolean!
|
|
|
+ $priorityField: String!
|
|
|
$startDateField: String!
|
|
|
) {
|
|
|
organization(login: $organization) {
|
|
|
@@ -484,8 +491,19 @@ async function projectContext(number, includeStatusActor = false, includeStartDa
|
|
|
title
|
|
|
fields(first: 50) {
|
|
|
nodes {
|
|
|
- ... on ProjectV2Field { id name dataType isIssueField }
|
|
|
- ... on ProjectV2SingleSelectField { id name dataType options { id name } }
|
|
|
+ ... on ProjectV2Field {
|
|
|
+ id
|
|
|
+ name
|
|
|
+ dataType
|
|
|
+ isIssueField
|
|
|
+ }
|
|
|
+ ... on ProjectV2SingleSelectField {
|
|
|
+ id
|
|
|
+ name
|
|
|
+ dataType
|
|
|
+ isIssueField
|
|
|
+ options { id name }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -510,6 +528,9 @@ async function projectContext(number, includeStatusActor = false, includeStartDa
|
|
|
fieldValueByName(name: "Status") {
|
|
|
... on ProjectV2ItemFieldSingleSelectValue { name optionId }
|
|
|
}
|
|
|
+ priorityValue: fieldValueByName(name: $priorityField) {
|
|
|
+ ... on ProjectV2ItemFieldSingleSelectValue { name optionId }
|
|
|
+ }
|
|
|
startDateValue: fieldValueByName(name: $startDateField)
|
|
|
@include(if: $includeStartDate) {
|
|
|
... on ProjectV2ItemFieldDateValue { date }
|
|
|
@@ -526,6 +547,7 @@ async function projectContext(number, includeStatusActor = false, includeStartDa
|
|
|
project: config.projectNumber,
|
|
|
includeStatusActor,
|
|
|
includeStartDate,
|
|
|
+ priorityField: config.priorityField,
|
|
|
startDateField: config.startDateField,
|
|
|
},
|
|
|
)
|
|
|
@@ -535,6 +557,14 @@ async function projectContext(number, includeStatusActor = false, includeStartDa
|
|
|
if (!issue) throw new Error(`#${number} 不存在`)
|
|
|
const statusField = project.fields.nodes.find((field) => field?.name === 'Status')
|
|
|
if (!statusField) throw new Error('Project 缺少 Status 字段')
|
|
|
+ const priorityField = project.fields.nodes.find((field) => field?.name === config.priorityField)
|
|
|
+ if (!priorityField) throw new Error(`Project 缺少 ${config.priorityField} 字段`)
|
|
|
+ if (priorityField.dataType !== 'SINGLE_SELECT') {
|
|
|
+ throw new Error(`Project ${config.priorityField} 字段必须为 Single Select`)
|
|
|
+ }
|
|
|
+ if (priorityField.isIssueField) {
|
|
|
+ throw new Error(`Project ${config.priorityField} 字段必须为 Project custom field`)
|
|
|
+ }
|
|
|
const startDateField = includeStartDate
|
|
|
? project.fields.nodes.find((field) => field?.name === config.startDateField)
|
|
|
: null
|
|
|
@@ -555,12 +585,7 @@ async function projectContext(number, includeStatusActor = false, includeStartDa
|
|
|
latestStatusEvent && latestStatusEvent.status === item?.fieldValueByName?.name
|
|
|
? (latestStatusEvent.actor?.login ?? null)
|
|
|
: null
|
|
|
- return { project, issue, statusField, startDateField, item, statusActor }
|
|
|
-}
|
|
|
-
|
|
|
-async function projectStatus(number) {
|
|
|
- const context = await projectContext(number)
|
|
|
- return context.item?.fieldValueByName?.name ?? null
|
|
|
+ return { project, issue, statusField, priorityField, startDateField, item, statusActor }
|
|
|
}
|
|
|
|
|
|
async function ensureProjectItem(number, includeStartDate = false) {
|
|
|
@@ -579,6 +604,7 @@ async function ensureProjectItem(number, includeStartDate = false) {
|
|
|
item: {
|
|
|
id: data.addProjectV2ItemById.item.id,
|
|
|
fieldValueByName: null,
|
|
|
+ priorityValue: null,
|
|
|
startDateValue: null,
|
|
|
},
|
|
|
}
|