Changeset 19a9a1e in Klonkt


Ignore:
Timestamp:
08/15/2026 07:43:02 AM (4 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
bec9001
Parents:
e1da6a1
Message:

Library: audience meesturen en de ongeordende collectievorm gebruiken

De 500 op /ap/users/dev/library kwam van open.audio, niet van ons, en de
oorzaak staat in hun bron (federation/serializers.py op develop):

LibrarySerializer.create() regel 1147

"privacy_level": privacy[validated_dataaudience]

audience staat als required=False in de serializer, dus als wij het
weglaten zit de sleutel niet in validated_data en is dat een KeyError --
een 500 op elke poging onze bibliotheek op te halen. Wij stuurden hem
niet.

Het veld is bovendien gewoon waar: alles in de bak is fedi_open. En bij
hen is het precies het verschil tussen privacy_level 'everyone' en 'me',
oftewel of onze nummers daar afspeelbaar zijn.

Daarnaast de vorm. Hun CollectionPageSerializer (regel 1157) accepteert
alleen CollectionPage met items; wij stuurden OrderedCollectionPage
met orderedItems, en dan komt er van de inhoud niets binnen. Dat is geen
dialect maar de andere helft van AS2: een platenkast heeft geen volgorde
die iets betekent, en hun LibrarySerializer accepteert als type ook alleen
as:Collection of fw:Library. De outbox is wél geordend en blijft zoals hij
was.

De eisen staan nu met bronverwijzing in test/ap-library.test.js, want ze
stonden nergens bij ons opgeschreven -- en het volgende dat gebeurt is
anders dat iemand dit 'opruimt' naar de gewone vorm.

Co-Authored-By: Claude Opus 5 <noreply@…>

Files:
4 edited

Legend:

Unmodified
Added
Removed
  • src/services/ap-core.js

    re1da6a1 r19a9a1e  
    265265 * en zonder `next`. Hem naar de laatste pagina terugbuigen zou opnieuw een
    266266 * antwoord zijn dat over zichzelf liegt.
    267  */
    268 export function pagedCollection(id, items, { totalItems, page = false, perPage = PAGINA_GROOTTE, alGesneden = false, extra = {} } = {}) {
     267 *
     268 * `ongeordend` maakt er de NIET-geordende vorm van: `Collection` met
     269 * `CollectionPage` en `items`, in plaats van `OrderedCollection` met
     270 * `OrderedCollectionPage` en `orderedItems`. Dat is geen dialect maar de andere
     271 * helft van AS2 -- en de bibliotheek hoort daar: een platenkast heeft geen
     272 * volgorde die iets betekent, en `Library` is bij Funkwhale expliciet een
     273 * `Collection`. Onze outbox is wél geordend (chronologie is daar de inhoud) en
     274 * blijft dus zoals hij was.
     275 *
     276 * De pagina draagt in die vorm ook `first` en `last`. AS2 staat dat toe --
     277 * CollectionPage erft van Collection -- en een lezer die halverwege binnenkomt
     278 * kan zo terug naar het begin zonder eerst de wortel op te halen.
     279 */
     280export function pagedCollection(id, items, { totalItems, page = false, perPage = PAGINA_GROOTTE, alGesneden = false, ongeordend = false, extra = {} } = {}) {
    269281  const lijst = items || [];
    270282  const telling = totalItems === undefined ? lijst.length : totalItems;
     
    283295      '@context': AP_CONTEXT,
    284296      id: url(n),
    285       type: 'OrderedCollectionPage',
     297      type: ongeordend ? 'CollectionPage' : 'OrderedCollectionPage',
    286298      partOf: id,
    287299      totalItems: telling,
     300      ...(ongeordend ? { first: url(1), last: url(paginas) } : {}),
     301      ...(extra.attributedTo ? { attributedTo: extra.attributedTo } : {}),
    288302      ...(n > 1 ? { prev: url(n - 1) } : {}),
    289303      ...(n < paginas ? { next: url(n + 1) } : {}),
    290       orderedItems: deel,
     304      ...(ongeordend ? { items: deel } : { orderedItems: deel }),
    291305    };
    292306  }
     
    294308    '@context': AP_CONTEXT,
    295309    id,
    296     type: 'OrderedCollection',
     310    type: ongeordend ? 'Collection' : 'OrderedCollection',
    297311    ...extra,
    298312    totalItems: telling,
    299313    first: url(1),
    300314    last: url(paginas),
    301     orderedItems: lijst,
     315    ...(ongeordend ? { items: lijst } : { orderedItems: lijst }),
    302316  };
    303317}
  • src/services/music/index.js

    re1da6a1 r19a9a1e  
    253253  return pagedCollection(id, items, {
    254254    page,
     255    // Een platenkast is geen tijdlijn: `Collection`, niet `OrderedCollection`.
     256    // Funkwhale's LibrarySerializer accepteert ook alleen die twee typen
     257    // (as:Collection of fw:Library) en zijn CollectionPageSerializer alleen
     258    // `CollectionPage` met `items`.
     259    ongeordend: true,
    255260    extra: {
    256261      type: 'Library',
    257262      name: site.title || site.slug,
    258263      attributedTo: actorId(base, site.slug),
     264      // WAAROM DIT VELD ER MOET STAAN. Funkwhale's LibrarySerializer noemt
     265      // `audience` optioneel, maar zijn create() doet er meteen
     266      // `privacy[validated_data["audience"]]` mee -- zonder de sleutel is dat
     267      // een KeyError en geeft hun server een 500. Dat is wat open.audio op 15-8
     268      // teruggaf toen Robin onze library-URI daar opzocht.
     269      //
     270      // Het is bovendien gewoon waar: alles hierin is fedi_open, dus openbaar.
     271      // Bij hen is dit precies het verschil tussen privacy_level 'everyone' en
     272      // 'me' -- oftewel of onze nummers daar afspeelbaar zijn.
     273      audience: 'https://www.w3.org/ns/activitystreams#Public',
    259274      // Vereist volgens hun docs. Openbaar, dus de telling is eerlijk en de
    260275      // lijst blijft leeg -- wie ons volgt volgt de ACTOR, niet de bak.
  • test/ap-collection-paging.test.js

    re1da6a1 r19a9a1e  
    8484    assert.notEqual(pagina.body.id, wortel.body.id, `${pad}: pagina deelt zijn id met de collectie`);
    8585
    86     // En hij mag niet zelf weer een `first` dragen -- dat is de lus waar
    87     // open.audio in liep.
    88     assert.equal(pagina.body.first, undefined, `${pad}: de pagina draagt zelf een first`);
     86    // Een pagina MAG `first` dragen (AS2: CollectionPage erft van Collection,
     87    // en Funkwhale eist het zelfs op library-pagina's). Wat niet mag is een
     88    // `next` naar zichzelf: dat is de lus, en die zit in de verwijzing die een
     89    // lezer volgt, niet in de aanwezigheid van het veld.
     90    assert.notEqual(pagina.body.next, eerste, `${pad}: next wijst naar de pagina zelf`);
     91
     92    // De items horen bij het type: geordend -> orderedItems, ongeordend ->
     93    // items. Een `Collection` met `orderedItems` is voor een strikte lezer leeg.
     94    const sleutel = pagina.body.type === 'CollectionPage' ? 'items' : 'orderedItems';
     95    assert.ok(Array.isArray(pagina.body[sleutel]), `${pad}: ${pagina.body.type} zonder ${sleutel}`);
    8996  });
    9097}
  • test/ap-library.test.js

    re1da6a1 r19a9a1e  
    5353  // van de bak zijn poort overrulen.
    5454  const lib = AP.buildLibrary(BASE, site, AP.siteOpenTracks('s1'));
    55   assert.deepEqual(lib.orderedItems.map((a) => a.name), ['Het open nummer']);
     55  assert.deepEqual(lib.items.map((a) => a.name), ['Het open nummer']);
    5656});
    5757
     
    5959  // Dit is het veld waar het om begonnen was: het haakje waar een upload aan komt.
    6060  const lib = AP.buildLibrary(BASE, site, AP.siteOpenTracks('s1'));
    61   for (const a of lib.orderedItems) assert.equal(a.library, LIB);
     61  for (const a of lib.items) assert.equal(a.library, LIB);
    6262  // Ook als de track ergens anders vandaan komt -- de losse ophaal, de
    6363  // trackcollectie, de playlist. Anders hangt het er maar op een plek.
    6464  const los = AP.buildTrackAudio(BASE, site, AP.openTrack('s1', 't1'), { standalone: true });
    6565  assert.equal(los.library, LIB);
     66});
     67
     68// Wat hun serializers WERKELIJK eisen, uit de bron en niet uit de docs.
     69// api/funkwhale_api/federation/serializers.py op develop, gelezen 15-8:
     70//
     71//   LibrarySerializer(PaginatedCollectionSerializer)   regel 1071
     72//     type in [as:Collection, fw:Library] · name · id · first · last · totalItems
     73//     audience: ChoiceField(required=False) MAAR create() doet
     74//       privacy[validated_data["audience"]]  -- ontbreekt de sleutel, dan een
     75//       KeyError en dus een 500. Zo gaf open.audio op 15-8 een 500 op onze URL.
     76//     en het is de schakel naar privacy_level 'everyone' == afspeelbaar.
     77//
     78//   CollectionPageSerializer                            regel 1157
     79//     type == as:CollectionPage (NIET OrderedCollectionPage) · items (NIET
     80//     orderedItems) · id · first · last · partOf · totalItems
     81//
     82// Deze test staat er omdat die eisen nergens anders bij ons zijn opgeschreven.
     83// Zonder hem is het volgende dat iemand de bibliotheek 'opruimt' naar de
     84// gewone OrderedCollection-vorm, en dan is hij daar stil weer leeg.
     85test('de bibliotheek voldoet aan Funkwhale LibrarySerializer', () => {
     86  const lib = AP.buildLibrary(BASE, site, AP.siteOpenTracks('s1'));
     87  assert.equal(lib.audience, 'https://www.w3.org/ns/activitystreams#Public',
     88    'zonder audience een KeyError in hun create() -- 500');
     89  assert.ok(Array.isArray(lib.items), 'Collection draagt items, geen orderedItems');
     90  assert.equal(lib.orderedItems, undefined);
     91  for (const veld of ['id', 'name', 'first', 'last', 'totalItems', 'attributedTo']) {
     92    assert.ok(lib[veld] !== undefined, `${veld} ontbreekt`);
     93  }
     94});
     95
     96test('een bibliotheekpagina voldoet aan Funkwhale CollectionPageSerializer', () => {
     97  const p = AP.buildLibrary(BASE, site, AP.siteOpenTracks('s1'), { page: 1 });
     98  assert.equal(p.type, 'CollectionPage', 'OrderedCollectionPage wijzen ze af');
     99  assert.ok(Array.isArray(p.items), 'items, niet orderedItems');
     100  assert.equal(p.orderedItems, undefined);
     101  assert.equal(p.partOf, LIB);
     102  assert.equal(p.id, `${LIB}?page=1`);
     103  for (const veld of ['first', 'last', 'totalItems']) {
     104    assert.ok(p[veld] !== undefined, `${veld} ontbreekt op de pagina`);
     105  }
    66106});
    67107
Note: See TracChangeset for help on using the changeset viewer.