Skip to content

Commit 44ec85f

Browse files
authored
refactor to move to cards
1 parent 52196a1 commit 44ec85f

File tree

8 files changed

+192
-68
lines changed

8 files changed

+192
-68
lines changed

src/interfaces/IParsedIssue.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
export interface IParsedIssue {
2-
owner: string;
3-
repo: string;
1+
import { IParsedRepo } from './IParsedRepo';
2+
3+
export interface IParsedIssue extends IParsedRepo {
44
issueNumber: number;
55
}

src/interfaces/IParsedRepo.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface IParsedRepo {
2+
owner: string;
3+
repo: string;
4+
}

src/interfaces/TProjectColumn.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
import { TProjectColumns } from './TProjectColumns';
1+
import { TProjectColumnsResponse } from './TProjectColumnsResponse';
22

3-
export type TProjectColumn = TProjectColumns[0];
3+
export type TProjectColumn = TProjectColumnsResponse['data'][0];

src/interfaces/TProjectColumns.ts

Lines changed: 0 additions & 3 deletions
This file was deleted.

src/octokit/ProjectsOctoKit.ts

Lines changed: 144 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,38 @@
11
import { OctoKitBase } from './OctoKitBase';
2-
import { parseIssueUrl } from '../utils/parseIssueUrl';
2+
import { parseIssueApiUrl, parseIssueUrl } from '../utils/parseIssueUrl';
33
import { parseFileUrl } from '../utils/parseFileUrl';
44
import { notEmpty } from '../utils/notEmpty';
55

66
import { IRepoSourceConfig } from '../interfaces/IRepoSourceConfig';
77
import { TColumnCard } from '../interfaces/TColumnCard';
8-
import { TProject } from '../interfaces/TProject';
9-
import { TProjectColumn } from '../interfaces/TProjectColumn';
10-
import { TProjectColumns } from '../interfaces/TProjectColumns';
8+
// import { TProject } from '../interfaces/TProject';
119
import { TRepoIssue } from '../interfaces/TRepoIssue';
1210
import { TColumnTypes } from '../interfaces/TColumnTypes';
1311
import { IWrappedIssue } from '../interfaces/IWrappedIssue';
1412
import { IProject } from '../interfaces/IProject';
1513
import { IProjectWithConfig } from '../interfaces/IProjectWithConfig';
14+
import { IParsedIssue } from '../interfaces/IParsedIssue';
15+
import { TProjectColumn } from '../interfaces/TProjectColumn';
16+
import { flattenArray } from '../utils/flatternArray';
17+
import { IParsedRepo } from '../interfaces/IParsedRepo';
18+
// import { IParsedIssue } from '../interfaces/IParsedIssue';
19+
// import { serializeIssuePath } from '../utils/serializeIssuePath';
20+
// import { serializeIssuePath } from '../utils/serializeIssuePath';
21+
22+
interface IColumnWithCards {
23+
column: TProjectColumn;
24+
cards: TColumnCard[];
25+
}
1626

1727
type TColumnsMap = Record<TColumnTypes, TProjectColumn | undefined>;
28+
type TColumnsWithCardsMap = Record<TColumnTypes, IColumnWithCards | undefined>
29+
30+
type TRepoIssuesMap = Record<string, TRepoIssue[]>
1831

19-
const findColumn = (columns: TProjectColumns, columnName: TColumnTypes) => {
32+
const findColumn = (
33+
columns: TProjectColumn[],
34+
columnName: TColumnTypes,
35+
): TProjectColumn | undefined => {
2036
const result = columns.find((column) => {
2137
return column.name.toLowerCase() === columnName.toLowerCase();
2238
});
@@ -33,21 +49,21 @@ const wrapIssue = (column: TColumnTypes) => {
3349
};
3450
};
3551

36-
const findColumnThrows = (
37-
projectName: string,
38-
columns: TProjectColumns,
39-
columnName: TColumnTypes,
40-
) => {
41-
const result = findColumn(columns, columnName);
52+
// const findColumnThrows = (
53+
// projectName: string,
54+
// columns: TProjectColumn[],
55+
// columnName: TColumnTypes,
56+
// ) => {
57+
// const result = findColumn(columns, columnName);
4258

43-
if (!result) {
44-
throw new Error(
45-
`No column "${columnName}" found in project "projectName".`,
46-
);
47-
}
59+
// if (!result) {
60+
// throw new Error(
61+
// `No column "${columnName}" found in project "projectName".`,
62+
// );
63+
// }
4864

49-
return result;
50-
};
65+
// return result;
66+
// };
5167

5268
const getProjectId = (project: IProject | number) => {
5369
return typeof project === 'number' ? project : project.id;
@@ -64,29 +80,28 @@ export class ProjectsOctoKit extends OctoKitBase {
6480
per_page: 100,
6581
});
6682

67-
const fetchedProjects = projectsResponse.map((project):
68-
| IProjectWithConfig
69-
| undefined => {
70-
const { projects } = repo;
83+
const fetchedProjects = projectsResponse
84+
.map((project): IProjectWithConfig | undefined => {
85+
const { projects } = repo;
7186

72-
if (!projects) {
73-
return;
74-
}
87+
if (!projects) {
88+
return;
89+
}
7590

76-
const proj = projects.find((p) => {
77-
return project.number === getProjectId(p);
78-
});
91+
const proj = projects.find((p) => {
92+
return project.number === getProjectId(p);
93+
});
7994

80-
if (!proj) {
81-
return;
82-
}
95+
if (!proj) {
96+
return;
97+
}
8398

84-
return {
85-
project,
86-
projectConfig: proj,
87-
};
88-
})
89-
.filter(notEmpty);
99+
return {
100+
project,
101+
projectConfig: proj,
102+
};
103+
})
104+
.filter(notEmpty);
90105

91106
return fetchedProjects;
92107
};
@@ -107,7 +122,9 @@ export class ProjectsOctoKit extends OctoKitBase {
107122
return result;
108123
};
109124

110-
public getColumns = async (projectWithLabels: IProjectWithConfig): Promise<TColumnsMap> => {
125+
public getColumns = async (
126+
projectWithLabels: IProjectWithConfig,
127+
): Promise<TColumnsMap> => {
111128
const { project } = projectWithLabels;
112129

113130
const { data: columns } = await this.kit.projects.listColumns({
@@ -133,15 +150,45 @@ export class ProjectsOctoKit extends OctoKitBase {
133150
public getColumnCards = async (
134151
column: TProjectColumn,
135152
): Promise<TColumnCard[]> => {
136-
const { data: cards } = await this.kit.projects.listCards({
153+
const cards = await this.kit.paginate(this.kit.projects.listCards, {
137154
column_id: column.id,
138-
per_page: 100,
139155
archived_state: 'not_archived',
140156
});
141157

142158
return cards;
143159
};
144160

161+
public getCards = async (
162+
columns: TColumnsMap,
163+
): Promise<TColumnsWithCardsMap> => {
164+
const cardPromises = Object.entries(columns).map(async ([ type, column ]) => {
165+
if (!column) {
166+
return;
167+
}
168+
169+
return {
170+
type,
171+
column,
172+
cards: await this.getColumnCards(column),
173+
};
174+
});
175+
176+
const columnCardsWithEmpty = await Promise.all(cardPromises);
177+
const columnCards = columnCardsWithEmpty.filter(notEmpty);
178+
179+
const result: any = {};
180+
for (let columnCard of columnCards) {
181+
const { type } = columnCard;
182+
183+
result[type] = {
184+
cards: columnCard.cards,
185+
column: columnCard.column,
186+
};
187+
}
188+
189+
return result as TColumnsWithCardsMap;
190+
};
191+
145192
public getRepoIssues = async (
146193
repo: IRepoSourceConfig,
147194
): Promise<TRepoIssue[]> => {
@@ -154,26 +201,68 @@ export class ProjectsOctoKit extends OctoKitBase {
154201
return issues;
155202
};
156203

157-
public getIssuesForColumnCards = async (
158-
repo: IRepoSourceConfig,
159-
column: TProjectColumn,
204+
public getReposIssues = async (
205+
repos: IParsedRepo[],
160206
): Promise<TRepoIssue[]> => {
161-
const issues = await this.kit.paginate(this.kit.issues.listForRepo, {
162-
repo: repo.repo,
163-
owner: repo.owner,
207+
208+
const resultPromises: Promise<TRepoIssue[]>[] = repos.map(async (repo) => {
209+
return await this.getRepoIssues(repo);
164210
});
165211

166-
const cards = await this.getColumnCards(column);
212+
return flattenArray(await Promise.all(resultPromises));
213+
};
167214

168-
const cardIssues = issues.filter((issue) => {
169-
const cardIssue = cards.find((card) => {
170-
return card.content_url === issue.url;
171-
});
215+
private getCardIssueFromNote = (card: TColumnCard): IParsedIssue | null => {
216+
try {
217+
const issue = parseIssueUrl(card.note);
218+
return issue;
219+
} catch {
220+
return null;
221+
}
222+
};
172223

173-
return !!cardIssue;
224+
private getCardIssueFromContentUrl = (
225+
card: TColumnCard,
226+
): IParsedIssue | null => {
227+
try {
228+
const issue = parseIssueApiUrl(card.content_url);
229+
return issue;
230+
} catch {
231+
return null;
232+
}
233+
};
234+
235+
private getAllCards = (columns: TColumnsWithCardsMap): TColumnCard[] => {
236+
const cards = Object.entries(columns).map(([type, columnWithCards]) => {
237+
if (!columnWithCards) {
238+
return [];
239+
}
240+
const { cards } = columnWithCards;
241+
return cards ?? [];
174242
});
175243

176-
return cardIssues;
244+
return flattenArray(cards);
245+
};
246+
247+
public getCardRepos = (columns: TColumnsWithCardsMap): IParsedRepo[] => {
248+
const cards = this.getAllCards(columns);
249+
250+
const repos: Record<string, IParsedRepo> = {};
251+
252+
for (let card of cards) {
253+
const issueFromNote = this.getCardIssueFromNote(card);
254+
const issueFromContent = this.getCardIssueFromContentUrl(card);
255+
const issue = issueFromNote ?? issueFromContent;
256+
257+
if (issue && issue.owner && issue.repo) {
258+
repos[`/${issue.owner}/${issue.repo}`] = {
259+
owner: issue.owner,
260+
repo: issue.repo,
261+
};
262+
}
263+
}
264+
265+
return Object.values(repos);
177266
};
178267

179268
public filterIssuesForColumnCards = async (
@@ -220,19 +309,16 @@ export class ProjectsOctoKit extends OctoKitBase {
220309
const { status, data } = await this.kit.issues.get({
221310
owner,
222311
repo,
223-
issue_number: issueNumber
312+
issue_number: issueNumber,
224313
});
225314

226315
if (status !== 200) {
227-
throw new Error(
228-
`Failed to get the issue ${issueUrl}`,
229-
);
316+
throw new Error(`Failed to get the issue ${issueUrl}`);
230317
}
231318

232319
return data;
233320
};
234321

235-
236322
public getBoardHeaderText = async (fileUrl: string): Promise<string> => {
237323
const fileRef = parseFileUrl(fileUrl);
238324

src/utils/getProjectData.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ProjectsOctoKit } from '../octokit/ProjectsOctoKit';
22
import { TColumnTypes } from '../interfaces/TColumnTypes';
3-
import { TProject } from '../interfaces/TProject';
3+
// import { TProject } from '../interfaces/TProject';
44
import { IRepoSourceConfig } from '../interfaces/IRepoSourceConfig';
55
import { IProjectData } from '../interfaces/IProjectData';
66
import { IProjectWithConfig } from '../interfaces/IProjectWithConfig';
@@ -11,7 +11,23 @@ export const getProjectData = async (
1111
project: IProjectWithConfig,
1212
): Promise<IProjectData> => {
1313
const columns = await projectKit.getColumns(project);
14-
const issues = await projectKit.getRepoIssues(repo);
14+
const cards = await projectKit.getCards(columns);
15+
const repos = await projectKit.getCardRepos(cards);
16+
17+
console.log('>>> repos:');
18+
console.log(repos);
19+
console.log('>>>');
20+
21+
const issues = await projectKit.getReposIssues(repos);
22+
const issues2 = await projectKit.getRepoIssues(repo);
23+
24+
console.log('>>> issues.length:');
25+
console.log(issues.length);
26+
console.log('>>>');
27+
28+
console.log('>>> issues2.length:');
29+
console.log(issues2.length);
30+
console.log('>>>');
1531

1632
const backlogIssues = await projectKit.filterIssuesForColumnCards(
1733
issues,

src/utils/parseIssueUrl.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,17 @@ export const parseIssueUrl = (issueUrl: string): IParsedIssue => {
1212
issueNumber: parseInt(split[4], 10),
1313
};
1414
};
15+
16+
17+
export const parseIssueApiUrl = (issueUrl: string): IParsedIssue => {
18+
const uri = new URL(issueUrl);
19+
const { pathname } = uri;
20+
21+
const split = pathname.split('/');
22+
23+
return {
24+
owner: split[2],
25+
repo: split[3],
26+
issueNumber: parseInt(split[5], 10),
27+
};
28+
};

src/utils/serializeIssuePath.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { IParsedIssue } from '../interfaces/IParsedIssue';
2+
3+
export const serializeIssuePath = (issue: IParsedIssue): string => {
4+
const { owner, repo, issueNumber } = issue;
5+
6+
return `/${owner}/${repo}/${issueNumber}`;
7+
};

0 commit comments

Comments
 (0)