trello-cli-unofficial 0.7.6 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/bun.lock +225 -2
- package/dist/main.js +26490 -25401
- package/main.ts +6 -3
- package/package.json +15 -3
- package/src/application/use-cases/AuthenticateUserUseCase.ts +7 -6
- package/src/application/use-cases/CreateBoardUseCase.ts +19 -0
- package/src/application/use-cases/CreateCardUseCase.ts +2 -1
- package/src/application/use-cases/CreateListUseCase.ts +19 -0
- package/src/application/use-cases/GetBoardDetailsUseCase.ts +41 -0
- package/src/application/use-cases/UpdateCardUseCase.ts +2 -1
- package/src/application/use-cases/index.ts +3 -0
- package/src/domain/entities/Board.ts +10 -2
- package/src/domain/entities/Card.ts +12 -1
- package/src/domain/entities/Config.ts +3 -1
- package/src/domain/entities/List.ts +14 -2
- package/src/domain/repositories/TrelloRepository.ts +4 -0
- package/src/i18n/index.ts +62 -5
- package/src/i18n/locales/en.json +154 -17
- package/src/i18n/locales/pt-BR.json +154 -17
- package/src/infrastructure/repositories/FileConfigRepository.ts +6 -3
- package/src/infrastructure/repositories/TrelloApiRepository.ts +155 -10
- package/src/presentation/cli/AuthController.ts +2 -1
- package/src/presentation/cli/BoardController.ts +160 -17
- package/src/presentation/cli/CardController.ts +169 -45
- package/src/presentation/cli/CommandController.ts +293 -27
- package/src/presentation/cli/ConfigController.ts +4 -3
- package/src/presentation/cli/TrelloCliController.ts +10 -2
- package/src/shared/ErrorHandler.ts +233 -0
- package/src/shared/OutputFormatter.ts +210 -0
- package/src/shared/index.ts +2 -0
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
import type { CreateCardData, UpdateCardData } from '@domain/entities';
|
|
2
2
|
import type { TrelloRepository } from '@domain/repositories';
|
|
3
3
|
import { BoardEntity, CardEntity, ListEntity } from '@domain/entities';
|
|
4
|
+
import { t } from '@/i18n';
|
|
5
|
+
|
|
6
|
+
// API Response types
|
|
7
|
+
interface TrelloBoardResponse {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
url: string;
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface TrelloListResponse {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
idBoard: string;
|
|
18
|
+
pos: number;
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface TrelloCardResponse {
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
desc?: string;
|
|
26
|
+
idList: string;
|
|
27
|
+
pos: number;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
}
|
|
4
30
|
|
|
5
31
|
export class TrelloApiRepository implements TrelloRepository {
|
|
6
32
|
private readonly baseUrl = 'https://api.trello.com/1';
|
|
@@ -10,7 +36,10 @@ export class TrelloApiRepository implements TrelloRepository {
|
|
|
10
36
|
private readonly token: string,
|
|
11
37
|
) {}
|
|
12
38
|
|
|
13
|
-
private async request(
|
|
39
|
+
private async request(
|
|
40
|
+
endpoint: string,
|
|
41
|
+
options?: RequestInit,
|
|
42
|
+
): Promise<unknown> {
|
|
14
43
|
const url = `${this.baseUrl}${endpoint}?key=${this.apiKey}&token=${this.token}`;
|
|
15
44
|
|
|
16
45
|
const response = await fetch(url, options);
|
|
@@ -18,26 +47,142 @@ export class TrelloApiRepository implements TrelloRepository {
|
|
|
18
47
|
if (!response.ok) {
|
|
19
48
|
const errorText = await response.text();
|
|
20
49
|
throw new Error(
|
|
21
|
-
|
|
50
|
+
`${t('api.trelloError')} ${response.status} ${response.statusText}\n${errorText}`,
|
|
22
51
|
);
|
|
23
52
|
}
|
|
24
53
|
|
|
25
54
|
return response.json();
|
|
26
55
|
}
|
|
27
56
|
|
|
57
|
+
private async requestBoards(
|
|
58
|
+
endpoint: string,
|
|
59
|
+
options?: RequestInit,
|
|
60
|
+
): Promise<TrelloBoardResponse[]> {
|
|
61
|
+
const data = await this.request(endpoint, options);
|
|
62
|
+
return data as TrelloBoardResponse[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private async requestLists(
|
|
66
|
+
endpoint: string,
|
|
67
|
+
options?: RequestInit,
|
|
68
|
+
): Promise<TrelloListResponse[]> {
|
|
69
|
+
const data = await this.request(endpoint, options);
|
|
70
|
+
return data as TrelloListResponse[];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private async requestCards(
|
|
74
|
+
endpoint: string,
|
|
75
|
+
options?: RequestInit,
|
|
76
|
+
): Promise<TrelloCardResponse[]> {
|
|
77
|
+
const data = await this.request(endpoint, options);
|
|
78
|
+
return data as TrelloCardResponse[];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private async requestList(
|
|
82
|
+
endpoint: string,
|
|
83
|
+
options?: RequestInit,
|
|
84
|
+
): Promise<TrelloListResponse> {
|
|
85
|
+
const data = await this.request(endpoint, options);
|
|
86
|
+
return data as TrelloListResponse;
|
|
87
|
+
}
|
|
88
|
+
|
|
28
89
|
async getBoards(): Promise<BoardEntity[]> {
|
|
29
|
-
const data = await this.
|
|
30
|
-
return data.map((board:
|
|
90
|
+
const data = await this.requestBoards('/members/me/boards');
|
|
91
|
+
return data.map((board: TrelloBoardResponse) =>
|
|
92
|
+
BoardEntity.fromApiResponse(board),
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async createBoard(name: string, description?: string): Promise<BoardEntity> {
|
|
97
|
+
const body = new URLSearchParams({
|
|
98
|
+
name,
|
|
99
|
+
desc: description || '',
|
|
100
|
+
key: this.apiKey,
|
|
101
|
+
token: this.token,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const data = await this.request('/boards', {
|
|
105
|
+
method: 'POST',
|
|
106
|
+
headers: {
|
|
107
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
108
|
+
},
|
|
109
|
+
body: body.toString(),
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return BoardEntity.fromApiResponse(data as TrelloBoardResponse);
|
|
31
113
|
}
|
|
32
114
|
|
|
33
115
|
async getLists(boardId: string): Promise<ListEntity[]> {
|
|
34
|
-
const data = await this.
|
|
35
|
-
return data.map((list:
|
|
116
|
+
const data = await this.requestLists(`/boards/${boardId}/lists`);
|
|
117
|
+
return data.map((list: TrelloListResponse) =>
|
|
118
|
+
ListEntity.fromApiResponse(list),
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async createList(boardId: string, name: string): Promise<ListEntity> {
|
|
123
|
+
const body = new URLSearchParams({
|
|
124
|
+
idBoard: boardId,
|
|
125
|
+
name,
|
|
126
|
+
key: this.apiKey,
|
|
127
|
+
token: this.token,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const data = await this.request('/lists', {
|
|
131
|
+
method: 'POST',
|
|
132
|
+
headers: {
|
|
133
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
134
|
+
},
|
|
135
|
+
body: body.toString(),
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return ListEntity.fromApiResponse(data as TrelloListResponse);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async deleteList(listId: string): Promise<void> {
|
|
142
|
+
// Primeiro, fechar (arquivar) a lista
|
|
143
|
+
const closeBody = new URLSearchParams({
|
|
144
|
+
closed: 'true',
|
|
145
|
+
key: this.apiKey,
|
|
146
|
+
token: this.token,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
await this.request(`/lists/${listId}`, {
|
|
150
|
+
method: 'PUT',
|
|
151
|
+
headers: {
|
|
152
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
153
|
+
},
|
|
154
|
+
body: closeBody.toString(),
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Depois, deletar a lista
|
|
158
|
+
await this.request(`/lists/${listId}`, {
|
|
159
|
+
method: 'DELETE',
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async moveList(listId: string, position: number): Promise<ListEntity> {
|
|
164
|
+
const body = new URLSearchParams({
|
|
165
|
+
pos: position.toString(),
|
|
166
|
+
key: this.apiKey,
|
|
167
|
+
token: this.token,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const data = await this.requestList(`/lists/${listId}`, {
|
|
171
|
+
method: 'PUT',
|
|
172
|
+
headers: {
|
|
173
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
174
|
+
},
|
|
175
|
+
body: body.toString(),
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
return ListEntity.fromApiResponse(data);
|
|
36
179
|
}
|
|
37
180
|
|
|
38
181
|
async getCards(listId: string): Promise<CardEntity[]> {
|
|
39
|
-
const data = await this.
|
|
40
|
-
return data.map((card:
|
|
182
|
+
const data = await this.requestCards(`/lists/${listId}/cards`);
|
|
183
|
+
return data.map((card: TrelloCardResponse) =>
|
|
184
|
+
CardEntity.fromApiResponse(card),
|
|
185
|
+
);
|
|
41
186
|
}
|
|
42
187
|
|
|
43
188
|
async createCard(cardData: CreateCardData): Promise<CardEntity> {
|
|
@@ -57,7 +202,7 @@ export class TrelloApiRepository implements TrelloRepository {
|
|
|
57
202
|
body: body.toString(),
|
|
58
203
|
});
|
|
59
204
|
|
|
60
|
-
return CardEntity.fromApiResponse(data);
|
|
205
|
+
return CardEntity.fromApiResponse(data as TrelloCardResponse);
|
|
61
206
|
}
|
|
62
207
|
|
|
63
208
|
async updateCard(
|
|
@@ -78,7 +223,7 @@ export class TrelloApiRepository implements TrelloRepository {
|
|
|
78
223
|
body: body.toString(),
|
|
79
224
|
});
|
|
80
225
|
|
|
81
|
-
return CardEntity.fromApiResponse(data);
|
|
226
|
+
return CardEntity.fromApiResponse(data as TrelloCardResponse);
|
|
82
227
|
}
|
|
83
228
|
|
|
84
229
|
async deleteCard(cardId: string): Promise<void> {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ConfigEntity } from '@domain/entities';
|
|
1
2
|
import type { ConfigRepository } from '@domain/repositories';
|
|
2
3
|
import { AuthenticateUserUseCase } from '@application/use-cases';
|
|
3
4
|
import inquirer from 'inquirer';
|
|
@@ -32,7 +33,7 @@ export class AuthController {
|
|
|
32
33
|
console.log(result.success ? t('auth.tokenSaved') : result.message);
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
async getConfig() {
|
|
36
|
+
async getConfig(): Promise<ConfigEntity> {
|
|
36
37
|
return await this.authenticateUseCase.getConfig();
|
|
37
38
|
}
|
|
38
39
|
}
|
|
@@ -1,30 +1,83 @@
|
|
|
1
|
+
import type { BoardEntity, CardEntity, ListEntity } from '@domain/entities';
|
|
1
2
|
import type { TrelloRepository } from '@domain/repositories';
|
|
2
|
-
|
|
3
|
-
|
|
3
|
+
import type { OutputFormatter } from '@/shared';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
CreateBoardUseCase,
|
|
7
|
+
CreateListUseCase,
|
|
8
|
+
GetBoardDetailsUseCase,
|
|
9
|
+
GetBoardsUseCase,
|
|
10
|
+
GetCardsUseCase,
|
|
11
|
+
GetListsUseCase,
|
|
12
|
+
} from '@application/use-cases';
|
|
4
13
|
import { t } from '@/i18n';
|
|
5
14
|
|
|
6
15
|
export class BoardController {
|
|
7
16
|
private getBoardsUseCase: GetBoardsUseCase;
|
|
8
17
|
private getListsUseCase: GetListsUseCase;
|
|
9
18
|
private getCardsUseCase: GetCardsUseCase;
|
|
10
|
-
|
|
11
|
-
|
|
19
|
+
private getBoardDetailsUseCase: GetBoardDetailsUseCase;
|
|
20
|
+
private createBoardUseCase: CreateBoardUseCase;
|
|
21
|
+
private createListUseCase: CreateListUseCase;
|
|
22
|
+
private trelloRepository: TrelloRepository;
|
|
23
|
+
private outputFormatter: OutputFormatter;
|
|
24
|
+
|
|
25
|
+
constructor(
|
|
26
|
+
trelloRepository: TrelloRepository,
|
|
27
|
+
outputFormatter: OutputFormatter,
|
|
28
|
+
) {
|
|
29
|
+
this.trelloRepository = trelloRepository;
|
|
30
|
+
this.outputFormatter = outputFormatter;
|
|
12
31
|
this.getBoardsUseCase = new GetBoardsUseCase(trelloRepository);
|
|
13
32
|
this.getListsUseCase = new GetListsUseCase(trelloRepository);
|
|
14
33
|
this.getCardsUseCase = new GetCardsUseCase(trelloRepository);
|
|
34
|
+
this.getBoardDetailsUseCase = new GetBoardDetailsUseCase(trelloRepository);
|
|
35
|
+
this.createBoardUseCase = new CreateBoardUseCase(trelloRepository);
|
|
36
|
+
this.createListUseCase = new CreateListUseCase(trelloRepository);
|
|
15
37
|
}
|
|
16
38
|
|
|
17
39
|
async showBoards(): Promise<void> {
|
|
18
40
|
const boards = await this.getBoardsUseCase.execute();
|
|
19
41
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
console.log(` 🔗 ${board.url}`);
|
|
24
|
-
console.log(` 🆔 ${board.id}\n`);
|
|
42
|
+
this.outputFormatter.output(boards, {
|
|
43
|
+
fields: ['name', 'id', 'url'],
|
|
44
|
+
headers: ['Name', 'ID', 'URL'],
|
|
25
45
|
});
|
|
26
46
|
}
|
|
27
47
|
|
|
48
|
+
async showBoardDetails(boardId: string): Promise<void> {
|
|
49
|
+
const details = await this.getBoardDetailsUseCase.execute(boardId);
|
|
50
|
+
|
|
51
|
+
this.outputFormatter.message(
|
|
52
|
+
t('board.boardName', { name: details.board.name }),
|
|
53
|
+
);
|
|
54
|
+
this.outputFormatter.message(
|
|
55
|
+
t('board.boardUrl', { url: details.board.url }),
|
|
56
|
+
);
|
|
57
|
+
this.outputFormatter.message(t('board.boardId', { id: details.board.id }));
|
|
58
|
+
this.outputFormatter.message(
|
|
59
|
+
t('board.boardStats', {
|
|
60
|
+
lists: details.totalLists,
|
|
61
|
+
cards: details.totalCards,
|
|
62
|
+
}),
|
|
63
|
+
);
|
|
64
|
+
this.outputFormatter.message('');
|
|
65
|
+
|
|
66
|
+
if (details.lists.length > 0) {
|
|
67
|
+
this.outputFormatter.message(t('board.listsTitle'));
|
|
68
|
+
this.outputFormatter.output(details.lists, {
|
|
69
|
+
fields: ['name', 'id'],
|
|
70
|
+
headers: [t('board.listsHeaders.name'), t('board.listsHeaders.id')],
|
|
71
|
+
});
|
|
72
|
+
} else {
|
|
73
|
+
this.outputFormatter.message(t('board.listsEmpty'));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async getBoards(): Promise<BoardEntity[]> {
|
|
78
|
+
return this.getBoardsUseCase.execute();
|
|
79
|
+
}
|
|
80
|
+
|
|
28
81
|
async showLists(boardName: string): Promise<void> {
|
|
29
82
|
const boards = await this.getBoardsUseCase.execute();
|
|
30
83
|
const board = boards.find(b => b.name === boardName);
|
|
@@ -38,10 +91,18 @@ export class BoardController {
|
|
|
38
91
|
console.log(t('list.boardLists', { boardName }));
|
|
39
92
|
lists.forEach((list, index) => {
|
|
40
93
|
console.log(`${index + 1}. ${list.name}`);
|
|
41
|
-
console.log(`
|
|
94
|
+
console.log(` ${t('list.listId', { id: list.id })}\n`);
|
|
42
95
|
});
|
|
43
96
|
}
|
|
44
97
|
|
|
98
|
+
async getLists(boardId: string): Promise<ListEntity[]> {
|
|
99
|
+
return this.getListsUseCase.execute(boardId);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async getCards(listId: string): Promise<CardEntity[]> {
|
|
103
|
+
return this.getCardsUseCase.execute(listId);
|
|
104
|
+
}
|
|
105
|
+
|
|
45
106
|
async showCards(boardName: string, listName: string): Promise<void> {
|
|
46
107
|
const boards = await this.getBoardsUseCase.execute();
|
|
47
108
|
const board = boards.find(b => b.name === boardName);
|
|
@@ -67,19 +128,101 @@ export class BoardController {
|
|
|
67
128
|
|
|
68
129
|
cards.forEach((card, index) => {
|
|
69
130
|
console.log(`${index + 1}. ${card.name}`);
|
|
70
|
-
console.log(`
|
|
131
|
+
console.log(` ${t('card.cardId', { id: card.id })}\n`);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async showListsById(boardId: string): Promise<void> {
|
|
136
|
+
const lists = await this.getListsUseCase.execute(boardId);
|
|
137
|
+
|
|
138
|
+
this.outputFormatter.output(lists, {
|
|
139
|
+
fields: ['name', 'id'],
|
|
140
|
+
headers: ['Name', 'ID'],
|
|
71
141
|
});
|
|
72
142
|
}
|
|
73
143
|
|
|
74
|
-
async
|
|
75
|
-
|
|
144
|
+
async showCardsByListId(listId: string): Promise<void> {
|
|
145
|
+
const cards = await this.getCardsUseCase.execute(listId);
|
|
146
|
+
|
|
147
|
+
if (cards.length === 0) {
|
|
148
|
+
this.outputFormatter.message(t('list.cardsEmpty'));
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
this.outputFormatter.output(cards, {
|
|
153
|
+
fields: ['name', 'id'],
|
|
154
|
+
headers: ['Name', 'ID'],
|
|
155
|
+
});
|
|
76
156
|
}
|
|
77
157
|
|
|
78
|
-
async
|
|
79
|
-
|
|
158
|
+
async createBoard(name: string, description?: string): Promise<void> {
|
|
159
|
+
const board = await this.createBoardUseCase.execute(name, description);
|
|
160
|
+
|
|
161
|
+
console.log(t('board.created', { name: board.name }));
|
|
162
|
+
console.log(`🔗 ${board.url}`);
|
|
163
|
+
console.log(`🆔 ${board.id}`);
|
|
80
164
|
}
|
|
81
165
|
|
|
82
|
-
async
|
|
83
|
-
|
|
166
|
+
async createList(boardId: string, name: string): Promise<void> {
|
|
167
|
+
const list = await this.createListUseCase.execute(boardId, name);
|
|
168
|
+
|
|
169
|
+
console.log(t('list.created', { name: list.name }));
|
|
170
|
+
console.log(`🆔 ${list.id}`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async deleteList(listId: string): Promise<void> {
|
|
174
|
+
// Primeiro precisamos encontrar a lista para mostrar informações
|
|
175
|
+
const boards = await this.getBoardsUseCase.execute();
|
|
176
|
+
let list: ListEntity | undefined;
|
|
177
|
+
|
|
178
|
+
for (const board of boards) {
|
|
179
|
+
const lists = await this.getListsUseCase.execute(board.id);
|
|
180
|
+
list = lists.find((l: ListEntity) => l.id === listId);
|
|
181
|
+
|
|
182
|
+
if (list) {
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!list) {
|
|
188
|
+
throw new Error(t('list.notFound', { listId }));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Verificar se a lista está vazia antes de deletar
|
|
192
|
+
const cards = await this.getCardsUseCase.execute(listId);
|
|
193
|
+
if (cards.length > 0) {
|
|
194
|
+
throw new Error(t('list.notEmpty', { listName: list.name }));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Deletar a lista usando o repositório
|
|
198
|
+
await this.trelloRepository.deleteList(listId);
|
|
199
|
+
|
|
200
|
+
console.log(t('list.deleted', { name: list.name }));
|
|
201
|
+
console.log(`🆔 ${list.id}`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async moveList(listId: string, position: number): Promise<void> {
|
|
205
|
+
// Primeiro precisamos encontrar a lista para mostrar informações
|
|
206
|
+
const boards = await this.getBoardsUseCase.execute();
|
|
207
|
+
let list: ListEntity | undefined;
|
|
208
|
+
|
|
209
|
+
for (const board of boards) {
|
|
210
|
+
const lists = await this.getListsUseCase.execute(board.id);
|
|
211
|
+
list = lists.find((l: ListEntity) => l.id === listId);
|
|
212
|
+
|
|
213
|
+
if (list) {
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (!list) {
|
|
219
|
+
throw new Error(t('list.notFound', { listId }));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Mover a lista para a nova posição
|
|
223
|
+
await this.trelloRepository.moveList(listId, position);
|
|
224
|
+
|
|
225
|
+
console.log(t('list.moved', { name: list.name, position }));
|
|
226
|
+
console.log(`🆔 ${list.id}`);
|
|
84
227
|
}
|
|
85
228
|
}
|