Przeglądaj źródła

fix(issue-management): restore Project date fields

Turtle 1 tydzień temu
rodzic
commit
070b46e1ef

+ 1 - 1
.github/issue-management/config.json

@@ -5,7 +5,7 @@
   "projectTitle": "DSH Issue Management",
   "lifecycleActor": "dsh-issue-management",
   "priorityField": "Priority",
-  "startDateField": "Start date",
+  "startDateField": "Start Date",
   "projectTimeZone": "Asia/Shanghai",
   "allowUnassignedOwner": true,
   "statuses": [

+ 17 - 22
.github/issue-management/policy.mjs

@@ -484,13 +484,7 @@ async function projectContext(number, includeStatusActor = false, includeStartDa
           title
           fields(first: 50) {
             nodes {
-              ... on ProjectV2Field {
-                id
-                name
-                dataType
-                isIssueField
-                issueField { ... on IssueFieldDate { id } }
-              }
+              ... on ProjectV2Field { id name dataType isIssueField }
               ... on ProjectV2SingleSelectField { id name dataType options { id name } }
             }
           }
@@ -518,9 +512,7 @@ async function projectContext(number, includeStatusActor = false, includeStartDa
               }
               startDateValue: fieldValueByName(name: $startDateField)
                 @include(if: $includeStartDate) {
-                ... on ProjectV2ItemIssueFieldValue {
-                  issueFieldValue { ... on IssueFieldDateValue { value } }
-                }
+                ... on ProjectV2ItemFieldDateValue { date }
               }
             }
           }
