tangled
alpha
login
or
join now
cosmik.network
/
semble
43
fork
atom
A social knowledge tool for researchers built on ATProto
43
fork
atom
overview
issues
13
pulls
pipelines
use cardId for update url card associations
Wesley Finck
4 months ago
982146f9
79cc7340
+68
-25
3 changed files
expand all
collapse all
unified
split
src
modules
cards
application
useCases
commands
UpdateUrlCardAssociationsUseCase.ts
infrastructure
http
controllers
UpdateUrlCardAssociationsController.ts
tests
application
UpdateUrlCardAssociationsUseCase.test.ts
+33
-9
src/modules/cards/application/useCases/commands/UpdateUrlCardAssociationsUseCase.ts
···
5
5
import { IEventPublisher } from '../../../../../shared/application/events/IEventPublisher';
6
6
import { ICardRepository } from '../../../domain/ICardRepository';
7
7
import { INoteCardInput } from '../../../domain/CardFactory';
8
8
+
import { CardId } from '../../../domain/value-objects/CardId';
8
9
import { CollectionId } from '../../../domain/value-objects/CollectionId';
9
10
import { CuratorId } from '../../../domain/value-objects/CuratorId';
10
11
import { CardTypeEnum } from '../../../domain/value-objects/CardType';
···
15
16
import { CardLibraryService } from '../../../domain/services/CardLibraryService';
16
17
17
18
export interface UpdateUrlCardAssociationsDTO {
18
18
-
url: string;
19
19
+
cardId: string;
19
20
curatorId: string;
20
21
note?: string;
21
22
addToCollections?: string[];
···
71
72
}
72
73
const curatorId = curatorIdResult.value;
73
74
74
74
-
// Validate URL
75
75
-
const urlResult = URL.create(request.url);
76
76
-
if (urlResult.isErr()) {
75
75
+
// Validate and create CardId
76
76
+
const cardIdResult = CardId.createFromString(request.cardId);
77
77
+
if (cardIdResult.isErr()) {
77
78
return err(
78
78
-
new ValidationError(`Invalid URL: ${urlResult.error.message}`),
79
79
+
new ValidationError(
80
80
+
`Invalid card ID: ${cardIdResult.error.message}`,
81
81
+
),
79
82
);
80
83
}
81
81
-
const url = urlResult.value;
84
84
+
const cardId = cardIdResult.value;
82
85
83
86
// Find the URL card - it must already exist
84
84
-
const existingUrlCardResult =
85
85
-
await this.cardRepository.findUsersUrlCardByUrl(url, curatorId);
87
87
+
const existingUrlCardResult = await this.cardRepository.findById(cardId);
86
88
if (existingUrlCardResult.isErr()) {
87
89
return err(
88
90
AppError.UnexpectedError.create(existingUrlCardResult.error),
···
98
100
);
99
101
}
100
102
103
103
+
// Verify it's a URL card
104
104
+
if (!urlCard.isUrlCard) {
105
105
+
return err(
106
106
+
new ValidationError('Card must be a URL card to update associations.'),
107
107
+
);
108
108
+
}
109
109
+
110
110
+
// Verify ownership
111
111
+
if (!urlCard.curatorId.equals(curatorId)) {
112
112
+
return err(
113
113
+
new ValidationError('You do not have permission to update this card.'),
114
114
+
);
115
115
+
}
116
116
+
117
117
+
// Get the URL from the card for note operations
118
118
+
if (!urlCard.url) {
119
119
+
return err(
120
120
+
new ValidationError('URL card must have a URL property.'),
121
121
+
);
122
122
+
}
123
123
+
const url = urlCard.url;
124
124
+
101
125
let noteCard;
102
126
103
127
// Handle note updates/creation
···
141
165
type: CardTypeEnum.NOTE,
142
166
text: request.note,
143
167
parentCardId: urlCard.cardId.getStringValue(),
144
144
-
url: request.url,
168
168
+
url: url.value,
145
169
};
146
170
147
171
const noteCardResult = CardFactory.create({
+5
-4
src/modules/cards/infrastructure/http/controllers/UpdateUrlCardAssociationsController.ts
···
12
12
13
13
async executeImpl(req: AuthenticatedRequest, res: Response): Promise<any> {
14
14
try {
15
15
-
const { url, note, addToCollections, removeFromCollections } = req.body;
15
15
+
const { cardId, note, addToCollections, removeFromCollections } =
16
16
+
req.body;
16
17
const curatorId = req.did;
17
18
18
19
if (!curatorId) {
19
20
return this.unauthorized(res);
20
21
}
21
22
22
22
-
if (!url) {
23
23
-
return this.badRequest(res, 'URL is required');
23
23
+
if (!cardId) {
24
24
+
return this.badRequest(res, 'Card ID is required');
24
25
}
25
26
26
27
const result = await this.updateUrlCardAssociationsUseCase.execute({
27
27
-
url,
28
28
+
cardId,
28
29
curatorId,
29
30
note,
30
31
addToCollections,
+30
-12
src/modules/cards/tests/application/UpdateUrlCardAssociationsUseCase.test.ts
···
81
81
curatorId: curatorId.value,
82
82
});
83
83
expect(addResult.isOk()).toBe(true);
84
84
+
const urlCardId = addResult.unwrap().urlCardId;
84
85
85
86
// Now create a note for it
86
87
const updateRequest = {
87
87
-
url,
88
88
+
cardId: urlCardId,
88
89
curatorId: curatorId.value,
89
90
note: 'This is my note',
90
91
};
···
118
119
119
120
// Now update the note
120
121
const updateRequest = {
121
121
-
url,
122
122
+
cardId: addResponse.urlCardId,
122
123
curatorId: curatorId.value,
123
124
note: 'Updated note',
124
125
};
···
140
141
141
142
it('should fail if URL card does not exist', async () => {
142
143
const request = {
143
143
-
url: 'https://example.com/nonexistent',
144
144
+
cardId: 'nonexistent-card-id',
144
145
curatorId: curatorId.value,
145
146
note: 'This should fail',
146
147
};
···
183
184
curatorId: curatorId.value,
184
185
});
185
186
expect(addResult.isOk()).toBe(true);
187
187
+
const urlCardId = addResult.unwrap().urlCardId;
186
188
187
189
// Add to collections
188
190
const updateRequest = {
189
189
-
url,
191
191
+
cardId: urlCardId,
190
192
curatorId: curatorId.value,
191
193
addToCollections: [
192
194
collection1.collectionId.getStringValue(),
···
229
231
curatorId: curatorId.value,
230
232
});
231
233
expect(addResult.isOk()).toBe(true);
234
234
+
const urlCardId = addResult.unwrap().urlCardId;
232
235
233
236
// Remove from collection
234
237
const updateRequest = {
235
235
-
url,
238
238
+
cardId: urlCardId,
236
239
curatorId: curatorId.value,
237
240
removeFromCollections: [collection.collectionId.getStringValue()],
238
241
};
···
283
286
curatorId: curatorId.value,
284
287
});
285
288
expect(addResult.isOk()).toBe(true);
289
289
+
const urlCardId = addResult.unwrap().urlCardId;
286
290
287
291
// Add to collection2 and collection3, remove from collection1
288
292
const updateRequest = {
289
289
-
url,
293
293
+
cardId: urlCardId,
290
294
curatorId: curatorId.value,
291
295
addToCollections: [
292
296
collection2.collectionId.getStringValue(),
···
335
339
curatorId: curatorId.value,
336
340
});
337
341
expect(addResult.isOk()).toBe(true);
342
342
+
const urlCardId = addResult.unwrap().urlCardId;
338
343
339
344
// Update note and add to collection
340
345
const updateRequest = {
341
341
-
url,
346
346
+
cardId: urlCardId,
342
347
curatorId: curatorId.value,
343
348
note: 'My note about this article',
344
349
addToCollections: [collection.collectionId.getStringValue()],
···
367
372
});
368
373
369
374
describe('Validation', () => {
370
370
-
it('should fail with invalid URL', async () => {
375
375
+
it('should fail when card does not exist', async () => {
371
376
const request = {
372
372
-
url: 'not-a-valid-url',
377
377
+
cardId: 'nonexistent-card-id',
373
378
curatorId: curatorId.value,
374
379
note: 'This should fail',
375
380
};
···
378
383
379
384
expect(result.isErr()).toBe(true);
380
385
if (result.isErr()) {
381
381
-
expect(result.error.message).toContain('Invalid URL');
386
386
+
expect(result.error.message).toContain(
387
387
+
'URL card not found. Please add the URL to your library first.',
388
388
+
);
382
389
}
383
390
});
384
391
385
392
it('should fail with invalid curator ID', async () => {
393
393
+
const url = 'https://example.com/article';
394
394
+
395
395
+
// Add URL to library first
396
396
+
const addResult = await addUrlToLibraryUseCase.execute({
397
397
+
url,
398
398
+
curatorId: curatorId.value,
399
399
+
});
400
400
+
expect(addResult.isOk()).toBe(true);
401
401
+
const urlCardId = addResult.unwrap().urlCardId;
402
402
+
386
403
const request = {
387
387
-
url: 'https://example.com/article',
404
404
+
cardId: urlCardId,
388
405
curatorId: 'invalid-curator-id',
389
406
note: 'This should fail',
390
407
};
···
406
423
curatorId: curatorId.value,
407
424
});
408
425
expect(addResult.isOk()).toBe(true);
426
426
+
const urlCardId = addResult.unwrap().urlCardId;
409
427
410
428
const request = {
411
411
-
url,
429
429
+
cardId: urlCardId,
412
430
curatorId: curatorId.value,
413
431
addToCollections: ['invalid-collection-id'],
414
432
};