Maintain local ⭤ remote in sync with automatic AT Protocol parity for Laravel (alpha & unstable)
1<?php
2
3namespace SocialDept\AtpParity\Discovery;
4
5use BackedEnum;
6use Generator;
7use SocialDept\AtpClient\Facades\Atp;
8use SocialDept\AtpParity\Import\ImportService;
9use Throwable;
10
11/**
12 * Service for discovering DIDs with records in specific collections.
13 */
14class DiscoveryService
15{
16 public function __construct(
17 protected ImportService $importService
18 ) {}
19
20 /**
21 * Discover all DIDs with records in a collection.
22 *
23 * @return Generator<string> Yields DIDs
24 */
25 public function discoverDids(string|BackedEnum $collection, ?int $limit = null): Generator
26 {
27 $collection = $collection instanceof BackedEnum ? $collection->value : $collection;
28 $cursor = null;
29 $count = 0;
30
31 do {
32 $response = Atp::public()->atproto->sync->listReposByCollection(
33 collection: $collection,
34 limit: min(500, $limit ? $limit - $count : 500),
35 cursor: $cursor,
36 );
37
38 foreach ($response->repos as $repo) {
39 $did = $repo['did'] ?? null;
40
41 if ($did) {
42 yield $did;
43 $count++;
44
45 if ($limit !== null && $count >= $limit) {
46 return;
47 }
48 }
49 }
50
51 $cursor = $response->cursor;
52 } while ($cursor !== null);
53 }
54
55 /**
56 * Discover DIDs and return as an array.
57 */
58 public function discover(string|BackedEnum $collection, ?int $limit = null): DiscoveryResult
59 {
60 try {
61 $dids = iterator_to_array($this->discoverDids($collection, $limit));
62 $incomplete = $limit !== null && count($dids) >= $limit;
63
64 return DiscoveryResult::success($dids, $incomplete);
65 } catch (Throwable $e) {
66 return DiscoveryResult::failed($e->getMessage());
67 }
68 }
69
70 /**
71 * Discover and import all users for a collection.
72 */
73 public function discoverAndImport(
74 string|BackedEnum $collection,
75 ?int $limit = null,
76 ?callable $onProgress = null
77 ): DiscoveryResult {
78 $collection = $collection instanceof BackedEnum ? $collection->value : $collection;
79
80 try {
81 $dids = [];
82 $count = 0;
83
84 foreach ($this->discoverDids($collection, $limit) as $did) {
85 $dids[] = $did;
86 $count++;
87
88 // Start import for this DID
89 $this->importService->import($did, [$collection]);
90
91 if ($onProgress) {
92 $onProgress($did, $count);
93 }
94 }
95
96 $incomplete = $limit !== null && count($dids) >= $limit;
97
98 return DiscoveryResult::success($dids, $incomplete);
99 } catch (Throwable $e) {
100 return DiscoveryResult::failed($e->getMessage());
101 }
102 }
103
104 /**
105 * Count total DIDs with records in a collection.
106 *
107 * Note: This iterates through all results, which can be slow.
108 */
109 public function count(string|BackedEnum $collection): int
110 {
111 $count = 0;
112
113 foreach ($this->discoverDids($collection) as $_) {
114 $count++;
115 }
116
117 return $count;
118 }
119}