···
5
5
import { RemoveCardFromLibraryUseCase } from '../../../cards/application/useCases/commands/RemoveCardFromLibraryUseCase';
6
6
import { InMemoryCardRepository } from '../../../cards/tests/utils/InMemoryCardRepository';
7
7
import { InMemoryCollectionRepository } from '../../../cards/tests/utils/InMemoryCollectionRepository';
8
8
+
import { InMemoryConnectionRepository } from '../../../cards/tests/utils/InMemoryConnectionRepository';
8
9
import { FakeCardPublisher } from '../../../cards/tests/utils/FakeCardPublisher';
9
10
import { FakeCollectionPublisher } from '../../../cards/tests/utils/FakeCollectionPublisher';
10
11
import { FakeMetadataService } from '../../../cards/tests/utils/FakeMetadataService';
···
26
27
let removeCardFromLibraryUseCase: RemoveCardFromLibraryUseCase;
27
28
let cardRepository: InMemoryCardRepository;
28
29
let collectionRepository: InMemoryCollectionRepository;
30
30
+
let connectionRepository: InMemoryConnectionRepository;
29
31
let cardPublisher: FakeCardPublisher;
30
32
let collectionPublisher: FakeCollectionPublisher;
31
33
let metadataService: FakeMetadataService;
···
39
41
configService = new EnvironmentConfigService();
40
42
cardRepository = InMemoryCardRepository.getInstance();
41
43
collectionRepository = InMemoryCollectionRepository.getInstance();
44
44
+
connectionRepository = InMemoryConnectionRepository.getInstance();
42
45
cardPublisher = new FakeCardPublisher();
43
46
collectionPublisher = new FakeCollectionPublisher();
44
47
metadataService = new FakeMetadataService();
···
59
62
atUriResolutionService = new InMemoryAtUriResolutionService(
60
63
collectionRepository,
61
64
cardRepository,
65
65
+
connectionRepository,
62
66
);
63
67
64
68
addUrlToLibraryUseCase = new AddUrlToLibraryUseCase(
···
4
4
import { UpdateCollectionUseCase } from '../../../cards/application/useCases/commands/UpdateCollectionUseCase';
5
5
import { DeleteCollectionUseCase } from '../../../cards/application/useCases/commands/DeleteCollectionUseCase';
6
6
import { InMemoryCollectionRepository } from '../../../cards/tests/utils/InMemoryCollectionRepository';
7
7
+
import { InMemoryConnectionRepository } from '../../../cards/tests/utils/InMemoryConnectionRepository';
7
8
import { FakeCollectionPublisher } from '../../../cards/tests/utils/FakeCollectionPublisher';
8
9
import { CollectionBuilder } from '../../../cards/tests/utils/builders/CollectionBuilder';
9
10
import { CuratorId } from '../../../cards/domain/value-objects/CuratorId';
···
20
21
let deleteCollectionUseCase: DeleteCollectionUseCase;
21
22
let collectionRepository: InMemoryCollectionRepository;
22
23
let cardRepository: InMemoryCardRepository;
24
24
+
let connectionRepository: InMemoryConnectionRepository;
23
25
let collectionPublisher: FakeCollectionPublisher;
24
26
let curatorId: CuratorId;
25
27
let configService: EnvironmentConfigService;
···
28
30
configService = new EnvironmentConfigService();
29
31
collectionRepository = InMemoryCollectionRepository.getInstance();
30
32
cardRepository = InMemoryCardRepository.getInstance();
33
33
+
connectionRepository = InMemoryConnectionRepository.getInstance();
31
34
collectionPublisher = new FakeCollectionPublisher();
32
35
33
36
atUriResolutionService = new InMemoryAtUriResolutionService(
34
37
collectionRepository,
35
38
cardRepository,
39
39
+
connectionRepository,
36
40
);
37
41
38
42
createCollectionUseCase = new CreateCollectionUseCase(
···
3
3
import { UpdateUrlCardAssociationsUseCase } from '../../../cards/application/useCases/commands/UpdateUrlCardAssociationsUseCase';
4
4
import { InMemoryCardRepository } from '../../../cards/tests/utils/InMemoryCardRepository';
5
5
import { InMemoryCollectionRepository } from '../../../cards/tests/utils/InMemoryCollectionRepository';
6
6
+
import { InMemoryConnectionRepository } from '../../../cards/tests/utils/InMemoryConnectionRepository';
6
7
import { FakeCardPublisher } from '../../../cards/tests/utils/FakeCardPublisher';
7
8
import { FakeCollectionPublisher } from '../../../cards/tests/utils/FakeCollectionPublisher';
8
9
import { FakeEventPublisher } from '../../../cards/tests/utils/FakeEventPublisher';
···
23
24
let updateUrlCardAssociationsUseCase: UpdateUrlCardAssociationsUseCase;
24
25
let cardRepository: InMemoryCardRepository;
25
26
let collectionRepository: InMemoryCollectionRepository;
27
27
+
let connectionRepository: InMemoryConnectionRepository;
26
28
let cardPublisher: FakeCardPublisher;
27
29
let collectionPublisher: FakeCollectionPublisher;
28
30
let eventPublisher: FakeEventPublisher;
···
35
37
configService = new EnvironmentConfigService();
36
38
cardRepository = InMemoryCardRepository.getInstance();
37
39
collectionRepository = InMemoryCollectionRepository.getInstance();
40
40
+
connectionRepository = InMemoryConnectionRepository.getInstance();
38
41
cardPublisher = new FakeCardPublisher();
39
42
collectionPublisher = new FakeCollectionPublisher();
40
43
eventPublisher = new FakeEventPublisher();
···
54
57
atUriResolutionService = new InMemoryAtUriResolutionService(
55
58
collectionRepository,
56
59
cardRepository,
60
60
+
connectionRepository,
57
61
);
58
62
59
63
updateUrlCardAssociationsUseCase = new UpdateUrlCardAssociationsUseCase(
···
24
24
import { InMemoryCardRepository } from '../../../cards/tests/utils/InMemoryCardRepository';
25
25
import { InMemoryCardQueryRepository } from '../../../cards/tests/utils/InMemoryCardQueryRepository';
26
26
import { InMemoryCollectionRepository } from '../../../cards/tests/utils/InMemoryCollectionRepository';
27
27
+
import { InMemoryConnectionRepository } from '../../../cards/tests/utils/InMemoryConnectionRepository';
27
28
import { FakeCardPublisher } from '../../../cards/tests/utils/FakeCardPublisher';
28
29
import { FakeCollectionPublisher } from '../../../cards/tests/utils/FakeCollectionPublisher';
29
30
import { FakeConnectionPublisher } from '../../../cards/tests/utils/FakeConnectionPublisher';
···
60
61
let cardRepository: InMemoryCardRepository;
61
62
let cardQueryRepository: InMemoryCardQueryRepository;
62
63
let collectionRepository: InMemoryCollectionRepository;
64
64
+
let connectionRepository: InMemoryConnectionRepository;
63
65
let cardPublisher: FakeCardPublisher;
64
66
let collectionPublisher: FakeCollectionPublisher;
65
67
let metadataService: FakeMetadataService;
···
90
92
// Set up all the real dependencies
91
93
cardRepository = InMemoryCardRepository.getInstance();
92
94
collectionRepository = InMemoryCollectionRepository.getInstance();
95
95
+
connectionRepository = InMemoryConnectionRepository.getInstance();
93
96
cardQueryRepository = new InMemoryCardQueryRepository(
94
97
cardRepository,
95
98
collectionRepository,
···
114
117
atUriResolutionService = new InMemoryAtUriResolutionService(
115
118
collectionRepository,
116
119
cardRepository,
120
120
+
connectionRepository,
117
121
);
118
122
119
123
// Create use cases for card processing
···
10
10
import { FollowTargetType } from '../../../user/domain/value-objects/FollowTargetType';
11
11
import { InMemoryCollectionRepository } from './InMemoryCollectionRepository';
12
12
import { InMemoryCardRepository } from './InMemoryCardRepository';
13
13
+
import { InMemoryConnectionRepository } from './InMemoryConnectionRepository';
13
14
14
15
export class InMemoryAtUriResolutionService implements IAtUriResolutionService {
15
16
constructor(
16
17
private collectionRepository: InMemoryCollectionRepository,
17
18
private cardRepository: InMemoryCardRepository,
19
19
+
private connectionRepository: InMemoryConnectionRepository,
18
20
) {}
19
21
20
22
async resolveAtUri(
···
53
55
},
54
56
});
55
57
}
58
58
+
}
59
59
+
}
60
60
+
61
61
+
// Check connections
62
62
+
const allConnections = this.connectionRepository.getAllConnections();
63
63
+
for (const connection of allConnections) {
64
64
+
if (connection.publishedRecordId?.uri === atUri) {
65
65
+
return ok({
66
66
+
type: AtUriResourceType.CONNECTION,
67
67
+
id: connection.connectionId,
68
68
+
});
56
69
}
57
70
}
58
71
···
128
141
async resolveConnectionId(
129
142
atUri: string,
130
143
): Promise<Result<ConnectionId | null>> {
131
131
-
// For testing purposes, return null (connection not found)
132
132
-
// TODO: When InMemoryConnectionRepository is created, search through connections
133
133
-
return ok(null);
144
144
+
const result = await this.resolveAtUri(atUri);
145
145
+
146
146
+
if (result.isErr()) {
147
147
+
return err(result.error);
148
148
+
}
149
149
+
150
150
+
if (!result.value || result.value.type !== AtUriResourceType.CONNECTION) {
151
151
+
return ok(null);
152
152
+
}
153
153
+
154
154
+
return ok(result.value.id as ConnectionId);
134
155
}
135
156
}
···
1
1
+
import { Result, ok, err } from '../../../../shared/core/Result';
2
2
+
import { IConnectionRepository } from '../../domain/IConnectionRepository';
3
3
+
import { Connection } from '../../domain/Connection';
4
4
+
import { ConnectionId } from '../../domain/value-objects/ConnectionId';
5
5
+
import { UrlOrCardId } from '../../domain/value-objects/UrlOrCardId';
6
6
+
import { CuratorId } from '../../domain/value-objects/CuratorId';
7
7
+
8
8
+
export class InMemoryConnectionRepository implements IConnectionRepository {
9
9
+
private static instance: InMemoryConnectionRepository;
10
10
+
private connections: Map<string, Connection> = new Map();
11
11
+
12
12
+
private constructor() {}
13
13
+
14
14
+
public static getInstance(): InMemoryConnectionRepository {
15
15
+
if (!InMemoryConnectionRepository.instance) {
16
16
+
InMemoryConnectionRepository.instance =
17
17
+
new InMemoryConnectionRepository();
18
18
+
}
19
19
+
return InMemoryConnectionRepository.instance;
20
20
+
}
21
21
+
22
22
+
private clone(connection: Connection): Connection {
23
23
+
const connectionResult = Connection.create(
24
24
+
{
25
25
+
curatorId: connection.curatorId,
26
26
+
source: connection.source,
27
27
+
target: connection.target,
28
28
+
type: connection.type,
29
29
+
note: connection.note,
30
30
+
publishedRecordId: connection.publishedRecordId,
31
31
+
createdAt: connection.createdAt,
32
32
+
updatedAt: connection.updatedAt,
33
33
+
},
34
34
+
connection.id,
35
35
+
);
36
36
+
37
37
+
if (connectionResult.isErr()) {
38
38
+
throw new Error(
39
39
+
`Failed to clone connection: ${connectionResult.error.message}`,
40
40
+
);
41
41
+
}
42
42
+
43
43
+
return connectionResult.value;
44
44
+
}
45
45
+
46
46
+
async findById(id: ConnectionId): Promise<Result<Connection | null>> {
47
47
+
try {
48
48
+
const connection = this.connections.get(id.getStringValue());
49
49
+
return ok(connection ? this.clone(connection) : null);
50
50
+
} catch (error) {
51
51
+
return err(error as Error);
52
52
+
}
53
53
+
}
54
54
+
55
55
+
async findByIds(ids: ConnectionId[]): Promise<Result<Connection[]>> {
56
56
+
try {
57
57
+
const connections: Connection[] = [];
58
58
+
for (const id of ids) {
59
59
+
const connection = this.connections.get(id.getStringValue());
60
60
+
if (connection) {
61
61
+
connections.push(this.clone(connection));
62
62
+
}
63
63
+
}
64
64
+
return ok(connections);
65
65
+
} catch (error) {
66
66
+
return err(error as Error);
67
67
+
}
68
68
+
}
69
69
+
70
70
+
async findByCuratorId(curatorId: CuratorId): Promise<Result<Connection[]>> {
71
71
+
try {
72
72
+
const connections = Array.from(this.connections.values()).filter(
73
73
+
(connection) => connection.curatorId.value === curatorId.value,
74
74
+
);
75
75
+
return ok(connections.map((connection) => this.clone(connection)));
76
76
+
} catch (error) {
77
77
+
return err(error as Error);
78
78
+
}
79
79
+
}
80
80
+
81
81
+
async findBySource(source: UrlOrCardId): Promise<Result<Connection[]>> {
82
82
+
try {
83
83
+
const connections = Array.from(this.connections.values()).filter(
84
84
+
(connection) =>
85
85
+
connection.source.type === source.type &&
86
86
+
connection.source.stringValue === source.stringValue,
87
87
+
);
88
88
+
return ok(connections.map((connection) => this.clone(connection)));
89
89
+
} catch (error) {
90
90
+
return err(error as Error);
91
91
+
}
92
92
+
}
93
93
+
94
94
+
async findByTarget(target: UrlOrCardId): Promise<Result<Connection[]>> {
95
95
+
try {
96
96
+
const connections = Array.from(this.connections.values()).filter(
97
97
+
(connection) =>
98
98
+
connection.target.type === target.type &&
99
99
+
connection.target.stringValue === target.stringValue,
100
100
+
);
101
101
+
return ok(connections.map((connection) => this.clone(connection)));
102
102
+
} catch (error) {
103
103
+
return err(error as Error);
104
104
+
}
105
105
+
}
106
106
+
107
107
+
async findBetween(
108
108
+
source: UrlOrCardId,
109
109
+
target: UrlOrCardId,
110
110
+
): Promise<Result<Connection[]>> {
111
111
+
try {
112
112
+
const connections = Array.from(this.connections.values()).filter(
113
113
+
(connection) =>
114
114
+
connection.source.type === source.type &&
115
115
+
connection.source.stringValue === source.stringValue &&
116
116
+
connection.target.type === target.type &&
117
117
+
connection.target.stringValue === target.stringValue,
118
118
+
);
119
119
+
return ok(connections.map((connection) => this.clone(connection)));
120
120
+
} catch (error) {
121
121
+
return err(error as Error);
122
122
+
}
123
123
+
}
124
124
+
125
125
+
async save(connection: Connection): Promise<Result<void>> {
126
126
+
try {
127
127
+
this.connections.set(
128
128
+
connection.connectionId.getStringValue(),
129
129
+
this.clone(connection),
130
130
+
);
131
131
+
return ok(undefined);
132
132
+
} catch (error) {
133
133
+
return err(error as Error);
134
134
+
}
135
135
+
}
136
136
+
137
137
+
async delete(connectionId: ConnectionId): Promise<Result<void>> {
138
138
+
try {
139
139
+
this.connections.delete(connectionId.getStringValue());
140
140
+
return ok(undefined);
141
141
+
} catch (error) {
142
142
+
return err(error as Error);
143
143
+
}
144
144
+
}
145
145
+
146
146
+
// Helper methods for testing
147
147
+
public clear(): void {
148
148
+
this.connections.clear();
149
149
+
}
150
150
+
151
151
+
public getStoredConnection(id: ConnectionId): Connection | undefined {
152
152
+
return this.connections.get(id.getStringValue());
153
153
+
}
154
154
+
155
155
+
public getAllConnections(): Connection[] {
156
156
+
return Array.from(this.connections.values()).map((connection) =>
157
157
+
this.clone(connection),
158
158
+
);
159
159
+
}
160
160
+
}
···
13
13
import { InMemoryCardQueryRepository } from '../../../../modules/cards/tests/utils/InMemoryCardQueryRepository';
14
14
import { InMemoryCollectionRepository } from '../../../../modules/cards/tests/utils/InMemoryCollectionRepository';
15
15
import { InMemoryCollectionQueryRepository } from '../../../../modules/cards/tests/utils/InMemoryCollectionQueryRepository';
16
16
+
import { InMemoryConnectionRepository } from '../../../../modules/cards/tests/utils/InMemoryConnectionRepository';
16
17
import { InMemoryUserRepository } from '../../../../modules/user/tests/infrastructure/InMemoryUserRepository';
17
18
import { InMemoryTokenRepository } from '../../../../modules/user/tests/infrastructure/InMemoryTokenRepository';
18
19
import { InMemoryAppPasswordSessionRepository } from '../../../../modules/atproto/tests/infrastructure/InMemoryAppPasswordSessionRepository';
···
76
77
const tokenRepository = InMemoryTokenRepository.getInstance();
77
78
const cardRepository = InMemoryCardRepository.getInstance();
78
79
const collectionRepository = InMemoryCollectionRepository.getInstance();
80
80
+
const connectionRepository = InMemoryConnectionRepository.getInstance();
79
81
const cardQueryRepository = new InMemoryCardQueryRepository(
80
82
cardRepository,
81
83
collectionRepository,
···
91
93
const atUriResolutionService = new InMemoryAtUriResolutionService(
92
94
collectionRepository,
93
95
cardRepository,
96
96
+
connectionRepository,
94
97
);
95
98
const notificationRepository =
96
99
InMemoryNotificationRepository.getInstance();
···
107
110
cardQueryRepository,
108
111
collectionRepository,
109
112
collectionQueryRepository,
110
110
-
// TODO: Create InMemoryConnectionRepository
111
111
-
connectionRepository: null as any,
113
113
+
connectionRepository,
112
114
appPasswordSessionRepository,
113
115
feedRepository,
114
116
followsRepository,