perlsky is a Perl 5 implementation of an AT Protocol Personal Data Server.
13
fork

Configure Feed

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

Scope sync block exports to repos

alice 7f774fc6 597788f2

+51 -7
+7 -7
lib/ATProto/PDS/API/Sync.pm
··· 18 18 ); 19 19 use ATProto::PDS::Identity qw(service_host); 20 20 use ATProto::PDS::Moderation qw(assert_blob_readable assert_repo_readable is_repo_takedown); 21 - use ATProto::PDS::Repo::CAR qw(write_car); 21 + use ATProto::PDS::Repo::CAR qw(read_car write_car); 22 22 use ATProto::PDS::Repo::CID; 23 23 use ATProto::PDS::Repo::Bytes; 24 24 use ATProto::PDS::Repo::MST qw(build_mst); ··· 98 98 my $account = _readable_repo_by_did($c); 99 99 my @cids = flatten_params($c->every_param('cids')); 100 100 xrpc_error(400, 'InvalidRequest', 'At least one CID is required') unless @cids; 101 - my $rows = $c->store->get_blocks(\@cids); 102 - my %found = map { $_->{cid} => $_ } @$rows; 103 - for my $cid (@cids) { 104 - xrpc_error(404, 'BlockNotFound', "Block $cid was not found") unless $found{$cid}; 105 - } 101 + my $repo_car = $c->store->repo_car($account->{did}); 102 + xrpc_error(404, 'RepoNotFound', 'Repository was not found') unless defined $repo_car; 103 + my %found = map { $_->{cid}->to_string => $_ } @{ read_car($repo_car)->{blocks} || [] }; 104 + my @missing = grep { !$found{$_} } @cids; 105 + xrpc_error(400, 'InvalidRequest', 'Could not find cids: ' . join(', ', @missing)) if @missing; 106 106 my @blocks = map { 107 107 +{ 108 108 cid => ATProto::PDS::Repo::CID->from_string($_), 109 109 bytes => $found{$_}{bytes}, 110 110 } 111 111 } @cids; 112 - return _render_car($c, write_car($blocks[0]{cid}, \@blocks)); 112 + return _render_car($c, write_car(undef, \@blocks)); 113 113 }); 114 114 115 115 $registry->register('com.atproto.sync.getBlob', sub ($c, $endpoint) {
+30
script/differential-validate
··· 1201 1201 'getHead matches the official reference PDS semantics', 1202 1202 ); 1203 1203 1204 + note('Comparing getBlocks'); 1205 + for my $name (sort keys %server) { 1206 + my $requested_cid = $server{$name}{latest_commit_raw}{cid}; 1207 + my $res = get_form($server{$name}{origin}, 'com.atproto.sync.getBlocks', { 1208 + did => $server{$name}{did}, 1209 + cids => $requested_cid, 1210 + }); 1211 + check($res->is_success, "$name getBlocks succeeds"); 1212 + my $car = $res->is_success ? read_car($res->body // q()) : undef; 1213 + 1214 + my $missing = get_form($server{$name}{origin}, 'com.atproto.sync.getBlocks', { 1215 + did => $server{$name}{did}, 1216 + cids => 'bafyreifakecidmismatch', 1217 + }); 1218 + 1219 + $server{$name}{get_blocks} = { 1220 + ok => $res->is_success ? 1 : 0, 1221 + car_type => (($res->headers->content_type // q()) =~ m{application/vnd\.ipld\.car}) ? 1 : 0, 1222 + roots_empty => ($car && !@{ $car->{roots} || [] }) ? 1 : 0, 1223 + has_requested_cid => ($car && scalar(grep { $_->{cid}->to_string eq $requested_cid } @{ $car->{blocks} || [] })) ? 1 : 0, 1224 + missing_status => $missing->code, 1225 + missing_error => ($missing->json || {})->{error}, 1226 + }; 1227 + } 1228 + 1229 + check( 1230 + same_hash($server{reference}{get_blocks}, $server{perlsky}{get_blocks}), 1231 + 'getBlocks repo scoping and missing-CID semantics match the official reference PDS', 1232 + ); 1233 + 1204 1234 note('Comparing getRepo CAR exports'); 1205 1235 for my $name (sort keys %server) { 1206 1236 my $res = get_form($server{$name}{origin}, 'com.atproto.sync.getRepo', { did => $server{$name}{did} });
+14
t/external-surface.t
··· 21 21 use JSON::PP (); 22 22 use Mojo::URL; 23 23 use ATProto::PDS; 24 + use ATProto::PDS::Repo::CAR qw(read_car); 24 25 25 26 my $root = File::Spec->rel2abs(File::Spec->catdir($Bin, '..')); 26 27 my $tmp = tempdir(CLEANUP => 1); ··· 105 106 ))->status_is(200) 106 107 ->content_type_like(qr{application/vnd\.ipld\.car}) 107 108 ->content_like(qr/.+/s); 109 + my $blocks_car = read_car($t->tx->res->body); 110 + is_deeply($blocks_car->{roots}, [], 'sync.getBlocks returns a rootless CAR'); 111 + ok( 112 + scalar(grep { $_->{cid}->to_string eq $latest->{cid} } @{ $blocks_car->{blocks} || [] }), 113 + 'sync.getBlocks returns the requested repo-scoped block', 114 + ); 108 115 109 116 $t->post_ok('/xrpc/com.atproto.repo.uploadBlob' => { 110 117 Authorization => "Bearer $access", ··· 163 170 did => $second_did, 164 171 ))->status_is(200) 165 172 ->json_is('/cids/0' => $blob_cid); 173 + 174 + $t->get_ok(Mojo::URL->new('/xrpc/com.atproto.sync.getBlocks')->query( 175 + did => $second_did, 176 + cids => $latest->{cid}, 177 + ))->status_is(400) 178 + ->json_is('/error' => 'InvalidRequest') 179 + ->json_like('/message' => qr/\Q$latest->{cid}\E/); 166 180 167 181 $t->post_ok('/xrpc/com.atproto.repo.uploadBlob' => { 168 182 Authorization => "Bearer $access",