@@ -552,8 +544,8 @@ async function projectContext(number, includeStatusActor = false, includeStartDa
   if (startDateField && startDateField.dataType !== 'DATE') {
     throw new Error(`Project ${config.startDateField} 字段必须为 Date`)
   }
-  if (startDateField && (!startDateField.isIssueField || !startDateField.issueField?.id)) {
-    throw new Error(`Project ${config.startDateField} 字段必须为 Issue Date 字段`)
+  if (startDateField?.isIssueField) {
+    throw new Error(`Project ${config.startDateField} 字段必须为 Project Date 字段`)
   }
   const item = issue.projectItems.nodes.find((candidate) => candidate.project.id === project.id)
   const latestStatusEvent = issue.timelineItems?.nodes
@@ -593,24 +585,27 @@ async function ensureProjectItem(number, includeStartDate = false) {
 }
 
 /**
- * Initialize one Issue's organization Start date when it is empty.
+ * Initialize one Issue's Project Start Date when it is empty.
  * @param {number} number Same-repository Issue number.
  * @param {string} date Date in YYYY-MM-DD form.
- * @returns {Promise<void>} Resolves after the conditional Issue-field update.
+ * @returns {Promise<void>} Resolves after the conditional Project update.
  */
 export async function initializeIssueStartDate(number, date) {
   const context = await ensureProjectItem(number, true)
-  if (context.item.startDateValue?.issueFieldValue?.value) return
+  if (context.item.startDateValue?.date) return
   await graphql(
-    `mutation($issueId: ID!, $fieldId: ID!, $date: String!) {
-      updateIssueFieldValue(input: {
-        issueId: $issueId,
-        issueField: {fieldId: $fieldId, dateValue: $date}
-      }) { issue { id } }
+    `mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $date: Date!) {
+      updateProjectV2ItemFieldValue(input: {
+        projectId: $projectId,
+        itemId: $itemId,
+        fieldId: $fieldId,
+        value: {date: $date}
+      }) { projectV2Item { id } }
     }`,
     {
-      issueId: context.issue.id,
-      fieldId: context.startDateField.issueField.id,
+      projectId: context.project.id,
+      itemId: context.item.id,
+      fieldId: context.startDateField.id,
       date,
     },
   )

+ 25 - 30
.github/issue-management/policy.test.mjs

@@ -21,8 +21,7 @@ const projectGraphqlData = ({
   startDate = null,
   startDateField = true,
   startDateType = 'DATE',
-  startDateIsIssueField = true,
-  startDateIssueField = true,
+  startDateIsIssueField = false,
 } = {}) => ({
   organization: {
     projectV2: {
@@ -34,11 +33,10 @@ const projectGraphqlData = ({
           ...(startDateField
             ? [
                 {
-                  id: 'start-date-project-field-id',
-                  name: 'Start date',
+                  id: 'start-date-field-id',
+                  name: 'Start Date',
                   dataType: startDateType,
                   isIssueField: startDateIsIssueField,
-                  issueField: startDateIssueField ? { id: 'start-date-issue-field-id' } : null,
                 },
               ]
             : []),
@@ -56,8 +54,7 @@ const projectGraphqlData = ({
                 id: 'item-id',
                 project: { id: 'project-id' },
                 fieldValueByName: { name: 'Inbox', optionId: 'inbox-option-id' },
-                startDateValue:
-                  startDate === null ? null : { issueFieldValue: { value: startDate } },
+                startDateValue: startDate === null ? null : { date: startDate },
               },
             ]
           : [],
@@ -269,27 +266,29 @@ test('initializes every referenced Issue only for a PR opened event', async () =
   assert.equal(writes.length, 3)
 })
 
-test('writes an empty Issue Start date with the configured field', async (t) => {
+test('writes an empty Project Start Date with the configured field', async (t) => {
   const requests = mockGraphql(t, (request) => {
     if (request.query.includes('query(')) return projectGraphqlData()
-    return { updateIssueFieldValue: { issue: { id: 'issue-id' } } }
+    return { updateProjectV2ItemFieldValue: { projectV2Item: { id: 'item-id' } } }
   })
 
   await initializeIssueStartDate(42, '2026-08-28')
 
   assert.equal(requests.length, 2)
   assert.match(requests[0].query, /isIssueField/)
-  assert.match(requests[0].query, /ProjectV2ItemIssueFieldValue/)
-  assert.match(requests[1].query, /updateIssueFieldValue/)
-  assert.match(requests[1].query, /issueField: \{fieldId: \$fieldId, dateValue: \$date\}/)
+  assert.doesNotMatch(requests[0].query, /issueField\s*\{/)
+  assert.match(requests[0].query, /ProjectV2ItemFieldDateValue/)
+  assert.match(requests[1].query, /updateProjectV2ItemFieldValue/)
+  assert.match(requests[1].query, /value: \{date: \$date\}/)
   assert.deepEqual(requests[1].variables, {
-    issueId: 'issue-id',
-    fieldId: 'start-date-issue-field-id',
+    projectId: 'project-id',
+    itemId: 'item-id',
+    fieldId: 'start-date-field-id',
     date: '2026-08-28',
   })
 })
 
-test('preserves an existing Issue Start date', async (t) => {
+test('preserves an existing Project Start Date', async (t) => {
   const requests = mockGraphql(t, () => projectGraphqlData({ startDate: '2026-08-01' }))
 
   await initializeIssueStartDate(42, '2026-08-28')
@@ -297,13 +296,13 @@ test('preserves an existing Issue Start date', async (t) => {
   assert.equal(requests.length, 1)
 })
 
-test('adds a referenced Issue to the Project before setting Start date', async (t) => {
+test('adds a referenced Issue to the Project before setting Start Date', async (t) => {
   const requests = mockGraphql(t, (request) => {
     if (request.query.includes('query(')) return projectGraphqlData({ projectItem: false })
     if (request.query.includes('addProjectV2ItemById')) {
       return { addProjectV2ItemById: { item: { id: 'new-item-id' } } }
     }
-    return { updateIssueFieldValue: { issue: { id: 'issue-id' } } }
+    return { updateProjectV2ItemFieldValue: { projectV2Item: { id: 'new-item-id' } } }
   })
 
   await initializeIssueStartDate(42, '2026-08-28')
@@ -311,30 +310,26 @@ test('adds a referenced Issue to the Project before setting Start date', async (
   assert.equal(requests.length, 3)
   assert.deepEqual(requests[1].variables, { projectId: 'project-id', contentId: 'issue-id' })
   assert.deepEqual(requests[2].variables, {
-    issueId: 'issue-id',
-    fieldId: 'start-date-issue-field-id',
+    projectId: 'project-id',
+    itemId: 'new-item-id',
+    fieldId: 'start-date-field-id',
     date: '2026-08-28',
   })
 })
 
-test('rejects a missing, non-Date, or Project-local Start date field', async (t) => {
+test('rejects a missing, non-Date, or Issue-level Start Date field', async (t) => {
   let response = projectGraphqlData({ startDateField: false })
   const requests = mockGraphql(t, () => response)
 
-  await assert.rejects(initializeIssueStartDate(42, '2026-08-28'), /Project 缺少 Start date 字段/)
+  await assert.rejects(initializeIssueStartDate(42, '2026-08-28'), /Project 缺少 Start Date 字段/)
   response = projectGraphqlData({ startDateType: 'TEXT' })
-  await assert.rejects(initializeIssueStartDate(42, '2026-08-28'), /Start date 字段必须为 Date/)
-  response = projectGraphqlData({ startDateIsIssueField: false })
+  await assert.rejects(initializeIssueStartDate(42, '2026-08-28'), /Start Date 字段必须为 Date/)
+  response = projectGraphqlData({ startDateIsIssueField: true })
   await assert.rejects(
     initializeIssueStartDate(42, '2026-08-28'),
-    /Start date 字段必须为 Issue Date 字段/,
+    /Start Date 字段必须为 Project Date 字段/,
   )
-  response = projectGraphqlData({ startDateIssueField: false })
-  await assert.rejects(
-    initializeIssueStartDate(42, '2026-08-28'),
-    /Start date 字段必须为 Issue Date 字段/,
-  )
-  assert.equal(requests.length, 4)
+  assert.equal(requests.length, 3)
 })
 
 test('does not treat pull request references as Issue associations', () => {