Skip to content
This repository was archived by the owner on Jul 11, 2023. It is now read-only.

Commit b1e1b76

Browse files
authored
Merge pull request #25 from jonabc/cards-pagination
Paginate cards from project columns
2 parents dd65292 + c336c53 commit b1e1b76

6 files changed

Lines changed: 174 additions & 11 deletions

File tree

dist/index.js

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1908,15 +1908,19 @@ url
19081908
project {
19091909
name
19101910
}
1911-
cards(first: 50, archivedStates: [NOT_ARCHIVED]) {
1911+
cards(first: 50, archivedStates: [NOT_ARCHIVED], after: $after) {
19121912
nodes {
19131913
${projectCardFields}
19141914
}
1915+
pageInfo {
1916+
hasNextPage
1917+
endCursor
1918+
}
19151919
}
19161920
`.trim();
19171921

19181922
const GET_PROJECT_COLUMNS = `
1919-
query($sourceColumnIds: [ID!]!, $targetColumnId: ID!) {
1923+
query($sourceColumnIds: [ID!]!, $targetColumnId: ID!, $after: String) {
19201924
sourceColumns: nodes(ids: $sourceColumnIds) {
19211925
... on ProjectColumn {
19221926
${projectColumnFields}
@@ -1930,6 +1934,16 @@ query($sourceColumnIds: [ID!]!, $targetColumnId: ID!) {
19301934
}
19311935
`.trim();
19321936

1937+
const GET_SINGLE_PROJECT_COLUMN = `
1938+
query($id: ID!, $after: String) {
1939+
column: node(id: $id) {
1940+
... on ProjectColumn {
1941+
${projectColumnFields}
1942+
}
1943+
}
1944+
}
1945+
`.trim();
1946+
19331947
const ADD_PROJECT_CARD = `
19341948
mutation addProjectCard($columnId: ID!, $contentId: ID, $note: String) {
19351949
addProjectCard(input: { projectColumnId: $columnId, contentId: $contentId, note: $note }) {
@@ -1964,6 +1978,7 @@ mutation deleteProjectCard($cardId: ID!) {
19641978

19651979
module.exports = {
19661980
GET_PROJECT_COLUMNS,
1981+
GET_SINGLE_PROJECT_COLUMN,
19671982
ADD_PROJECT_CARD,
19681983
MOVE_PROJECT_CARD,
19691984
DELETE_PROJECT_CARD
@@ -5948,6 +5963,29 @@ ${columnReferences.join('\n')}
59485963
`.trim();
59495964
}
59505965

5966+
// Paginate project cards from all columns if/as needed
5967+
async function paginateColumnCards(api, columns) {
5968+
for (let i = 0; i < columns.length; i += 1) {
5969+
const originalColumn = columns[i];
5970+
5971+
let currentColumn = originalColumn;
5972+
while (currentColumn.cards.pageInfo.hasNextPage) {
5973+
core.info(
5974+
`paginating ${currentColumn.project.name}:${currentColumn.name} after ${currentColumn.cards.pageInfo.endCursor}`
5975+
);
5976+
5977+
// eslint-disable-next-line no-await-in-loop
5978+
const { column } = await api(queries.GET_SINGLE_PROJECT_COLUMN, {
5979+
id: currentColumn.id,
5980+
after: currentColumn.cards.pageInfo.endCursor
5981+
});
5982+
5983+
originalColumn.cards.nodes.push(...column.cards.nodes);
5984+
currentColumn = column;
5985+
}
5986+
}
5987+
}
5988+
59515989
// Find a card in an array of cards based on it's linked content, or it's note.
59525990
// Returns an array of [found card, index of found card]
59535991
function findCard(card, cards) {
@@ -6018,14 +6056,16 @@ async function run() {
60186056
const sourceColumnIds = utils.getInputList(core.getInput('source_column_id', { required: true }));
60196057
const targetColumnId = core.getInput('target_column_id', { required: true });
60206058

6021-
const response = await api(queries.GET_PROJECT_COLUMNS, {
6059+
const { sourceColumns, targetColumn } = await api(queries.GET_PROJECT_COLUMNS, {
60226060
sourceColumnIds,
60236061
targetColumnId
60246062
});
60256063

6064+
// paginate to gather all cards if needed
6065+
await paginateColumnCards(api, [...sourceColumns, targetColumn]);
6066+
60266067
// apply user supplied filters to cards from the source column and mirror the
60276068
// target column based on the remaining filters
6028-
const { sourceColumns, targetColumn } = response;
60296069
const sourceCards = sourceColumns.flatMap(column => {
60306070
return applyFilters(column.cards.nodes, [...Object.values(utils.filters)]);
60316071
});

src/graphql.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,19 @@ url
3030
project {
3131
name
3232
}
33-
cards(first: 50, archivedStates: [NOT_ARCHIVED]) {
33+
cards(first: 50, archivedStates: [NOT_ARCHIVED], after: $after) {
3434
nodes {
3535
${projectCardFields}
3636
}
37+
pageInfo {
38+
hasNextPage
39+
endCursor
40+
}
3741
}
3842
`.trim();
3943

4044
const GET_PROJECT_COLUMNS = `
41-
query($sourceColumnIds: [ID!]!, $targetColumnId: ID!) {
45+
query($sourceColumnIds: [ID!]!, $targetColumnId: ID!, $after: String) {
4246
sourceColumns: nodes(ids: $sourceColumnIds) {
4347
... on ProjectColumn {
4448
${projectColumnFields}
@@ -52,6 +56,16 @@ query($sourceColumnIds: [ID!]!, $targetColumnId: ID!) {
5256
}
5357
`.trim();
5458

59+
const GET_SINGLE_PROJECT_COLUMN = `
60+
query($id: ID!, $after: String) {
61+
column: node(id: $id) {
62+
... on ProjectColumn {
63+
${projectColumnFields}
64+
}
65+
}
66+
}
67+
`.trim();
68+
5569
const ADD_PROJECT_CARD = `
5670
mutation addProjectCard($columnId: ID!, $contentId: ID, $note: String) {
5771
addProjectCard(input: { projectColumnId: $columnId, contentId: $contentId, note: $note }) {
@@ -86,6 +100,7 @@ mutation deleteProjectCard($cardId: ID!) {
86100

87101
module.exports = {
88102
GET_PROJECT_COLUMNS,
103+
GET_SINGLE_PROJECT_COLUMN,
89104
ADD_PROJECT_CARD,
90105
MOVE_PROJECT_CARD,
91106
DELETE_PROJECT_CARD

src/linked-project-columns.js

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,29 @@ ${columnReferences.join('\n')}
2424
`.trim();
2525
}
2626

27+
// Paginate project cards from all columns if/as needed
28+
async function paginateColumnCards(api, columns) {
29+
for (let i = 0; i < columns.length; i += 1) {
30+
const originalColumn = columns[i];
31+
32+
let currentColumn = originalColumn;
33+
while (currentColumn.cards.pageInfo.hasNextPage) {
34+
core.info(
35+
`paginating ${currentColumn.project.name}:${currentColumn.name} after ${currentColumn.cards.pageInfo.endCursor}`
36+
);
37+
38+
// eslint-disable-next-line no-await-in-loop
39+
const { column } = await api(queries.GET_SINGLE_PROJECT_COLUMN, {
40+
id: currentColumn.id,
41+
after: currentColumn.cards.pageInfo.endCursor
42+
});
43+
44+
originalColumn.cards.nodes.push(...column.cards.nodes);
45+
currentColumn = column;
46+
}
47+
}
48+
}
49+
2750
// Find a card in an array of cards based on it's linked content, or it's note.
2851
// Returns an array of [found card, index of found card]
2952
function findCard(card, cards) {
@@ -94,14 +117,16 @@ async function run() {
94117
const sourceColumnIds = utils.getInputList(core.getInput('source_column_id', { required: true }));
95118
const targetColumnId = core.getInput('target_column_id', { required: true });
96119

97-
const response = await api(queries.GET_PROJECT_COLUMNS, {
120+
const { sourceColumns, targetColumn } = await api(queries.GET_PROJECT_COLUMNS, {
98121
sourceColumnIds,
99122
targetColumnId
100123
});
101124

125+
// paginate to gather all cards if needed
126+
await paginateColumnCards(api, [...sourceColumns, targetColumn]);
127+
102128
// apply user supplied filters to cards from the source column and mirror the
103129
// target column based on the remaining filters
104-
const { sourceColumns, targetColumn } = response;
105130
const sourceCards = sourceColumns.flatMap(column => {
106131
return applyFilters(column.cards.nodes, [...Object.values(utils.filters)]);
107132
});

test/fixtures/get-project-columns.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
"name": "source project"
99
},
1010
"cards": {
11-
"nodes": []
11+
"nodes": [],
12+
"pageInfo": {
13+
"hasNextPage": false,
14+
"endCursor": null
15+
}
1216
}
1317
}
1418
],
@@ -20,7 +24,11 @@
2024
"name": "target project"
2125
},
2226
"cards": {
23-
"nodes": []
27+
"nodes": [],
28+
"pageInfo": {
29+
"hasNextPage": false,
30+
"endCursor": null
31+
}
2432
}
2533
}
2634
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"column": {
3+
"id": 1,
4+
"name": "source column",
5+
"url": "https://example.com/projects/1/columns/1",
6+
"project": {
7+
"name": "source project"
8+
},
9+
"cards": {
10+
"nodes": [],
11+
"pageInfo": {
12+
"hasNextPage": false,
13+
"endCursor": null
14+
}
15+
}
16+
}
17+
}

test/linked-project-columns.test.js

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ describe('linked-project-columns', () => {
1717
const getColumnsFixture = readFileSync(resolvePath(__dirname, './fixtures/get-project-columns.json'), {
1818
encoding: 'utf8'
1919
});
20+
const getSingleColumnFixture = readFileSync(resolvePath(__dirname, './fixtures/get-single-project-column.json'), {
21+
encoding: 'utf8'
22+
});
2023
const deleteCardFixture = readFileSync(resolvePath(__dirname, './fixtures/delete-project-card.json'), {
2124
encoding: 'utf8'
2225
});
@@ -28,6 +31,7 @@ describe('linked-project-columns', () => {
2831
});
2932

3033
let getColumnsResponse;
34+
let getSingleColumnResponse;
3135

3236
beforeEach(() => {
3337
process.env = {
@@ -45,8 +49,10 @@ describe('linked-project-columns', () => {
4549
sinon.stub(octokit.graphql, 'defaults').returns(api);
4650

4751
getColumnsResponse = JSON.parse(getColumnsFixture);
52+
getSingleColumnResponse = JSON.parse(getSingleColumnFixture);
4853

4954
api.withArgs(queries.GET_PROJECT_COLUMNS).resolves(getColumnsResponse);
55+
api.withArgs(queries.GET_SINGLE_PROJECT_COLUMN).resolves(getSingleColumnResponse);
5056
api.withArgs(queries.DELETE_PROJECT_CARD).callsFake((query, input) => {
5157
const response = JSON.parse(deleteCardFixture);
5258
response.deleteProjectCard.deletedCardId = input.cardId;
@@ -483,6 +489,54 @@ describe('linked-project-columns', () => {
483489
expect(api.getCall(0).args[0]).toContain('archivedStates: [NOT_ARCHIVED]');
484490
});
485491

492+
it('gathers additional pages of cards for source columns', async () => {
493+
getColumnsResponse.sourceColumns[0].cards.pageInfo.hasNextPage = true;
494+
getColumnsResponse.sourceColumns[0].cards.pageInfo.endCursor = 'abc';
495+
getSingleColumnResponse.column.cards.nodes.push({ id: 1, note: '1' }, { id: 2, note: '2' });
496+
497+
await run();
498+
499+
expect(core.warning.callCount).toEqual(0);
500+
expect(core.setFailed.callCount).toEqual(0);
501+
expect(api.callCount).toEqual(7);
502+
// call 0 -> get columns
503+
expect(api.getCall(1).args).toEqual([
504+
queries.GET_SINGLE_PROJECT_COLUMN,
505+
{
506+
id: getColumnsResponse.sourceColumns[0].id,
507+
after: 'abc'
508+
}
509+
]);
510+
// call 2 -> add automation note
511+
expect(api.getCall(3).args).toEqual([queries.ADD_PROJECT_CARD, { columnId: 2, note: '1' }]);
512+
expect(api.getCall(4).args).toEqual([queries.MOVE_PROJECT_CARD, { columnId: 2, cardId: 201, afterCardId: 200 }]);
513+
expect(api.getCall(5).args).toEqual([queries.ADD_PROJECT_CARD, { columnId: 2, note: '2' }]);
514+
expect(api.getCall(6).args).toEqual([queries.MOVE_PROJECT_CARD, { columnId: 2, cardId: 202, afterCardId: 201 }]);
515+
});
516+
517+
it('gathers additional pages of cards for the target column', async () => {
518+
getColumnsResponse.targetColumn.cards.pageInfo.hasNextPage = true;
519+
getColumnsResponse.targetColumn.cards.pageInfo.endCursor = 'abc';
520+
getSingleColumnResponse.column.cards.nodes.push({ id: 1, note: '1' }, { id: 2, note: '2' });
521+
522+
await run();
523+
524+
expect(core.warning.callCount).toEqual(0);
525+
expect(core.setFailed.callCount).toEqual(0);
526+
expect(api.callCount).toEqual(5);
527+
// call 0 -> get columns
528+
expect(api.getCall(1).args).toEqual([
529+
queries.GET_SINGLE_PROJECT_COLUMN,
530+
{
531+
id: getColumnsResponse.targetColumn.id,
532+
after: 'abc'
533+
}
534+
]);
535+
expect(api.getCall(2).args).toEqual([queries.DELETE_PROJECT_CARD, { cardId: 2 }]);
536+
expect(api.getCall(3).args).toEqual([queries.DELETE_PROJECT_CARD, { cardId: 1 }]);
537+
// call 4 -> add automation note
538+
});
539+
486540
describe('with multiple source columns', () => {
487541
const secondSourceColumnId = 'second';
488542

@@ -496,7 +550,11 @@ describe('linked-project-columns', () => {
496550
name: 'source project'
497551
},
498552
cards: {
499-
nodes: []
553+
nodes: [],
554+
pageInfo: {
555+
hasNextPage: false,
556+
endCursor: null
557+
}
500558
}
501559
});
502560
});

0 commit comments

Comments
 (0)