From d076c40f0d8aeaa2ec87e6bccda7013b88ccd601 Mon Sep 17 00:00:00 2001 From: David Sanders Date: Tue, 21 Jan 2025 21:13:11 -0800 Subject: [PATCH] fix: handle redacted items on project --- __tests__/completed-by.test.mts | 20 +++--- __tests__/copy-project.test.mts | 16 ++--- __tests__/delete-project.test.mts | 11 +++ __tests__/edit-item.test.mts | 30 ++++++-- __tests__/get-item.test.mts | 35 ++++++++-- __tests__/helpers.test.ts | 25 +++++++ __tests__/lib.test.ts | 98 +++++++++++++++----------- dist/add-item.js | 6 +- dist/archive-item.js | 18 ++--- dist/close-project.js | 6 +- dist/completed-by.js | 10 +-- dist/copy-project.js | 10 +-- dist/delete-item.js | 18 ++--- dist/delete-project.js | 6 +- dist/edit-item.js | 24 +++---- dist/edit-project.js | 6 +- dist/find-project.js | 6 +- dist/find-workflow.js | 6 +- dist/get-item.js | 28 ++++---- dist/get-project.js | 6 +- dist/get-workflow.js | 6 +- dist/github-script/index.js | 27 ++++--- dist/link-project.js | 6 +- src/edit-item.ts | 7 +- src/get-item.ts | 10 +-- src/lib.ts | 112 ++++++++++++------------------ 26 files changed, 313 insertions(+), 240 deletions(-) diff --git a/__tests__/completed-by.test.mts b/__tests__/completed-by.test.mts index 783487f..36ad25c 100644 --- a/__tests__/completed-by.test.mts +++ b/__tests__/completed-by.test.mts @@ -137,11 +137,11 @@ describe('completedByAction', () => { { id: itemId, content: { - __typename: 'DraftIssue', id: 'content-id', body: 'This is the item body', title: 'Item Title' - } + }, + type: 'DRAFT_ISSUE' } ]); @@ -168,11 +168,11 @@ describe('completedByAction', () => { { id: itemId, content: { - __typename: 'DraftIssue', id: 'content-id', body: `Completed by ${itemUrl}`, title: 'Item Title' - } + }, + type: 'DRAFT_ISSUE' } ]); vi.mocked(getPullRequestState).mockResolvedValue('MERGED'); @@ -204,11 +204,11 @@ describe('completedByAction', () => { { id: itemId, content: { - __typename: 'DraftIssue', id: 'content-id', body: `Completed by ${itemUrl}`, title: 'Item Title' - } + }, + type: 'DRAFT_ISSUE' } ]); vi.mocked(getPullRequestState).mockResolvedValue('OPEN'); @@ -238,14 +238,14 @@ describe('completedByAction', () => { { id: itemId, content: { - __typename: 'DraftIssue', id: 'content-id', body: ` Completed by ${itemUrl1} Completed by ${itemUrl2} `, title: 'Item Title' - } + }, + type: 'DRAFT_ISSUE' } ]); vi.mocked(getPullRequestState) @@ -278,11 +278,11 @@ describe('completedByAction', () => { { id: itemId, content: { - __typename: 'DraftIssue', id: 'content-id', body: `Completed by ${itemUrl}`, title: 'Item Title' - } + }, + type: 'DRAFT_ISSUE' } ]); vi.mocked(getPullRequestState).mockRejectedValue(new Error(errorMessage)); diff --git a/__tests__/copy-project.test.mts b/__tests__/copy-project.test.mts index b9fca36..14813ee 100644 --- a/__tests__/copy-project.test.mts +++ b/__tests__/copy-project.test.mts @@ -235,11 +235,11 @@ describe('copyProjectAction', () => { { id: 'item-id', content: { - __typename: 'DraftIssue', id: 'content-id', body: 'This is the item body', title: 'Item Title' - } + }, + type: 'DRAFT_ISSUE' } ]); @@ -268,11 +268,11 @@ describe('copyProjectAction', () => { { id: itemId, content: { - __typename: 'DraftIssue', id: contentId, body: 'This is the item {{ foo }}', title: 'Item {{ foo }}' - } + }, + type: 'DRAFT_ISSUE' } ]); @@ -306,7 +306,6 @@ describe('copyProjectAction', () => { { id: itemId, content: { - __typename: 'DraftIssue', id: 'content-id', body: ``, title: 'Item Title' - } + }, + type: 'DRAFT_ISSUE' } ]); @@ -348,11 +348,11 @@ describe('copyProjectAction', () => { { id: itemId, content: { - __typename: 'DraftIssue', id: contentId, body: 'This is the item {{ foo }}', title: 'Item {{ foo }}' - } + }, + type: 'DRAFT_ISSUE' } ]); vi.mocked(editItem).mockImplementation(() => { diff --git a/__tests__/delete-project.test.mts b/__tests__/delete-project.test.mts index bcf4c36..1389238 100644 --- a/__tests__/delete-project.test.mts +++ b/__tests__/delete-project.test.mts @@ -73,4 +73,15 @@ describe('deleteProjectAction', () => { expect(core.setFailed).toHaveBeenCalledTimes(1); expect(core.setFailed).toHaveBeenLastCalledWith('42'); }); + + it('passes inputs correctly', async () => { + mockGetInput({ owner, 'project-number': projectNumber }); + vi.mocked(deleteProject).mockResolvedValue(); + + await index.deleteProjectAction(); + expect(deleteProjectActionSpy).toHaveReturned(); + + expect(deleteProject).toHaveBeenCalledTimes(1); + expect(deleteProject).toHaveBeenLastCalledWith(owner, projectNumber); + }); }); diff --git a/__tests__/edit-item.test.mts b/__tests__/edit-item.test.mts index cbac266..398987a 100644 --- a/__tests__/edit-item.test.mts +++ b/__tests__/edit-item.test.mts @@ -112,7 +112,7 @@ describe('editItemAction', () => { mockGetInput({ owner, 'project-number': projectNumber, item }); vi.mocked(getItem).mockResolvedValue({ id: itemId, - content: { type: 'PullRequest' } + type: 'PULL_REQUEST' } as ItemDetails); vi.mocked(editItem).mockImplementation(() => { throw new ProjectNotFoundError(); @@ -151,6 +151,26 @@ describe('editItemAction', () => { expect(core.setFailed).toHaveBeenLastCalledWith('42'); }); + it('cannot edit redacted items', async () => { + mockGetInput({ + owner, + 'project-number': projectNumber, + item, + title: 'New Title' + }); + vi.mocked(getItem).mockResolvedValue({ + type: 'REDACTED' + } as ItemDetails); + + await index.editItemAction(); + expect(editItemActionSpy).toHaveReturned(); + + expect(core.setFailed).toHaveBeenCalledTimes(1); + expect(core.setFailed).toHaveBeenLastCalledWith( + 'Cannot edit redacted items' + ); + }); + it('can only set title/body for draft issues', async () => { mockGetInput({ owner, @@ -159,7 +179,7 @@ describe('editItemAction', () => { title: 'New Title' }); vi.mocked(getItem).mockResolvedValue({ - content: { type: 'PullRequest' } + type: 'PULL_REQUEST' } as ItemDetails); await index.editItemAction(); @@ -183,7 +203,7 @@ describe('editItemAction', () => { }); vi.mocked(getItem).mockResolvedValue({ id: itemId, - content: { type: 'PullRequest' }, + type: 'PULL_REQUEST', projectId } as ItemDetails); vi.mocked(editItem).mockResolvedValue(itemId); @@ -209,7 +229,7 @@ describe('editItemAction', () => { }); vi.mocked(getItem).mockResolvedValue({ id: itemId, - content: { type: 'DraftIssue' }, + type: 'DRAFT_ISSUE', projectId } as ItemDetails); vi.mocked(editItem).mockResolvedValue(itemId); @@ -224,7 +244,7 @@ describe('editItemAction', () => { mockGetInput({ owner, 'project-number': projectNumber, item }); vi.mocked(getItem).mockResolvedValue({ id: itemId, - content: { type: 'PullRequest' }, + type: 'PULL_REQUEST', projectId } as ItemDetails); vi.mocked(editItem).mockResolvedValue(itemId); diff --git a/__tests__/get-item.test.mts b/__tests__/get-item.test.mts index d00f720..e61125c 100644 --- a/__tests__/get-item.test.mts +++ b/__tests__/get-item.test.mts @@ -128,9 +128,10 @@ describe('getItemAction', () => { }); vi.mocked(getItem).mockResolvedValue({ id: itemId, - content: { id: contentId, type: 'PullRequest', url, title, body }, + content: { id: contentId, url, title, body }, field: { id: fieldId, value: fieldValue }, - projectId + projectId, + type: 'PULL_REQUEST' }); await index.getItemAction(); @@ -161,9 +162,10 @@ describe('getItemAction', () => { }); vi.mocked(getItem).mockResolvedValue({ id: itemId, - content: { id: contentId, type: 'PullRequest', url, title, body }, + content: { id: contentId, url, title, body }, field: { id: fieldId, value: null }, - projectId + projectId, + type: 'PULL_REQUEST' }); await index.getItemAction(); @@ -178,4 +180,29 @@ describe('getItemAction', () => { expect(core.setOutput).toHaveBeenCalledWith('project-id', projectId); expect(core.setOutput).toHaveBeenCalledWith('field-id', fieldId); }); + + it('handles redacted items', async () => { + mockGetInput({ + owner, + 'project-number': projectNumber, + item, + field: 'Status' + }); + vi.mocked(getItem).mockResolvedValue({ + id: itemId, + content: null, + projectId, + type: 'REDACTED' + }); + + await index.getItemAction(); + expect(getItemActionSpy).toHaveReturned(); + + expect(core.setOutput).toHaveBeenCalledTimes(5); + expect(core.setOutput).toHaveBeenCalledWith('id', itemId); + expect(core.setOutput).toHaveBeenCalledWith('body', null); + expect(core.setOutput).toHaveBeenCalledWith('title', null); + expect(core.setOutput).toHaveBeenCalledWith('content-id', null); + expect(core.setOutput).toHaveBeenCalledWith('project-id', projectId); + }); }); diff --git a/__tests__/helpers.test.ts b/__tests__/helpers.test.ts index 9108cdc..311e336 100644 --- a/__tests__/helpers.test.ts +++ b/__tests__/helpers.test.ts @@ -1,6 +1,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import type { Mock } from 'vitest'; +import * as core from '@actions/core'; import * as exec from '@actions/exec'; import * as tc from '@actions/tool-cache'; import { Octokit } from '@octokit/core'; @@ -108,6 +109,30 @@ describe('helpers', () => { ); }); + it('sets GH_DEBUG if core.isDebug() is true', async () => { + const args = ['project', 'view']; + const stdout = 'output'; + const token = 'gh-token'; + mockGetInput({ token }); + vi.mocked(core.isDebug).mockReturnValue(true); + vi.mocked(exec.getExecOutput).mockResolvedValue({ + exitCode: 0, + stdout, + stderr: '' + }); + await expect(helpers.execCliCommand(args)).resolves.toEqual(stdout); + expect(exec.getExecOutput).toHaveBeenCalledWith( + expect.anything(), + args, + expect.objectContaining({ + env: { + GH_DEBUG: 'api', + GH_TOKEN: token + } + }) + ); + }); + it('throws error on non-zero exit code', async () => { const args = ['project', 'view']; const stdout = 'output'; diff --git a/__tests__/lib.test.ts b/__tests__/lib.test.ts index e6376b3..2bf9644 100644 --- a/__tests__/lib.test.ts +++ b/__tests__/lib.test.ts @@ -143,31 +143,36 @@ describe('lib', () => { { id: 'DI_one', content: { - __typename: 'DraftIssue', id: 'content-id-one', body: 'Body One', title: 'Title One' - } + }, + type: 'DRAFT_ISSUE' }, { id: 'PR_two', content: { - __typename: 'PullRequest', id: 'content-id-two', body: 'Body Two', title: 'Title Two', url: 'foobar' - } + }, + type: 'PULL_REQUEST' }, { id: 'I_three', content: { - __typename: 'Issue', id: 'content-id-three', body: 'Body three', title: 'Title three', url: 'foobar-two' - } + }, + type: 'ISSUE' + }, + { + id: 'REDACTED_foo', + content: null, + type: 'REDACTED' } ]; const mockOctokit = mockGetOctokit(); @@ -253,20 +258,20 @@ describe('lib', () => { { id: 'DI_one', content: { - __typename: 'DraftIssue', id: 'content-id-one', body: 'Body One', title: 'Title One' - } + }, + type: 'DRAFT_ISSUE' }, { id: 'DI_two', content: { - __typename: 'DraftIssue', id: 'content-id-two', body: 'Body Two', title: 'Title Two' - } + }, + type: 'DRAFT_ISSUE' } ]; @@ -274,23 +279,28 @@ describe('lib', () => { { id: 'PR_two', content: { - __typename: 'PullRequest', id: 'content-id-two', body: 'Body Two', title: 'Title Two', url: 'foobar' - } + }, + type: 'PULL_REQUEST' + }, + { + id: 'REDACTED_foo', + content: null, + type: 'REDACTED' }, ...draftItems, { id: 'I_three', content: { - __typename: 'Issue', id: 'content-id-three', body: 'Body three', title: 'Title three', url: 'foobar-two' - } + }, + type: 'ISSUE' } ]; @@ -432,6 +442,14 @@ describe('lib', () => { lib.deleteItem(owner, projectNumber, 'foobar') ).rejects.toThrow(lib.ProjectNotFoundError); }); + + it('passes arguments correctly', async () => { + vi.mocked(execCliCommand).mockResolvedValue(''); + await lib.deleteItem(owner, projectNumber, 'foobar'); + expect(execCliCommand).toHaveBeenCalledWith( + expect.arrayContaining([owner, projectNumber]) + ); + }); }); describe('deleteProject', () => { @@ -441,6 +459,14 @@ describe('lib', () => { lib.ProjectNotFoundError ); }); + + it('passes arguments correctly', async () => { + vi.mocked(execCliCommand).mockResolvedValue(''); + await lib.deleteProject(owner, projectNumber); + expect(execCliCommand).toHaveBeenCalledWith( + expect.arrayContaining([owner, projectNumber]) + ); + }); }); describe('editItem', () => { @@ -980,12 +1006,12 @@ describe('lib', () => { { id: itemId, content: { - __typename: 'Issue', id: 'content-id-one', body: 'Body One', title: 'Title One', url: itemUrl - } + }, + type: 'ISSUE' } ]; const mockOctokit = mockGetOctokit(); @@ -1017,16 +1043,14 @@ describe('lib', () => { }) }); - const { __typename, ...content } = items[0].content; + const { content, type } = items[0]; await expect(lib.getItem(owner, projectNumber, itemUrl)).resolves.toEqual( { id: itemId, projectId, - content: { - type: __typename, - ...content - } + content, + type } ); }); @@ -1038,7 +1062,6 @@ describe('lib', () => { { id: itemId, content: { - __typename: 'Issue', id: 'content-id-one', body: 'Body One', title: 'Title One', @@ -1046,7 +1069,8 @@ describe('lib', () => { }, fieldValueByName: { singleSelectValue: fieldValue - } + }, + type: 'ISSUE' } ]; const mockOctokit = mockGetOctokit(); @@ -1081,21 +1105,19 @@ describe('lib', () => { }) }); - const { __typename, ...content } = items[0].content; + const { content, type } = items[0]; await expect( lib.getItem(owner, projectNumber, itemUrl, 'Name') ).resolves.toEqual({ id: itemId, projectId, - content: { - type: __typename, - ...content - }, + content, field: { id: fieldId, value: fieldValue - } + }, + type }); }); @@ -1132,13 +1154,13 @@ describe('lib', () => { { id: itemId, content: { - __typename: 'Issue', id: 'content-id-one', body: 'Body One', title: 'Title One', url: itemUrl }, - fieldValueByName: null + fieldValueByName: null, + type: 'ISSUE' } ]; const mockOctokit = mockGetOctokit(); @@ -1210,13 +1232,13 @@ describe('lib', () => { { id: itemId, content: { - __typename: 'Issue', id: 'content-id-one', body: 'Body One', title: 'Title One', url: itemUrl }, - fieldValueByName: null + fieldValueByName: null, + type: 'ISSUE' } ]; const mockOctokit = mockGetOctokit(); @@ -1251,21 +1273,19 @@ describe('lib', () => { }) }); - const { __typename, ...content } = items[0].content; + const { content, type } = items[0]; await expect( lib.getItem(owner, projectNumber, itemUrl, 'Name') ).resolves.toEqual({ id: itemId, projectId, - content: { - type: __typename, - ...content - }, + content, field: { id: fieldId, value: null - } + }, + type }); }); diff --git a/dist/add-item.js b/dist/add-item.js index dda88aa..fbe5373 100644 --- a/dist/add-item.js +++ b/dist/add-item.js @@ -23511,7 +23511,6 @@ function getOctokit() { // src/lib.ts var PROJECT_ITEM_CONTENT_FRAGMENT = ` content { - __typename ... on DraftIssue { id body @@ -23529,7 +23528,9 @@ var PROJECT_ITEM_CONTENT_FRAGMENT = ` body title } - }`; + } + id + type`; var PROJECT_ITEMS_QUERY = ` query paginate($cursor: String, $projectId: ID!) { projectV2: node(id: $projectId) { @@ -23537,7 +23538,6 @@ var PROJECT_ITEMS_QUERY = ` id items(first: 50, after: $cursor) { nodes { - id ${PROJECT_ITEM_CONTENT_FRAGMENT} } pageInfo { diff --git a/dist/archive-item.js b/dist/archive-item.js index 86bfc35..1b62b71 100644 --- a/dist/archive-item.js +++ b/dist/archive-item.js @@ -23511,7 +23511,6 @@ function getOctokit() { // src/lib.ts var PROJECT_ITEM_CONTENT_FRAGMENT = ` content { - __typename ... on DraftIssue { id body @@ -23529,7 +23528,9 @@ var PROJECT_ITEM_CONTENT_FRAGMENT = ` body title } - }`; + } + id + type`; var PROJECT_ITEMS_QUERY = ` query paginate($cursor: String, $projectId: ID!) { projectV2: node(id: $projectId) { @@ -23537,7 +23538,6 @@ var PROJECT_ITEMS_QUERY = ` id items(first: 50, after: $cursor) { nodes { - id ${PROJECT_ITEM_CONTENT_FRAGMENT} } pageInfo { @@ -23613,7 +23613,6 @@ async function getItem(owner, projectNumber, item, field) { } items(first: 50, after: $cursor) { nodes { - id fieldValueByName(name: $field) { ... on ProjectV2ItemFieldDateValue { date @@ -23652,17 +23651,14 @@ async function getItem(owner, projectNumber, item, field) { try { for await (const { projectV2 } of pageIterator) { for (const node of projectV2.items.nodes) { - if (node.id === item || node.content.id === item || node.content.__typename !== "DraftIssue" && node.content.url === item) { - const { __typename, ...content } = node.content; + if (node.id === item || node.content?.id === item || node.type !== "DRAFT_ISSUE" && node.type !== "REDACTED" && node.content.url === item) { const details = { id: node.id, projectId: project.id, - content: { - type: __typename, - ...content - } + content: node.content, + type: node.type }; - if ("field" in projectV2 && "fieldValueByName" in node) { + if (details.type !== "REDACTED" && "field" in projectV2 && "fieldValueByName" in node) { if (projectV2.field === null) { throw new FieldNotFoundError(); } diff --git a/dist/close-project.js b/dist/close-project.js index fe09752..c1fb7ba 100644 --- a/dist/close-project.js +++ b/dist/close-project.js @@ -22058,7 +22058,6 @@ async function execCliCommand(args) { // src/lib.ts var PROJECT_ITEM_CONTENT_FRAGMENT = ` content { - __typename ... on DraftIssue { id body @@ -22076,7 +22075,9 @@ var PROJECT_ITEM_CONTENT_FRAGMENT = ` body title } - }`; + } + id + type`; var PROJECT_ITEMS_QUERY = ` query paginate($cursor: String, $projectId: ID!) { projectV2: node(id: $projectId) { @@ -22084,7 +22085,6 @@ var PROJECT_ITEMS_QUERY = ` id items(first: 50, after: $cursor) { nodes { - id ${PROJECT_ITEM_CONTENT_FRAGMENT} } pageInfo { diff --git a/dist/completed-by.js b/dist/completed-by.js index 11848c1..1bdf352 100644 --- a/dist/completed-by.js +++ b/dist/completed-by.js @@ -23511,7 +23511,6 @@ function getOctokit() { // src/lib.ts var PROJECT_ITEM_CONTENT_FRAGMENT = ` content { - __typename ... on DraftIssue { id body @@ -23529,7 +23528,9 @@ var PROJECT_ITEM_CONTENT_FRAGMENT = ` body title } - }`; + } + id + type`; var PROJECT_ITEMS_QUERY = ` query paginate($cursor: String, $projectId: ID!) { projectV2: node(id: $projectId) { @@ -23537,7 +23538,6 @@ var PROJECT_ITEMS_QUERY = ` id items(first: 50, after: $cursor) { nodes { - id ${PROJECT_ITEM_CONTENT_FRAGMENT} } pageInfo { @@ -23598,7 +23598,7 @@ var SingleSelectOptionNotFoundError = class extends Error { } }; function isDraftIssue(item) { - return item.content.__typename === "DraftIssue"; + return item.type === "DRAFT_ISSUE"; } function handleCliError(error2) { if (error2 instanceof Error && error2.message.includes("Could not resolve to a ProjectV2")) { @@ -23619,7 +23619,7 @@ async function getAllItems(projectId) { try { for await (const { projectV2 } of pageIterator) { for (const node of projectV2.items.nodes) { - if (Object.keys(node.content).length) { + if (node.type === "REDACTED" || Object.keys(node.content).length) { items.push(node); } } diff --git a/dist/copy-project.js b/dist/copy-project.js index 5571d51..386a30e 100644 --- a/dist/copy-project.js +++ b/dist/copy-project.js @@ -23974,7 +23974,6 @@ function getOctokit() { // src/lib.ts var PROJECT_ITEM_CONTENT_FRAGMENT = ` content { - __typename ... on DraftIssue { id body @@ -23992,7 +23991,9 @@ var PROJECT_ITEM_CONTENT_FRAGMENT = ` body title } - }`; + } + id + type`; var PROJECT_ITEMS_QUERY = ` query paginate($cursor: String, $projectId: ID!) { projectV2: node(id: $projectId) { @@ -24000,7 +24001,6 @@ var PROJECT_ITEMS_QUERY = ` id items(first: 50, after: $cursor) { nodes { - id ${PROJECT_ITEM_CONTENT_FRAGMENT} } pageInfo { @@ -24066,7 +24066,7 @@ var TeamNotFoundError = class extends Error { } }; function isDraftIssue(item) { - return item.content.__typename === "DraftIssue"; + return item.type === "DRAFT_ISSUE"; } function handleCliError(error2) { if (error2 instanceof Error && error2.message.includes("Could not resolve to a ProjectV2")) { @@ -24087,7 +24087,7 @@ async function getAllItems(projectId) { try { for await (const { projectV2 } of pageIterator) { for (const node of projectV2.items.nodes) { - if (Object.keys(node.content).length) { + if (node.type === "REDACTED" || Object.keys(node.content).length) { items.push(node); } } diff --git a/dist/delete-item.js b/dist/delete-item.js index dcf154f..cc1e514 100644 --- a/dist/delete-item.js +++ b/dist/delete-item.js @@ -23511,7 +23511,6 @@ function getOctokit() { // src/lib.ts var PROJECT_ITEM_CONTENT_FRAGMENT = ` content { - __typename ... on DraftIssue { id body @@ -23529,7 +23528,9 @@ var PROJECT_ITEM_CONTENT_FRAGMENT = ` body title } - }`; + } + id + type`; var PROJECT_ITEMS_QUERY = ` query paginate($cursor: String, $projectId: ID!) { projectV2: node(id: $projectId) { @@ -23537,7 +23538,6 @@ var PROJECT_ITEMS_QUERY = ` id items(first: 50, after: $cursor) { nodes { - id ${PROJECT_ITEM_CONTENT_FRAGMENT} } pageInfo { @@ -23613,7 +23613,6 @@ async function getItem(owner, projectNumber, item, field) { } items(first: 50, after: $cursor) { nodes { - id fieldValueByName(name: $field) { ... on ProjectV2ItemFieldDateValue { date @@ -23652,17 +23651,14 @@ async function getItem(owner, projectNumber, item, field) { try { for await (const { projectV2 } of pageIterator) { for (const node of projectV2.items.nodes) { - if (node.id === item || node.content.id === item || node.content.__typename !== "DraftIssue" && node.content.url === item) { - const { __typename, ...content } = node.content; + if (node.id === item || node.content?.id === item || node.type !== "DRAFT_ISSUE" && node.type !== "REDACTED" && node.content.url === item) { const details = { id: node.id, projectId: project.id, - content: { - type: __typename, - ...content - } + content: node.content, + type: node.type }; - if ("field" in projectV2 && "fieldValueByName" in node) { + if (details.type !== "REDACTED" && "field" in projectV2 && "fieldValueByName" in node) { if (projectV2.field === null) { throw new FieldNotFoundError(); } diff --git a/dist/delete-project.js b/dist/delete-project.js index 798512b..a55f1f5 100644 --- a/dist/delete-project.js +++ b/dist/delete-project.js @@ -22058,7 +22058,6 @@ async function execCliCommand(args) { // src/lib.ts var PROJECT_ITEM_CONTENT_FRAGMENT = ` content { - __typename ... on DraftIssue { id body @@ -22076,7 +22075,9 @@ var PROJECT_ITEM_CONTENT_FRAGMENT = ` body title } - }`; + } + id + type`; var PROJECT_ITEMS_QUERY = ` query paginate($cursor: String, $projectId: ID!) { projectV2: node(id: $projectId) { @@ -22084,7 +22085,6 @@ var PROJECT_ITEMS_QUERY = ` id items(first: 50, after: $cursor) { nodes { - id ${PROJECT_ITEM_CONTENT_FRAGMENT} } pageInfo { diff --git a/dist/edit-item.js b/dist/edit-item.js index 5e05ce9..d48c4cb 100644 --- a/dist/edit-item.js +++ b/dist/edit-item.js @@ -23511,7 +23511,6 @@ function getOctokit() { // src/lib.ts var PROJECT_ITEM_CONTENT_FRAGMENT = ` content { - __typename ... on DraftIssue { id body @@ -23529,7 +23528,9 @@ var PROJECT_ITEM_CONTENT_FRAGMENT = ` body title } - }`; + } + id + type`; var PROJECT_ITEMS_QUERY = ` query paginate($cursor: String, $projectId: ID!) { projectV2: node(id: $projectId) { @@ -23537,7 +23538,6 @@ var PROJECT_ITEMS_QUERY = ` id items(first: 50, after: $cursor) { nodes { - id ${PROJECT_ITEM_CONTENT_FRAGMENT} } pageInfo { @@ -23623,7 +23623,6 @@ async function getItem(owner, projectNumber, item, field) { } items(first: 50, after: $cursor) { nodes { - id fieldValueByName(name: $field) { ... on ProjectV2ItemFieldDateValue { date @@ -23662,17 +23661,14 @@ async function getItem(owner, projectNumber, item, field) { try { for await (const { projectV2 } of pageIterator) { for (const node of projectV2.items.nodes) { - if (node.id === item || node.content.id === item || node.content.__typename !== "DraftIssue" && node.content.url === item) { - const { __typename, ...content } = node.content; + if (node.id === item || node.content?.id === item || node.type !== "DRAFT_ISSUE" && node.type !== "REDACTED" && node.content.url === item) { const details = { id: node.id, projectId: project.id, - content: { - type: __typename, - ...content - } + content: node.content, + type: node.type }; - if ("field" in projectV2 && "fieldValueByName" in node) { + if (details.type !== "REDACTED" && "field" in projectV2 && "fieldValueByName" in node) { if (projectV2.field === null) { throw new FieldNotFoundError(); } @@ -23881,7 +23877,11 @@ async function editItemAction() { if (failIfItemNotFound) core2.setFailed(`Item not found: ${item}`); return; } - if (fullItem.content.type !== "DraftIssue" && (!!title || !!body)) { + if (fullItem.type === "REDACTED") { + core2.setFailed("Cannot edit redacted items"); + return; + } + if (fullItem.type !== "DRAFT_ISSUE" && (!!title || !!body)) { core2.setFailed("Can only set title or body for draft issues"); return; } diff --git a/dist/edit-project.js b/dist/edit-project.js index e912e71..814d441 100644 --- a/dist/edit-project.js +++ b/dist/edit-project.js @@ -22058,7 +22058,6 @@ async function execCliCommand(args) { // src/lib.ts var PROJECT_ITEM_CONTENT_FRAGMENT = ` content { - __typename ... on DraftIssue { id body @@ -22076,7 +22075,9 @@ var PROJECT_ITEM_CONTENT_FRAGMENT = ` body title } - }`; + } + id + type`; var PROJECT_ITEMS_QUERY = ` query paginate($cursor: String, $projectId: ID!) { projectV2: node(id: $projectId) { @@ -22084,7 +22085,6 @@ var PROJECT_ITEMS_QUERY = ` id items(first: 50, after: $cursor) { nodes { - id ${PROJECT_ITEM_CONTENT_FRAGMENT} } pageInfo { diff --git a/dist/find-project.js b/dist/find-project.js index 54ce591..a77a1f5 100644 --- a/dist/find-project.js +++ b/dist/find-project.js @@ -22058,7 +22058,6 @@ async function execCliCommand(args) { // src/lib.ts var PROJECT_ITEM_CONTENT_FRAGMENT = ` content { - __typename ... on DraftIssue { id body @@ -22076,7 +22075,9 @@ var PROJECT_ITEM_CONTENT_FRAGMENT = ` body title } - }`; + } + id + type`; var PROJECT_ITEMS_QUERY = ` query paginate($cursor: String, $projectId: ID!) { projectV2: node(id: $projectId) { @@ -22084,7 +22085,6 @@ var PROJECT_ITEMS_QUERY = ` id items(first: 50, after: $cursor) { nodes { - id ${PROJECT_ITEM_CONTENT_FRAGMENT} } pageInfo { diff --git a/dist/find-workflow.js b/dist/find-workflow.js index 615f080..43b9f2d 100644 --- a/dist/find-workflow.js +++ b/dist/find-workflow.js @@ -23511,7 +23511,6 @@ function getOctokit() { // src/lib.ts var PROJECT_ITEM_CONTENT_FRAGMENT = ` content { - __typename ... on DraftIssue { id body @@ -23529,7 +23528,9 @@ var PROJECT_ITEM_CONTENT_FRAGMENT = ` body title } - }`; + } + id + type`; var PROJECT_ITEMS_QUERY = ` query paginate($cursor: String, $projectId: ID!) { projectV2: node(id: $projectId) { @@ -23537,7 +23538,6 @@ var PROJECT_ITEMS_QUERY = ` id items(first: 50, after: $cursor) { nodes { - id ${PROJECT_ITEM_CONTENT_FRAGMENT} } pageInfo { diff --git a/dist/get-item.js b/dist/get-item.js index b65a293..05a54a3 100644 --- a/dist/get-item.js +++ b/dist/get-item.js @@ -23511,7 +23511,6 @@ function getOctokit() { // src/lib.ts var PROJECT_ITEM_CONTENT_FRAGMENT = ` content { - __typename ... on DraftIssue { id body @@ -23529,7 +23528,9 @@ var PROJECT_ITEM_CONTENT_FRAGMENT = ` body title } - }`; + } + id + type`; var PROJECT_ITEMS_QUERY = ` query paginate($cursor: String, $projectId: ID!) { projectV2: node(id: $projectId) { @@ -23537,7 +23538,6 @@ var PROJECT_ITEMS_QUERY = ` id items(first: 50, after: $cursor) { nodes { - id ${PROJECT_ITEM_CONTENT_FRAGMENT} } pageInfo { @@ -23613,7 +23613,6 @@ async function getItem(owner, projectNumber, item, field) { } items(first: 50, after: $cursor) { nodes { - id fieldValueByName(name: $field) { ... on ProjectV2ItemFieldDateValue { date @@ -23652,17 +23651,14 @@ async function getItem(owner, projectNumber, item, field) { try { for await (const { projectV2 } of pageIterator) { for (const node of projectV2.items.nodes) { - if (node.id === item || node.content.id === item || node.content.__typename !== "DraftIssue" && node.content.url === item) { - const { __typename, ...content } = node.content; + if (node.id === item || node.content?.id === item || node.type !== "DRAFT_ISSUE" && node.type !== "REDACTED" && node.content.url === item) { const details = { id: node.id, projectId: project.id, - content: { - type: __typename, - ...content - } + content: node.content, + type: node.type }; - if ("field" in projectV2 && "fieldValueByName" in node) { + if (details.type !== "REDACTED" && "field" in projectV2 && "fieldValueByName" in node) { if (projectV2.field === null) { throw new FieldNotFoundError(); } @@ -23731,14 +23727,14 @@ async function getItemAction() { return; } core2.setOutput("id", fullItem.id); - core2.setOutput("body", fullItem.content.body); - core2.setOutput("content-id", fullItem.content.id); + core2.setOutput("body", fullItem.content?.body ?? null); + core2.setOutput("content-id", fullItem.content?.id ?? null); core2.setOutput("project-id", fullItem.projectId); - core2.setOutput("title", fullItem.content.title); - if (fullItem.content.type !== "DraftIssue") { + core2.setOutput("title", fullItem.content?.title ?? null); + if (fullItem.type === "ISSUE" || fullItem.type === "PULL_REQUEST") { core2.setOutput("url", fullItem.content.url); } - if (fullItem.field) { + if (fullItem.type !== "REDACTED" && fullItem.field) { core2.setOutput("field-id", fullItem.field.id); if (fullItem.field.value !== null) { core2.setOutput("field-value", fullItem.field.value); diff --git a/dist/get-project.js b/dist/get-project.js index 5f72a84..72aaf9f 100644 --- a/dist/get-project.js +++ b/dist/get-project.js @@ -22058,7 +22058,6 @@ async function execCliCommand(args) { // src/lib.ts var PROJECT_ITEM_CONTENT_FRAGMENT = ` content { - __typename ... on DraftIssue { id body @@ -22076,7 +22075,9 @@ var PROJECT_ITEM_CONTENT_FRAGMENT = ` body title } - }`; + } + id + type`; var PROJECT_ITEMS_QUERY = ` query paginate($cursor: String, $projectId: ID!) { projectV2: node(id: $projectId) { @@ -22084,7 +22085,6 @@ var PROJECT_ITEMS_QUERY = ` id items(first: 50, after: $cursor) { nodes { - id ${PROJECT_ITEM_CONTENT_FRAGMENT} } pageInfo { diff --git a/dist/get-workflow.js b/dist/get-workflow.js index b2caa10..6b836c9 100644 --- a/dist/get-workflow.js +++ b/dist/get-workflow.js @@ -23511,7 +23511,6 @@ function getOctokit() { // src/lib.ts var PROJECT_ITEM_CONTENT_FRAGMENT = ` content { - __typename ... on DraftIssue { id body @@ -23529,7 +23528,9 @@ var PROJECT_ITEM_CONTENT_FRAGMENT = ` body title } - }`; + } + id + type`; var PROJECT_ITEMS_QUERY = ` query paginate($cursor: String, $projectId: ID!) { projectV2: node(id: $projectId) { @@ -23537,7 +23538,6 @@ var PROJECT_ITEMS_QUERY = ` id items(first: 50, after: $cursor) { nodes { - id ${PROJECT_ITEM_CONTENT_FRAGMENT} } pageInfo { diff --git a/dist/github-script/index.js b/dist/github-script/index.js index e143bb3..85a80f0 100644 --- a/dist/github-script/index.js +++ b/dist/github-script/index.js @@ -37768,7 +37768,6 @@ const graphql_1 = __nccwpck_require__(7); const helpers_1 = __nccwpck_require__(1302); const PROJECT_ITEM_CONTENT_FRAGMENT = ` content { - __typename ... on DraftIssue { id body @@ -37786,7 +37785,9 @@ const PROJECT_ITEM_CONTENT_FRAGMENT = ` body title } - }`; + } + id + type`; const PROJECT_ITEMS_QUERY = ` query paginate($cursor: String, $projectId: ID!) { projectV2: node(id: $projectId) { @@ -37794,7 +37795,6 @@ const PROJECT_ITEMS_QUERY = ` id items(first: 50, after: $cursor) { nodes { - id ${PROJECT_ITEM_CONTENT_FRAGMENT} } pageInfo { @@ -37866,7 +37866,7 @@ class TeamNotFoundError extends Error { } exports.TeamNotFoundError = TeamNotFoundError; function isDraftIssue(item) { - return item.content.__typename === 'DraftIssue'; + return item.type === 'DRAFT_ISSUE'; } function handleCliError(error) { if (error instanceof Error && @@ -37903,7 +37903,6 @@ async function getItem(owner, projectNumber, item, field) { } items(first: 50, after: $cursor) { nodes { - id fieldValueByName(name: $field) { ... on ProjectV2ItemFieldDateValue { date @@ -37939,19 +37938,19 @@ async function getItem(owner, projectNumber, item, field) { for await (const { projectV2 } of pageIterator) { for (const node of projectV2.items.nodes) { if (node.id === item || - node.content.id === item || - (node.content.__typename !== 'DraftIssue' && + node.content?.id === item || + (node.type !== 'DRAFT_ISSUE' && + node.type !== 'REDACTED' && node.content.url === item)) { - const { __typename, ...content } = node.content; const details = { id: node.id, projectId: project.id, - content: { - type: __typename, - ...content - } + content: node.content, + type: node.type }; - if ('field' in projectV2 && 'fieldValueByName' in node) { + if (details.type !== 'REDACTED' && + 'field' in projectV2 && + 'fieldValueByName' in node) { if (projectV2.field === null) { throw new FieldNotFoundError(); } @@ -38002,7 +38001,7 @@ async function getAllItems(projectId) { try { for await (const { projectV2 } of pageIterator) { for (const node of projectV2.items.nodes) { - if (Object.keys(node.content).length) { + if (node.type === 'REDACTED' || Object.keys(node.content).length) { items.push(node); } } diff --git a/dist/link-project.js b/dist/link-project.js index 9fffc11..9f5acf1 100644 --- a/dist/link-project.js +++ b/dist/link-project.js @@ -23511,7 +23511,6 @@ function getOctokit() { // src/lib.ts var PROJECT_ITEM_CONTENT_FRAGMENT = ` content { - __typename ... on DraftIssue { id body @@ -23529,7 +23528,9 @@ var PROJECT_ITEM_CONTENT_FRAGMENT = ` body title } - }`; + } + id + type`; var PROJECT_ITEMS_QUERY = ` query paginate($cursor: String, $projectId: ID!) { projectV2: node(id: $projectId) { @@ -23537,7 +23538,6 @@ var PROJECT_ITEMS_QUERY = ` id items(first: 50, after: $cursor) { nodes { - id ${PROJECT_ITEM_CONTENT_FRAGMENT} } pageInfo { diff --git a/src/edit-item.ts b/src/edit-item.ts index 9824666..d3c9c45 100644 --- a/src/edit-item.ts +++ b/src/edit-item.ts @@ -29,7 +29,12 @@ export async function editItemAction(): Promise { return; } - if (fullItem.content.type !== 'DraftIssue' && (!!title || !!body)) { + if (fullItem.type === 'REDACTED') { + core.setFailed('Cannot edit redacted items'); + return; + } + + if (fullItem.type !== 'DRAFT_ISSUE' && (!!title || !!body)) { core.setFailed('Can only set title or body for draft issues'); return; } diff --git a/src/get-item.ts b/src/get-item.ts index 9ea503a..c27161a 100644 --- a/src/get-item.ts +++ b/src/get-item.ts @@ -22,16 +22,16 @@ export async function getItemAction(): Promise { } core.setOutput('id', fullItem.id); - core.setOutput('body', fullItem.content.body); - core.setOutput('content-id', fullItem.content.id); + core.setOutput('body', fullItem.content?.body ?? null); + core.setOutput('content-id', fullItem.content?.id ?? null); core.setOutput('project-id', fullItem.projectId); - core.setOutput('title', fullItem.content.title); + core.setOutput('title', fullItem.content?.title ?? null); - if (fullItem.content.type !== 'DraftIssue') { + if (fullItem.type === 'ISSUE' || fullItem.type === 'PULL_REQUEST') { core.setOutput('url', fullItem.content.url); } - if (fullItem.field) { + if (fullItem.type !== 'REDACTED' && fullItem.field) { core.setOutput('field-id', fullItem.field.id); if (fullItem.field.value !== null) { diff --git a/src/lib.ts b/src/lib.ts index cd9b4f8..184baa2 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -3,34 +3,8 @@ import type { PageInfoForward } from '@octokit/plugin-paginate-graphql'; import { getOctokit, execCliCommand } from './helpers'; -interface DraftIssueContent { - type: 'DraftIssue'; - id: string; - body: string; - title: string; -} - -interface IssueContent { - type: 'Issue'; - id: string; - url: string; - body: string; - title: string; -} - -interface PullRequestContent { - type: 'PullRequest'; - id: string; - url: string; - body: string; - title: string; -} - -type ItemContent = DraftIssueContent | IssueContent | PullRequestContent; - const PROJECT_ITEM_CONTENT_FRAGMENT = ` content { - __typename ... on DraftIssue { id body @@ -48,7 +22,9 @@ const PROJECT_ITEM_CONTENT_FRAGMENT = ` body title } - }`; + } + id + type`; const PROJECT_ITEMS_QUERY = ` query paginate($cursor: String, $projectId: ID!) { @@ -57,7 +33,6 @@ const PROJECT_ITEMS_QUERY = ` id items(first: 50, after: $cursor) { nodes { - id ${PROJECT_ITEM_CONTENT_FRAGMENT} } pageInfo { @@ -95,16 +70,24 @@ const PROJECT_WORKFLOWS_QUERY = ` } }`; -export interface ItemDetails { +interface ItemField { id: string; - projectId: string; - content: ItemContent; - field?: { - id: string; - value: string | number | null; - }; + value: string | number | null; } +export type ItemDetails = { + id: string; + projectId: string; +} & ( + | { content: DraftIssueItemContent; field?: ItemField; type: 'DRAFT_ISSUE' } + | { + content: ProjectItemContent; + field?: ItemField; + type: 'ISSUE' | 'PULL_REQUEST'; + } + | { content: null; type: 'REDACTED' } +); + export interface ProjectDetails { number: number; url: string; @@ -179,42 +162,37 @@ export class TeamNotFoundError extends Error { } } -type DraftIssueItemContent = { - __typename: 'DraftIssue'; +export type ProjectItemContent = { id: string; + url: string; body: string; title: string; }; -export type ProjectItemContent = - | DraftIssueItemContent +type DraftIssueItemContent = Omit; + +export type ProjectItem = | { - __typename: 'Issue' | 'PullRequest'; id: string; - url: string; - body: string; - title: string; - }; + content: ProjectItemContent; + type: 'ISSUE' | 'PULL_REQUEST'; + } + | DraftIssueItem + | { id: string; content: null; type: 'REDACTED' }; -export type ProjectItemWithFieldValue = { - id: string; +export type ProjectItemWithFieldValue = ProjectItem & { fieldValueByName: { date?: string; text?: string; number?: number; singleSelectValue?: string; } | null; - content: ProjectItemContent; -}; - -export type ProjectItem = { - id: string; - content: ProjectItemContent; }; export type DraftIssueItem = { id: string; content: DraftIssueItemContent; + type: 'DRAFT_ISSUE'; }; export type FieldTypeResponse = { @@ -296,7 +274,7 @@ export type SingleSelectOptionIdResponse = { }; function isDraftIssue(item: ProjectItem): item is DraftIssueItem { - return item.content.__typename === 'DraftIssue'; + return item.type === 'DRAFT_ISSUE'; } export function handleCliError(error: unknown): never { @@ -348,7 +326,6 @@ export async function getItem( } items(first: 50, after: $cursor) { nodes { - id fieldValueByName(name: $field) { ... on ProjectV2ItemFieldDateValue { date @@ -390,22 +367,23 @@ export async function getItem( for (const node of projectV2.items.nodes) { if ( node.id === item || - node.content.id === item || - (node.content.__typename !== 'DraftIssue' && + node.content?.id === item || + (node.type !== 'DRAFT_ISSUE' && + node.type !== 'REDACTED' && node.content.url === item) ) { - const { __typename, ...content } = node.content; - - const details: ItemDetails = { + const details = { id: node.id, projectId: project.id, - content: { - type: __typename, - ...content - } as ItemContent - }; - - if ('field' in projectV2 && 'fieldValueByName' in node) { + content: node.content, + type: node.type + } as ItemDetails; + + if ( + details.type !== 'REDACTED' && + 'field' in projectV2 && + 'fieldValueByName' in node + ) { if (projectV2.field === null) { throw new FieldNotFoundError(); } @@ -465,7 +443,7 @@ export async function getAllItems(projectId: string): Promise { try { for await (const { projectV2 } of pageIterator) { for (const node of projectV2.items.nodes) { - if (Object.keys(node.content).length) { + if (node.type === 'REDACTED' || Object.keys(node.content).length) { items.push(node); } }