This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

include user id and access type for collection search

+92 -2
+40
src/modules/cards/application/useCases/queries/SearchCollectionsUseCase.ts
··· 11 11 PaginationDTO, 12 12 CollectionSortingDTO, 13 13 } from '@semble/types'; 14 + import { IIdentityResolutionService } from 'src/modules/atproto/domain/services/IIdentityResolutionService'; 15 + import { DIDOrHandle } from 'src/modules/atproto/domain/DIDOrHandle'; 16 + import { CollectionAccessType } from '../../../domain/Collection'; 14 17 15 18 export interface SearchCollectionsQuery { 16 19 page?: number; ··· 18 21 sortBy?: CollectionSortField; 19 22 sortOrder?: SortOrder; 20 23 searchText?: string; 24 + identifier?: string; // Can be DID or handle 25 + accessType?: CollectionAccessType; 21 26 } 22 27 23 28 export interface SearchCollectionsResult { ··· 32 37 constructor( 33 38 private collectionQueryRepo: ICollectionQueryRepository, 34 39 private profileService: IProfileService, 40 + private identityResolutionService: IIdentityResolutionService, 35 41 ) {} 36 42 37 43 async execute( ··· 44 50 const sortOrder = query.sortOrder || SortOrder.DESC; 45 51 46 52 try { 53 + // Resolve identifier to DID if provided 54 + let authorId: string | undefined; 55 + if (query.identifier) { 56 + const identifierResult = DIDOrHandle.create(query.identifier); 57 + if (identifierResult.isErr()) { 58 + return err( 59 + new Error(`Invalid identifier: ${identifierResult.error.message}`), 60 + ); 61 + } 62 + 63 + const didResult = await this.identityResolutionService.resolveToDID( 64 + identifierResult.value, 65 + ); 66 + if (didResult.isErr()) { 67 + return err( 68 + new Error( 69 + `Failed to resolve identifier to DID: ${didResult.error.message}`, 70 + ), 71 + ); 72 + } 73 + 74 + authorId = didResult.value.value; 75 + } 76 + 77 + // Validate accessType if provided 78 + if ( 79 + query.accessType && 80 + !Object.values(CollectionAccessType).includes(query.accessType) 81 + ) { 82 + return err(new Error(`Invalid access type: ${query.accessType}`)); 83 + } 84 + 47 85 // Execute query to get raw collection data 48 86 const result = await this.collectionQueryRepo.searchCollections({ 49 87 page, ··· 51 89 sortBy, 52 90 sortOrder, 53 91 searchText: query.searchText, 92 + authorId, 93 + accessType: query.accessType, 54 94 }); 55 95 56 96 // Get unique author IDs from the results
+2
src/modules/cards/domain/ICollectionQueryRepository.ts
··· 78 78 sortBy: CollectionSortField; 79 79 sortOrder: SortOrder; 80 80 searchText?: string; 81 + authorId?: string; // Filter by author DID 82 + accessType?: string; // Filter by access type (OPEN or CLOSED) 81 83 } 82 84 83 85 export interface ICollectionQueryRepository {
+12 -1
src/modules/cards/infrastructure/http/controllers/SearchCollectionsController.ts
··· 5 5 CollectionSortField, 6 6 SortOrder, 7 7 } from '../../../domain/ICollectionQueryRepository'; 8 + import { CollectionAccessType } from '../../../domain/Collection'; 8 9 9 10 export class SearchCollectionsController extends Controller { 10 11 constructor(private searchCollectionsUseCase: SearchCollectionsUseCase) { ··· 13 14 14 15 async executeImpl(req: Request, res: Response): Promise<any> { 15 16 try { 16 - const { page, limit, sortBy, sortOrder, searchText } = req.query; 17 + const { 18 + page, 19 + limit, 20 + sortBy, 21 + sortOrder, 22 + searchText, 23 + identifier, 24 + accessType, 25 + } = req.query; 17 26 18 27 const result = await this.searchCollectionsUseCase.execute({ 19 28 page: page ? parseInt(page as string) : undefined, ··· 21 30 sortBy: sortBy as CollectionSortField, 22 31 sortOrder: sortOrder as SortOrder, 23 32 searchText: searchText as string, 33 + identifier: identifier as string, 34 + accessType: accessType as CollectionAccessType, 24 35 }); 25 36 26 37 if (result.isErr()) {
+19 -1
src/modules/cards/infrastructure/repositories/DrizzleCollectionQueryRepository.ts
··· 264 264 options: SearchCollectionsOptions, 265 265 ): Promise<PaginatedQueryResult<CollectionQueryResultDTO>> { 266 266 try { 267 - const { page, limit, sortBy, sortOrder, searchText } = options; 267 + const { 268 + page, 269 + limit, 270 + sortBy, 271 + sortOrder, 272 + searchText, 273 + authorId, 274 + accessType, 275 + } = options; 268 276 const offset = (page - 1) * limit; 269 277 270 278 // Build the sort order ··· 272 280 273 281 // Build where conditions 274 282 const whereConditions = []; 283 + 284 + // Add author filter if provided 285 + if (authorId) { 286 + whereConditions.push(eq(collections.authorId, authorId)); 287 + } 288 + 289 + // Add access type filter if provided 290 + if (accessType) { 291 + whereConditions.push(eq(collections.accessType, accessType)); 292 + } 275 293 276 294 // Add tokenized search condition if searchText is provided 277 295 if (searchText && searchText.trim()) {
+14
src/modules/cards/tests/utils/InMemoryCollectionQueryRepository.ts
··· 228 228 try { 229 229 let allCollections = this.collectionRepository.getAllCollections(); 230 230 231 + // Apply author filter if provided 232 + if (options.authorId) { 233 + allCollections = allCollections.filter( 234 + (collection) => collection.authorId.value === options.authorId, 235 + ); 236 + } 237 + 238 + // Apply access type filter if provided 239 + if (options.accessType) { 240 + allCollections = allCollections.filter( 241 + (collection) => collection.accessType === options.accessType, 242 + ); 243 + } 244 + 231 245 // Apply tokenized search if searchText is provided 232 246 if (options.searchText && options.searchText.trim()) { 233 247 const searchWords = options.searchText
+1
src/shared/infrastructure/http/factories/UseCaseFactory.ts
··· 238 238 searchCollectionsUseCase: new SearchCollectionsUseCase( 239 239 repositories.collectionQueryRepository, 240 240 services.profileService, 241 + services.identityResolutionService, 241 242 ), 242 243 getUrlStatusForMyLibraryUseCase: new GetUrlStatusForMyLibraryUseCase( 243 244 repositories.cardRepository,
+2
src/types/src/api/requests.ts
··· 208 208 209 209 export interface SearchCollectionsParams extends PaginatedSortedParams { 210 210 searchText?: string; 211 + identifier?: string; // Can be DID or handle 212 + accessType?: 'OPEN' | 'CLOSED'; 211 213 } 212 214 213 215 // Notification request types
+2
src/webapp/api-client/clients/CollectionClient.ts
··· 50 50 if (params?.sortBy) searchParams.set('sortBy', params.sortBy); 51 51 if (params?.sortOrder) searchParams.set('sortOrder', params.sortOrder); 52 52 if (params?.searchText) searchParams.set('searchText', params.searchText); 53 + if (params?.identifier) searchParams.set('identifier', params.identifier); 54 + if (params?.accessType) searchParams.set('accessType', params.accessType); 53 55 54 56 const queryString = searchParams.toString(); 55 57 const endpoint = queryString