From 48d45aabfe09095212171c1dea2ccdf5ff8362c2 Mon Sep 17 00:00:00 2001 From: dholms Date: Tue, 19 Sep 2023 14:46:13 -0500 Subject: [PATCH 01/31] rm indexing service --- .../src/app-view/services/indexing/index.ts | 136 ------- .../services/indexing/plugins/block.ts | 97 ----- .../indexing/plugins/feed-generator.ts | 89 ----- .../services/indexing/plugins/follow.ts | 138 -------- .../services/indexing/plugins/like.ts | 128 ------- .../services/indexing/plugins/list-item.ts | 102 ------ .../services/indexing/plugins/list.ts | 86 ----- .../services/indexing/plugins/post.ts | 334 ------------------ .../services/indexing/plugins/profile.ts | 82 ----- .../services/indexing/plugins/repost.ts | 153 -------- .../app-view/services/indexing/processor.ts | 259 -------------- .../src/app-view/services/indexing/util.ts | 16 - 12 files changed, 1620 deletions(-) delete mode 100644 packages/pds/src/app-view/services/indexing/index.ts delete mode 100644 packages/pds/src/app-view/services/indexing/plugins/block.ts delete mode 100644 packages/pds/src/app-view/services/indexing/plugins/feed-generator.ts delete mode 100644 packages/pds/src/app-view/services/indexing/plugins/follow.ts delete mode 100644 packages/pds/src/app-view/services/indexing/plugins/like.ts delete mode 100644 packages/pds/src/app-view/services/indexing/plugins/list-item.ts delete mode 100644 packages/pds/src/app-view/services/indexing/plugins/list.ts delete mode 100644 packages/pds/src/app-view/services/indexing/plugins/post.ts delete mode 100644 packages/pds/src/app-view/services/indexing/plugins/profile.ts delete mode 100644 packages/pds/src/app-view/services/indexing/plugins/repost.ts delete mode 100644 packages/pds/src/app-view/services/indexing/processor.ts delete mode 100644 packages/pds/src/app-view/services/indexing/util.ts diff --git a/packages/pds/src/app-view/services/indexing/index.ts b/packages/pds/src/app-view/services/indexing/index.ts deleted file mode 100644 index 346c31e83ea..00000000000 --- a/packages/pds/src/app-view/services/indexing/index.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { CID } from 'multiformats/cid' -import { WriteOpAction } from '@atproto/repo' -import { AtUri } from '@atproto/syntax' -import { ids } from '../../../lexicon/lexicons' -import Database from '../../../db' -import { BackgroundQueue } from '../../../event-stream/background-queue' -import { NoopProcessor } from './processor' -import * as Post from './plugins/post' -import * as Like from './plugins/like' -import * as Repost from './plugins/repost' -import * as Follow from './plugins/follow' -import * as Block from './plugins/block' -import * as List from './plugins/list' -import * as ListItem from './plugins/list-item' -import * as Profile from './plugins/profile' -import * as FeedGenerator from './plugins/feed-generator' - -export class IndexingService { - records: { - post: Post.PluginType - like: Like.PluginType - repost: Repost.PluginType - follow: Follow.PluginType - block: Block.PluginType - list: List.PluginType - listItem: ListItem.PluginType - listBlock: NoopProcessor - profile: Profile.PluginType - feedGenerator: FeedGenerator.PluginType - } - - constructor(public db: Database, public backgroundQueue: BackgroundQueue) { - this.records = { - post: Post.makePlugin(this.db, backgroundQueue), - like: Like.makePlugin(this.db, backgroundQueue), - repost: Repost.makePlugin(this.db, backgroundQueue), - follow: Follow.makePlugin(this.db, backgroundQueue), - block: Block.makePlugin(this.db, backgroundQueue), - list: List.makePlugin(this.db, backgroundQueue), - listItem: ListItem.makePlugin(this.db, backgroundQueue), - listBlock: new NoopProcessor( - ids.AppBskyGraphListblock, - this.db, - backgroundQueue, - ), - profile: Profile.makePlugin(this.db, backgroundQueue), - feedGenerator: FeedGenerator.makePlugin(this.db, backgroundQueue), - } - } - - static creator(backgroundQueue: BackgroundQueue) { - return (db: Database) => new IndexingService(db, backgroundQueue) - } - - async indexRecord( - uri: AtUri, - cid: CID, - obj: unknown, - action: WriteOpAction.Create | WriteOpAction.Update, - timestamp: string, - ) { - this.db.assertTransaction() - const indexer = this.findIndexerForCollection(uri.collection) - if (action === WriteOpAction.Create) { - await indexer.insertRecord(uri, cid, obj, timestamp) - } else { - await indexer.updateRecord(uri, cid, obj, timestamp) - } - } - - async deleteRecord(uri: AtUri, cascading = false) { - this.db.assertTransaction() - const indexer = this.findIndexerForCollection(uri.collection) - await indexer.deleteRecord(uri, cascading) - } - - findIndexerForCollection(collection: string) { - const found = Object.values(this.records).find( - (plugin) => plugin.collection === collection, - ) - if (!found) { - throw new Error('Could not find indexer for collection') - } - return found - } - - async deleteForUser(did: string) { - // Not done in transaction because it would be too long, prone to contention. - // Also, this can safely be run multiple times if it fails. - // Omitting updates to profile_agg and post_agg since it's expensive - // and they'll organically update themselves over time. - - const postByUser = (qb) => - qb - .selectFrom('post') - .where('post.creator', '=', did) - .select('post.uri as uri') - - await this.db.db - .deleteFrom('post_embed_image') - .where('post_embed_image.postUri', 'in', postByUser) - .execute() - await this.db.db - .deleteFrom('post_embed_external') - .where('post_embed_external.postUri', 'in', postByUser) - .execute() - await this.db.db - .deleteFrom('post_embed_record') - .where('post_embed_record.postUri', 'in', postByUser) - .execute() - await this.db.db - .deleteFrom('duplicate_record') - .where('duplicate_record.duplicateOf', 'in', (qb) => - // @TODO remove dependency on record table from app view - qb - .selectFrom('record') - .where('record.did', '=', did) - .select('record.uri as uri'), - ) - .execute() - await this.db.db - .deleteFrom('actor_block') - .where('creator', '=', did) - .execute() - await this.db.db.deleteFrom('list').where('creator', '=', did).execute() - await this.db.db - .deleteFrom('list_item') - .where('creator', '=', did) - .execute() - await this.db.db.deleteFrom('follow').where('creator', '=', did).execute() - await this.db.db.deleteFrom('post').where('creator', '=', did).execute() - await this.db.db.deleteFrom('profile').where('creator', '=', did).execute() - await this.db.db.deleteFrom('repost').where('creator', '=', did).execute() - await this.db.db.deleteFrom('like').where('creator', '=', did).execute() - } -} diff --git a/packages/pds/src/app-view/services/indexing/plugins/block.ts b/packages/pds/src/app-view/services/indexing/plugins/block.ts deleted file mode 100644 index bb30071c4d1..00000000000 --- a/packages/pds/src/app-view/services/indexing/plugins/block.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { AtUri } from '@atproto/syntax' -import { CID } from 'multiformats/cid' -import * as Block from '../../../../lexicon/types/app/bsky/graph/block' -import * as lex from '../../../../lexicon/lexicons' -import Database from '../../../../db' -import { - DatabaseSchema, - DatabaseSchemaType, -} from '../../../../db/database-schema' -import { BackgroundQueue } from '../../../../event-stream/background-queue' -import RecordProcessor from '../processor' -import { toSimplifiedISOSafe } from '../util' - -const lexId = lex.ids.AppBskyGraphBlock -type IndexedBlock = DatabaseSchemaType['actor_block'] - -const insertFn = async ( - db: DatabaseSchema, - uri: AtUri, - cid: CID, - obj: Block.Record, - timestamp: string, -): Promise => { - const inserted = await db - .insertInto('actor_block') - .values({ - uri: uri.toString(), - cid: cid.toString(), - creator: uri.host, - subjectDid: obj.subject, - createdAt: toSimplifiedISOSafe(obj.createdAt), - indexedAt: timestamp, - }) - .onConflict((oc) => oc.doNothing()) - .returningAll() - .executeTakeFirst() - return inserted || null -} - -const findDuplicate = async ( - db: DatabaseSchema, - uri: AtUri, - obj: Block.Record, -): Promise => { - const found = await db - .selectFrom('actor_block') - .where('creator', '=', uri.host) - .where('subjectDid', '=', obj.subject) - .selectAll() - .executeTakeFirst() - return found ? new AtUri(found.uri) : null -} - -const notifsForInsert = () => { - return [] -} - -const deleteFn = async ( - db: DatabaseSchema, - uri: AtUri, -): Promise => { - const deleted = await db - .deleteFrom('actor_block') - .where('uri', '=', uri.toString()) - .returningAll() - .executeTakeFirst() - return deleted || null -} - -const notifsForDelete = ( - deleted: IndexedBlock, - replacedBy: IndexedBlock | null, -) => { - const toDelete = replacedBy ? [] : [deleted.uri] - return { notifs: [], toDelete } -} - -const updateAggregates = async () => {} - -export type PluginType = RecordProcessor - -export const makePlugin = ( - db: Database, - backgroundQueue: BackgroundQueue, -): PluginType => { - return new RecordProcessor(db, backgroundQueue, { - lexId, - insertFn, - findDuplicate, - deleteFn, - notifsForInsert, - notifsForDelete, - updateAggregates, - }) -} - -export default makePlugin diff --git a/packages/pds/src/app-view/services/indexing/plugins/feed-generator.ts b/packages/pds/src/app-view/services/indexing/plugins/feed-generator.ts deleted file mode 100644 index c18da08d51c..00000000000 --- a/packages/pds/src/app-view/services/indexing/plugins/feed-generator.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { AtUri } from '@atproto/syntax' -import { CID } from 'multiformats/cid' -import * as FeedGenerator from '../../../../lexicon/types/app/bsky/feed/generator' -import * as lex from '../../../../lexicon/lexicons' -import Database from '../../../../db' -import { - DatabaseSchema, - DatabaseSchemaType, -} from '../../../../db/database-schema' -import { BackgroundQueue } from '../../../../event-stream/background-queue' -import RecordProcessor from '../processor' -import { toSimplifiedISOSafe } from '../util' - -const lexId = lex.ids.AppBskyFeedGenerator -type IndexedFeedGenerator = DatabaseSchemaType['feed_generator'] - -const insertFn = async ( - db: DatabaseSchema, - uri: AtUri, - cid: CID, - obj: FeedGenerator.Record, - timestamp: string, -): Promise => { - const inserted = await db - .insertInto('feed_generator') - .values({ - uri: uri.toString(), - cid: cid.toString(), - creator: uri.host, - feedDid: obj.did, - displayName: obj.displayName, - description: obj.description, - descriptionFacets: obj.descriptionFacets - ? JSON.stringify(obj.descriptionFacets) - : undefined, - avatarCid: obj.avatar?.ref.toString(), - createdAt: toSimplifiedISOSafe(obj.createdAt), - indexedAt: timestamp, - }) - .onConflict((oc) => oc.doNothing()) - .returningAll() - .executeTakeFirst() - return inserted || null -} - -const findDuplicate = async (): Promise => { - return null -} - -const notifsForInsert = () => { - return [] -} - -const deleteFn = async ( - db: DatabaseSchema, - uri: AtUri, -): Promise => { - const deleted = await db - .deleteFrom('feed_generator') - .where('uri', '=', uri.toString()) - .returningAll() - .executeTakeFirst() - return deleted || null -} - -const notifsForDelete = () => { - return { notifs: [], toDelete: [] } -} - -export type PluginType = RecordProcessor< - FeedGenerator.Record, - IndexedFeedGenerator -> - -export const makePlugin = ( - db: Database, - backgroundQueue: BackgroundQueue, -): PluginType => { - return new RecordProcessor(db, backgroundQueue, { - lexId, - insertFn, - findDuplicate, - deleteFn, - notifsForInsert, - notifsForDelete, - }) -} - -export default makePlugin diff --git a/packages/pds/src/app-view/services/indexing/plugins/follow.ts b/packages/pds/src/app-view/services/indexing/plugins/follow.ts deleted file mode 100644 index c405f288b76..00000000000 --- a/packages/pds/src/app-view/services/indexing/plugins/follow.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { AtUri } from '@atproto/syntax' -import { CID } from 'multiformats/cid' -import * as Follow from '../../../../lexicon/types/app/bsky/graph/follow' -import * as lex from '../../../../lexicon/lexicons' -import Database from '../../../../db' -import { - DatabaseSchema, - DatabaseSchemaType, -} from '../../../../db/database-schema' -import { countAll, excluded } from '../../../../db/util' -import { BackgroundQueue } from '../../../../event-stream/background-queue' -import RecordProcessor from '../processor' -import { toSimplifiedISOSafe } from '../util' - -const lexId = lex.ids.AppBskyGraphFollow -type IndexedFollow = DatabaseSchemaType['follow'] - -const insertFn = async ( - db: DatabaseSchema, - uri: AtUri, - cid: CID, - obj: Follow.Record, - timestamp: string, -): Promise => { - const inserted = await db - .insertInto('follow') - .values({ - uri: uri.toString(), - cid: cid.toString(), - creator: uri.host, - subjectDid: obj.subject, - createdAt: toSimplifiedISOSafe(obj.createdAt), - indexedAt: timestamp, - }) - .onConflict((oc) => oc.doNothing()) - .returningAll() - .executeTakeFirst() - return inserted || null -} - -const findDuplicate = async ( - db: DatabaseSchema, - uri: AtUri, - obj: Follow.Record, -): Promise => { - const found = await db - .selectFrom('follow') - .where('creator', '=', uri.host) - .where('subjectDid', '=', obj.subject) - .selectAll() - .executeTakeFirst() - return found ? new AtUri(found.uri) : null -} - -const notifsForInsert = (obj: IndexedFollow) => { - return [ - { - userDid: obj.subjectDid, - author: obj.creator, - recordUri: obj.uri, - recordCid: obj.cid, - reason: 'follow' as const, - reasonSubject: null, - indexedAt: obj.indexedAt, - }, - ] -} - -const deleteFn = async ( - db: DatabaseSchema, - uri: AtUri, -): Promise => { - const deleted = await db - .deleteFrom('follow') - .where('uri', '=', uri.toString()) - .returningAll() - .executeTakeFirst() - return deleted || null -} - -const notifsForDelete = ( - deleted: IndexedFollow, - replacedBy: IndexedFollow | null, -) => { - const toDelete = replacedBy ? [] : [deleted.uri] - return { notifs: [], toDelete } -} - -const updateAggregates = async (db: DatabaseSchema, follow: IndexedFollow) => { - const followersCountQb = db - .insertInto('profile_agg') - .values({ - did: follow.subjectDid, - followersCount: db - .selectFrom('follow') - .where('follow.subjectDid', '=', follow.subjectDid) - .select(countAll.as('count')), - }) - .onConflict((oc) => - oc.column('did').doUpdateSet({ - followersCount: excluded(db, 'followersCount'), - }), - ) - const followsCountQb = db - .insertInto('profile_agg') - .values({ - did: follow.creator, - followsCount: db - .selectFrom('follow') - .where('follow.creator', '=', follow.creator) - .select(countAll.as('count')), - }) - .onConflict((oc) => - oc.column('did').doUpdateSet({ - followsCount: excluded(db, 'followsCount'), - }), - ) - await Promise.all([followersCountQb.execute(), followsCountQb.execute()]) -} - -export type PluginType = RecordProcessor - -export const makePlugin = ( - db: Database, - backgroundQueue: BackgroundQueue, -): PluginType => { - return new RecordProcessor(db, backgroundQueue, { - lexId, - insertFn, - findDuplicate, - deleteFn, - notifsForInsert, - notifsForDelete, - updateAggregates, - }) -} - -export default makePlugin diff --git a/packages/pds/src/app-view/services/indexing/plugins/like.ts b/packages/pds/src/app-view/services/indexing/plugins/like.ts deleted file mode 100644 index 32d87ebe177..00000000000 --- a/packages/pds/src/app-view/services/indexing/plugins/like.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { AtUri } from '@atproto/syntax' -import { CID } from 'multiformats/cid' -import * as Like from '../../../../lexicon/types/app/bsky/feed/like' -import * as lex from '../../../../lexicon/lexicons' -import Database from '../../../../db' -import { - DatabaseSchema, - DatabaseSchemaType, -} from '../../../../db/database-schema' -import { countAll, excluded } from '../../../../db/util' -import { BackgroundQueue } from '../../../../event-stream/background-queue' -import RecordProcessor from '../processor' -import { toSimplifiedISOSafe } from '../util' - -const lexId = lex.ids.AppBskyFeedLike -type IndexedLike = DatabaseSchemaType['like'] - -const insertFn = async ( - db: DatabaseSchema, - uri: AtUri, - cid: CID, - obj: Like.Record, - timestamp: string, -): Promise => { - const inserted = await db - .insertInto('like') - .values({ - uri: uri.toString(), - cid: cid.toString(), - creator: uri.host, - subject: obj.subject.uri, - subjectCid: obj.subject.cid, - createdAt: toSimplifiedISOSafe(obj.createdAt), - indexedAt: timestamp, - }) - .onConflict((oc) => oc.doNothing()) - .returningAll() - .executeTakeFirst() - return inserted || null -} - -const findDuplicate = async ( - db: DatabaseSchema, - uri: AtUri, - obj: Like.Record, -): Promise => { - const found = await db - .selectFrom('like') - .where('creator', '=', uri.host) - .where('subject', '=', obj.subject.uri) - .selectAll() - .executeTakeFirst() - return found ? new AtUri(found.uri) : null -} - -const notifsForInsert = (obj: IndexedLike) => { - const subjectUri = new AtUri(obj.subject) - // prevent self-notifications - const isSelf = subjectUri.host === obj.creator - return isSelf - ? [] - : [ - { - userDid: subjectUri.host, - author: obj.creator, - recordUri: obj.uri, - recordCid: obj.cid, - reason: 'like' as const, - reasonSubject: subjectUri.toString(), - indexedAt: obj.indexedAt, - }, - ] -} - -const deleteFn = async ( - db: DatabaseSchema, - uri: AtUri, -): Promise => { - const deleted = await db - .deleteFrom('like') - .where('uri', '=', uri.toString()) - .returningAll() - .executeTakeFirst() - return deleted || null -} - -const notifsForDelete = ( - deleted: IndexedLike, - replacedBy: IndexedLike | null, -) => { - const toDelete = replacedBy ? [] : [deleted.uri] - return { notifs: [], toDelete } -} - -const updateAggregates = async (db: DatabaseSchema, like: IndexedLike) => { - const likeCountQb = db - .insertInto('post_agg') - .values({ - uri: like.subject, - likeCount: db - .selectFrom('like') - .where('like.subject', '=', like.subject) - .select(countAll.as('count')), - }) - .onConflict((oc) => - oc.column('uri').doUpdateSet({ likeCount: excluded(db, 'likeCount') }), - ) - await likeCountQb.execute() -} - -export type PluginType = RecordProcessor - -export const makePlugin = ( - db: Database, - backgroundQueue: BackgroundQueue, -): PluginType => { - return new RecordProcessor(db, backgroundQueue, { - lexId, - insertFn, - findDuplicate, - deleteFn, - notifsForInsert, - notifsForDelete, - updateAggregates, - }) -} - -export default makePlugin diff --git a/packages/pds/src/app-view/services/indexing/plugins/list-item.ts b/packages/pds/src/app-view/services/indexing/plugins/list-item.ts deleted file mode 100644 index 33d0c7e8152..00000000000 --- a/packages/pds/src/app-view/services/indexing/plugins/list-item.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { AtUri } from '@atproto/syntax' -import { CID } from 'multiformats/cid' -import * as ListItem from '../../../../lexicon/types/app/bsky/graph/listitem' -import * as lex from '../../../../lexicon/lexicons' -import Database from '../../../../db' -import { - DatabaseSchema, - DatabaseSchemaType, -} from '../../../../db/database-schema' -import { BackgroundQueue } from '../../../../event-stream/background-queue' -import RecordProcessor from '../processor' -import { InvalidRequestError } from '@atproto/xrpc-server' -import { toSimplifiedISOSafe } from '../util' - -const lexId = lex.ids.AppBskyGraphListitem -type IndexedListItem = DatabaseSchemaType['list_item'] - -const insertFn = async ( - db: DatabaseSchema, - uri: AtUri, - cid: CID, - obj: ListItem.Record, - timestamp: string, -): Promise => { - const listUri = new AtUri(obj.list) - if (listUri.hostname !== uri.hostname) { - throw new InvalidRequestError( - 'Creator of listitem does not match creator of list', - ) - } - const inserted = await db - .insertInto('list_item') - .values({ - uri: uri.toString(), - cid: cid.toString(), - creator: uri.host, - subjectDid: obj.subject, - listUri: obj.list, - createdAt: toSimplifiedISOSafe(obj.createdAt), - indexedAt: timestamp, - }) - .onConflict((oc) => oc.doNothing()) - .returningAll() - .executeTakeFirst() - return inserted || null -} - -const findDuplicate = async ( - db: DatabaseSchema, - _uri: AtUri, - obj: ListItem.Record, -): Promise => { - const found = await db - .selectFrom('list_item') - .where('listUri', '=', obj.list) - .where('subjectDid', '=', obj.subject) - .selectAll() - .executeTakeFirst() - return found ? new AtUri(found.uri) : null -} - -const notifsForInsert = () => { - return [] -} - -const deleteFn = async ( - db: DatabaseSchema, - uri: AtUri, -): Promise => { - const deleted = await db - .deleteFrom('list_item') - .where('uri', '=', uri.toString()) - .returningAll() - .executeTakeFirst() - return deleted || null -} - -const notifsForDelete = ( - deleted: IndexedListItem, - replacedBy: IndexedListItem | null, -) => { - const toDelete = replacedBy ? [] : [deleted.uri] - return { notifs: [], toDelete } -} - -export type PluginType = RecordProcessor - -export const makePlugin = ( - db: Database, - backgroundQueue: BackgroundQueue, -): PluginType => { - return new RecordProcessor(db, backgroundQueue, { - lexId, - insertFn, - findDuplicate, - deleteFn, - notifsForInsert, - notifsForDelete, - }) -} - -export default makePlugin diff --git a/packages/pds/src/app-view/services/indexing/plugins/list.ts b/packages/pds/src/app-view/services/indexing/plugins/list.ts deleted file mode 100644 index b0250c5be39..00000000000 --- a/packages/pds/src/app-view/services/indexing/plugins/list.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { AtUri } from '@atproto/syntax' -import { CID } from 'multiformats/cid' -import * as List from '../../../../lexicon/types/app/bsky/graph/list' -import * as lex from '../../../../lexicon/lexicons' -import Database from '../../../../db' -import { - DatabaseSchema, - DatabaseSchemaType, -} from '../../../../db/database-schema' -import { BackgroundQueue } from '../../../../event-stream/background-queue' -import RecordProcessor from '../processor' -import { toSimplifiedISOSafe } from '../util' - -const lexId = lex.ids.AppBskyGraphList -type IndexedList = DatabaseSchemaType['list'] - -const insertFn = async ( - db: DatabaseSchema, - uri: AtUri, - cid: CID, - obj: List.Record, - timestamp: string, -): Promise => { - const inserted = await db - .insertInto('list') - .values({ - uri: uri.toString(), - cid: cid.toString(), - creator: uri.host, - name: obj.name, - purpose: obj.purpose, - description: obj.description, - descriptionFacets: obj.descriptionFacets - ? JSON.stringify(obj.descriptionFacets) - : undefined, - avatarCid: obj.avatar?.ref.toString(), - createdAt: toSimplifiedISOSafe(obj.createdAt), - indexedAt: timestamp, - }) - .onConflict((oc) => oc.doNothing()) - .returningAll() - .executeTakeFirst() - return inserted || null -} - -const findDuplicate = async (): Promise => { - return null -} - -const notifsForInsert = () => { - return [] -} - -const deleteFn = async ( - db: DatabaseSchema, - uri: AtUri, -): Promise => { - const deleted = await db - .deleteFrom('list') - .where('uri', '=', uri.toString()) - .returningAll() - .executeTakeFirst() - return deleted || null -} - -const notifsForDelete = () => { - return { notifs: [], toDelete: [] } -} - -export type PluginType = RecordProcessor - -export const makePlugin = ( - db: Database, - backgroundQueue: BackgroundQueue, -): PluginType => { - return new RecordProcessor(db, backgroundQueue, { - lexId, - insertFn, - findDuplicate, - deleteFn, - notifsForInsert, - notifsForDelete, - }) -} - -export default makePlugin diff --git a/packages/pds/src/app-view/services/indexing/plugins/post.ts b/packages/pds/src/app-view/services/indexing/plugins/post.ts deleted file mode 100644 index 3583f087cf1..00000000000 --- a/packages/pds/src/app-view/services/indexing/plugins/post.ts +++ /dev/null @@ -1,334 +0,0 @@ -import { CID } from 'multiformats/cid' -import { AtUri } from '@atproto/syntax' -import { Record as PostRecord } from '../../../../lexicon/types/app/bsky/feed/post' -import { isMain as isEmbedImage } from '../../../../lexicon/types/app/bsky/embed/images' -import { isMain as isEmbedExternal } from '../../../../lexicon/types/app/bsky/embed/external' -import { isMain as isEmbedRecord } from '../../../../lexicon/types/app/bsky/embed/record' -import { isMain as isEmbedRecordWithMedia } from '../../../../lexicon/types/app/bsky/embed/recordWithMedia' -import { - isMention, - isLink, -} from '../../../../lexicon/types/app/bsky/richtext/facet' -import * as lex from '../../../../lexicon/lexicons' -import Database from '../../../../db' -import { - DatabaseSchema, - DatabaseSchemaType, -} from '../../../../db/database-schema' -import { BackgroundQueue } from '../../../../event-stream/background-queue' -import RecordProcessor from '../processor' -import { UserNotification } from '../../../../db/tables/user-notification' -import { countAll, excluded } from '../../../../db/util' -import { toSimplifiedISOSafe } from '../util' -import { getAncestorsAndSelfQb } from '../../feed/util' - -type Post = DatabaseSchemaType['post'] -type PostEmbedImage = DatabaseSchemaType['post_embed_image'] -type PostEmbedExternal = DatabaseSchemaType['post_embed_external'] -type PostEmbedRecord = DatabaseSchemaType['post_embed_record'] -type PostAncestor = { - uri: string - height: number -} -type IndexedPost = { - post: Post - facets: { type: 'mention' | 'link'; value: string }[] - embeds?: (PostEmbedImage[] | PostEmbedExternal | PostEmbedRecord)[] - ancestors?: PostAncestor[] -} - -const lexId = lex.ids.AppBskyFeedPost - -const REPLY_NOTIF_DEPTH = 5 - -const insertFn = async ( - db: DatabaseSchema, - uri: AtUri, - cid: CID, - obj: PostRecord, - timestamp: string, -): Promise => { - const post = { - uri: uri.toString(), - cid: cid.toString(), - creator: uri.host, - text: obj.text, - createdAt: toSimplifiedISOSafe(obj.createdAt), - replyRoot: obj.reply?.root?.uri || null, - replyRootCid: obj.reply?.root?.cid || null, - replyParent: obj.reply?.parent?.uri || null, - replyParentCid: obj.reply?.parent?.cid || null, - indexedAt: timestamp, - } - const [insertedPost] = await Promise.all([ - db - .insertInto('post') - .values(post) - .onConflict((oc) => oc.doNothing()) - .returningAll() - .executeTakeFirst(), - db - .insertInto('feed_item') - .values({ - type: 'post', - uri: post.uri, - cid: post.cid, - postUri: post.uri, - originatorDid: post.creator, - sortAt: - post.indexedAt < post.createdAt ? post.indexedAt : post.createdAt, - }) - .onConflict((oc) => oc.doNothing()) - .executeTakeFirst(), - ]) - if (!insertedPost) { - return null // Post already indexed - } - - const facets = (obj.facets || []) - .flatMap((facet) => facet.features) - .flatMap((feature) => { - if (isMention(feature)) { - return { - type: 'mention' as const, - value: feature.did, - } - } - if (isLink(feature)) { - return { - type: 'link' as const, - value: feature.uri, - } - } - return [] - }) - // Embed indices - const embeds: (PostEmbedImage[] | PostEmbedExternal | PostEmbedRecord)[] = [] - const postEmbeds = separateEmbeds(obj.embed) - for (const postEmbed of postEmbeds) { - if (isEmbedImage(postEmbed)) { - const { images } = postEmbed - const imagesEmbed = images.map((img, i) => ({ - postUri: uri.toString(), - position: i, - imageCid: img.image.ref.toString(), - alt: img.alt, - })) - embeds.push(imagesEmbed) - await db.insertInto('post_embed_image').values(imagesEmbed).execute() - } else if (isEmbedExternal(postEmbed)) { - const { external } = postEmbed - const externalEmbed = { - postUri: uri.toString(), - uri: external.uri, - title: external.title, - description: external.description, - thumbCid: external.thumb?.ref.toString() || null, - } - embeds.push(externalEmbed) - await db.insertInto('post_embed_external').values(externalEmbed).execute() - } else if (isEmbedRecord(postEmbed)) { - const { record } = postEmbed - const recordEmbed = { - postUri: uri.toString(), - embedUri: record.uri, - embedCid: record.cid, - } - embeds.push(recordEmbed) - await db.insertInto('post_embed_record').values(recordEmbed).execute() - } - } - const ancestors = await getAncestorsAndSelfQb(db, { - uri: post.uri, - parentHeight: REPLY_NOTIF_DEPTH, - }) - .selectFrom('ancestor') - .selectAll() - .execute() - return { post: insertedPost, facets, embeds, ancestors } -} - -const findDuplicate = async (): Promise => { - return null -} - -const notifsForInsert = (obj: IndexedPost) => { - const notifs: UserNotification[] = [] - const notified = new Set([obj.post.creator]) - const maybeNotify = (notif: UserNotification) => { - if (!notified.has(notif.userDid)) { - notified.add(notif.userDid) - notifs.push(notif) - } - } - for (const facet of obj.facets) { - if (facet.type === 'mention') { - maybeNotify({ - userDid: facet.value, - reason: 'mention', - reasonSubject: null, - author: obj.post.creator, - recordUri: obj.post.uri, - recordCid: obj.post.cid, - indexedAt: obj.post.indexedAt, - }) - } - } - for (const embed of obj.embeds ?? []) { - if ('embedUri' in embed) { - const embedUri = new AtUri(embed.embedUri) - if (embedUri.collection === lex.ids.AppBskyFeedPost) { - maybeNotify({ - userDid: embedUri.host, - reason: 'quote', - reasonSubject: embedUri.toString(), - author: obj.post.creator, - recordUri: obj.post.uri, - recordCid: obj.post.cid, - indexedAt: obj.post.indexedAt, - }) - } - } - } - for (const ancestor of obj.ancestors ?? []) { - if (ancestor.uri === obj.post.uri) continue // no need to notify for own post - if (ancestor.height < REPLY_NOTIF_DEPTH) { - const ancestorUri = new AtUri(ancestor.uri) - maybeNotify({ - userDid: ancestorUri.host, - reason: 'reply', - reasonSubject: ancestorUri.toString(), - author: obj.post.creator, - recordUri: obj.post.uri, - recordCid: obj.post.cid, - indexedAt: obj.post.indexedAt, - }) - } - } - return notifs -} - -const deleteFn = async ( - db: DatabaseSchema, - uri: AtUri, -): Promise => { - const uriStr = uri.toString() - const [deleted] = await Promise.all([ - db - .deleteFrom('post') - .where('uri', '=', uriStr) - .returningAll() - .executeTakeFirst(), - db.deleteFrom('feed_item').where('postUri', '=', uriStr).executeTakeFirst(), - ]) - const deletedEmbeds: ( - | PostEmbedImage[] - | PostEmbedExternal - | PostEmbedRecord - )[] = [] - const [deletedImgs, deletedExternals, deletedPosts] = await Promise.all([ - db - .deleteFrom('post_embed_image') - .where('postUri', '=', uriStr) - .returningAll() - .execute(), - db - .deleteFrom('post_embed_external') - .where('postUri', '=', uriStr) - .returningAll() - .executeTakeFirst(), - db - .deleteFrom('post_embed_record') - .where('postUri', '=', uriStr) - .returningAll() - .executeTakeFirst(), - ]) - if (deletedImgs.length) { - deletedEmbeds.push(deletedImgs) - } - if (deletedExternals) { - deletedEmbeds.push(deletedExternals) - } - if (deletedPosts) { - deletedEmbeds.push(deletedPosts) - } - return deleted - ? { - post: deleted, - facets: [], // Not used - embeds: deletedEmbeds, - } - : null -} - -const notifsForDelete = ( - deleted: IndexedPost, - replacedBy: IndexedPost | null, -) => { - const notifs = replacedBy ? notifsForInsert(replacedBy) : [] - return { - notifs, - toDelete: [deleted.post.uri], - } -} - -const updateAggregates = async (db: DatabaseSchema, postIdx: IndexedPost) => { - const replyCountQb = postIdx.post.replyParent - ? db - .insertInto('post_agg') - .values({ - uri: postIdx.post.replyParent, - replyCount: db - .selectFrom('post') - .where('post.replyParent', '=', postIdx.post.replyParent) - .select(countAll.as('count')), - }) - .onConflict((oc) => - oc - .column('uri') - .doUpdateSet({ replyCount: excluded(db, 'replyCount') }), - ) - : null - const postsCountQb = db - .insertInto('profile_agg') - .values({ - did: postIdx.post.creator, - postsCount: db - .selectFrom('post') - .where('post.creator', '=', postIdx.post.creator) - .select(countAll.as('count')), - }) - .onConflict((oc) => - oc.column('did').doUpdateSet({ postsCount: excluded(db, 'postsCount') }), - ) - await Promise.all([replyCountQb?.execute(), postsCountQb.execute()]) -} - -export type PluginType = RecordProcessor - -export const makePlugin = ( - db: Database, - backgroundQueue: BackgroundQueue, -): PluginType => { - return new RecordProcessor(db, backgroundQueue, { - lexId, - insertFn, - findDuplicate, - deleteFn, - notifsForInsert, - notifsForDelete, - updateAggregates, - }) -} - -export default makePlugin - -function separateEmbeds(embed: PostRecord['embed']) { - if (!embed) { - return [] - } - if (isEmbedRecordWithMedia(embed)) { - return [{ $type: lex.ids.AppBskyEmbedRecord, ...embed.record }, embed.media] - } - return [embed] -} diff --git a/packages/pds/src/app-view/services/indexing/plugins/profile.ts b/packages/pds/src/app-view/services/indexing/plugins/profile.ts deleted file mode 100644 index 3b8e6db506c..00000000000 --- a/packages/pds/src/app-view/services/indexing/plugins/profile.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { AtUri } from '@atproto/syntax' -import { CID } from 'multiformats/cid' -import * as Profile from '../../../../lexicon/types/app/bsky/actor/profile' -import * as lex from '../../../../lexicon/lexicons' -import Database from '../../../../db' -import { - DatabaseSchema, - DatabaseSchemaType, -} from '../../../../db/database-schema' -import { BackgroundQueue } from '../../../../event-stream/background-queue' -import RecordProcessor from '../processor' - -const lexId = lex.ids.AppBskyActorProfile -type IndexedProfile = DatabaseSchemaType['profile'] - -const insertFn = async ( - db: DatabaseSchema, - uri: AtUri, - cid: CID, - obj: Profile.Record, - timestamp: string, -): Promise => { - if (uri.rkey !== 'self') return null - const inserted = await db - .insertInto('profile') - .values({ - uri: uri.toString(), - cid: cid.toString(), - creator: uri.host, - displayName: obj.displayName, - description: obj.description, - avatarCid: obj.avatar?.ref.toString(), - bannerCid: obj.banner?.ref.toString(), - indexedAt: timestamp, - }) - .onConflict((oc) => oc.doNothing()) - .returningAll() - .executeTakeFirst() - return inserted || null -} - -const findDuplicate = async (): Promise => { - return null -} - -const notifsForInsert = () => { - return [] -} - -const deleteFn = async ( - db: DatabaseSchema, - uri: AtUri, -): Promise => { - const deleted = await db - .deleteFrom('profile') - .where('uri', '=', uri.toString()) - .returningAll() - .executeTakeFirst() - return deleted || null -} - -const notifsForDelete = () => { - return { notifs: [], toDelete: [] } -} - -export type PluginType = RecordProcessor - -export const makePlugin = ( - db: Database, - backgroundQueue: BackgroundQueue, -): PluginType => { - return new RecordProcessor(db, backgroundQueue, { - lexId, - insertFn, - findDuplicate, - deleteFn, - notifsForInsert, - notifsForDelete, - }) -} - -export default makePlugin diff --git a/packages/pds/src/app-view/services/indexing/plugins/repost.ts b/packages/pds/src/app-view/services/indexing/plugins/repost.ts deleted file mode 100644 index fb05ca57ffd..00000000000 --- a/packages/pds/src/app-view/services/indexing/plugins/repost.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { CID } from 'multiformats/cid' -import { AtUri } from '@atproto/syntax' -import * as Repost from '../../../../lexicon/types/app/bsky/feed/repost' -import * as lex from '../../../../lexicon/lexicons' -import Database from '../../../../db' -import { - DatabaseSchema, - DatabaseSchemaType, -} from '../../../../db/database-schema' -import { BackgroundQueue } from '../../../../event-stream/background-queue' -import RecordProcessor from '../processor' -import { countAll, excluded } from '../../../../db/util' -import { toSimplifiedISOSafe } from '../util' - -const lexId = lex.ids.AppBskyFeedRepost -type IndexedRepost = DatabaseSchemaType['repost'] - -const insertFn = async ( - db: DatabaseSchema, - uri: AtUri, - cid: CID, - obj: Repost.Record, - timestamp: string, -): Promise => { - const repost = { - uri: uri.toString(), - cid: cid.toString(), - creator: uri.host, - subject: obj.subject.uri, - subjectCid: obj.subject.cid, - createdAt: toSimplifiedISOSafe(obj.createdAt), - indexedAt: timestamp, - } - const [inserted] = await Promise.all([ - db - .insertInto('repost') - .values(repost) - .onConflict((oc) => oc.doNothing()) - .returningAll() - .executeTakeFirst(), - db - .insertInto('feed_item') - .values({ - type: 'repost', - uri: repost.uri, - cid: repost.cid, - postUri: repost.subject, - originatorDid: repost.creator, - sortAt: - repost.indexedAt < repost.createdAt - ? repost.indexedAt - : repost.createdAt, - }) - .onConflict((oc) => oc.doNothing()) - .executeTakeFirst(), - ]) - - return inserted || null -} - -const findDuplicate = async ( - db: DatabaseSchema, - uri: AtUri, - obj: Repost.Record, -): Promise => { - const found = await db - .selectFrom('repost') - .where('creator', '=', uri.host) - .where('subject', '=', obj.subject.uri) - .selectAll() - .executeTakeFirst() - return found ? new AtUri(found.uri) : null -} - -const notifsForInsert = (obj: IndexedRepost) => { - const subjectUri = new AtUri(obj.subject) - // prevent self-notifications - const isSelf = subjectUri.host === obj.creator - return isSelf - ? [] - : [ - { - userDid: subjectUri.host, - author: obj.creator, - recordUri: obj.uri, - recordCid: obj.cid, - reason: 'repost' as const, - reasonSubject: subjectUri.toString(), - indexedAt: obj.indexedAt, - }, - ] -} - -const deleteFn = async ( - db: DatabaseSchema, - uri: AtUri, -): Promise => { - const uriStr = uri.toString() - const [deleted] = await Promise.all([ - db - .deleteFrom('repost') - .where('uri', '=', uriStr) - .returningAll() - .executeTakeFirst(), - db.deleteFrom('feed_item').where('uri', '=', uriStr).executeTakeFirst(), - ]) - return deleted || null -} - -const notifsForDelete = ( - deleted: IndexedRepost, - replacedBy: IndexedRepost | null, -) => { - const toDelete = replacedBy ? [] : [deleted.uri] - return { notifs: [], toDelete } -} - -const updateAggregates = async (db: DatabaseSchema, repost: IndexedRepost) => { - const repostCountQb = db - .insertInto('post_agg') - .values({ - uri: repost.subject, - repostCount: db - .selectFrom('repost') - .where('repost.subject', '=', repost.subject) - .select(countAll.as('count')), - }) - .onConflict((oc) => - oc - .column('uri') - .doUpdateSet({ repostCount: excluded(db, 'repostCount') }), - ) - await repostCountQb.execute() -} - -export type PluginType = RecordProcessor - -export const makePlugin = ( - db: Database, - backgroundQueue: BackgroundQueue, -): PluginType => { - return new RecordProcessor(db, backgroundQueue, { - lexId, - insertFn, - findDuplicate, - deleteFn, - notifsForInsert, - notifsForDelete, - updateAggregates, - }) -} - -export default makePlugin diff --git a/packages/pds/src/app-view/services/indexing/processor.ts b/packages/pds/src/app-view/services/indexing/processor.ts deleted file mode 100644 index 6b6712889f2..00000000000 --- a/packages/pds/src/app-view/services/indexing/processor.ts +++ /dev/null @@ -1,259 +0,0 @@ -import { CID } from 'multiformats/cid' -import { AtUri } from '@atproto/syntax' -import { cborToLexRecord } from '@atproto/repo' -import Database from '../../../db' -import DatabaseSchema from '../../../db/database-schema' -import { BackgroundQueue } from '../../../event-stream/background-queue' -import { lexicons } from '../../../lexicon/lexicons' -import { UserNotification } from '../../../db/tables/user-notification' - -// @NOTE re: insertions and deletions. Due to how record updates are handled, -// (insertFn) should have the same effect as (insertFn -> deleteFn -> insertFn). -type RecordProcessorParams = { - lexId: string - insertFn: ( - db: DatabaseSchema, - uri: AtUri, - cid: CID, - obj: T, - timestamp: string, - ) => Promise - findDuplicate: ( - db: DatabaseSchema, - uri: AtUri, - obj: T, - ) => Promise - deleteFn: (db: DatabaseSchema, uri: AtUri) => Promise - notifsForInsert: (obj: S) => UserNotification[] - notifsForDelete: ( - prev: S, - replacedBy: S | null, - ) => { notifs: UserNotification[]; toDelete: string[] } - updateAggregates?: (db: DatabaseSchema, obj: S) => Promise -} - -export class RecordProcessor { - collection: string - db: DatabaseSchema - constructor( - private appDb: Database, - private backgroundQueue: BackgroundQueue, - private params: RecordProcessorParams, - ) { - this.db = appDb.db - this.collection = this.params.lexId - } - - matchesSchema(obj: unknown): obj is T { - try { - this.assertValidRecord(obj) - return true - } catch { - return false - } - } - - assertValidRecord(obj: unknown): void { - lexicons.assertValidRecord(this.params.lexId, obj) - } - - async insertRecord(uri: AtUri, cid: CID, obj: unknown, timestamp: string) { - if (!this.matchesSchema(obj)) { - throw new Error(`Record does not match schema: ${this.params.lexId}`) - } - const inserted = await this.params.insertFn( - this.db, - uri, - cid, - obj, - timestamp, - ) - // if this was a new record, return events - if (inserted) { - this.aggregateOnCommit(inserted) - await this.handleNotifs({ inserted }) - return - } - // if duplicate, insert into duplicates table with no events - const found = await this.params.findDuplicate(this.db, uri, obj) - if (found && found.toString() !== uri.toString()) { - await this.db - .insertInto('duplicate_record') - .values({ - uri: uri.toString(), - cid: cid.toString(), - duplicateOf: found.toString(), - indexedAt: timestamp, - }) - .onConflict((oc) => oc.doNothing()) - .execute() - } - } - - // Currently using a very simple strategy for updates: purge the existing index - // for the uri then replace it. The main upside is that this allows the indexer - // for each collection to avoid bespoke logic for in-place updates, which isn't - // straightforward in the general case. We still get nice control over notifications. - async updateRecord(uri: AtUri, cid: CID, obj: unknown, timestamp: string) { - if (!this.matchesSchema(obj)) { - throw new Error(`Record does not match schema: ${this.params.lexId}`) - } - - // If the updated record was a dupe, update dupe info for it - const dupe = await this.params.findDuplicate(this.db, uri, obj) - if (dupe) { - await this.db - .updateTable('duplicate_record') - .where('uri', '=', uri.toString()) - .set({ - cid: cid.toString(), - duplicateOf: dupe.toString(), - indexedAt: timestamp, - }) - .execute() - } else { - await this.db - .deleteFrom('duplicate_record') - .where('uri', '=', uri.toString()) - .execute() - } - - const deleted = await this.params.deleteFn(this.db, uri) - if (!deleted) { - // If a record was updated but hadn't been indexed yet, treat it like a plain insert. - return this.insertRecord(uri, cid, obj, timestamp) - } - this.aggregateOnCommit(deleted) - const inserted = await this.params.insertFn( - this.db, - uri, - cid, - obj, - timestamp, - ) - if (!inserted) { - throw new Error( - 'Record update failed: removed from index but could not be replaced', - ) - } - this.aggregateOnCommit(inserted) - await this.handleNotifs({ inserted, deleted }) - } - - async deleteRecord(uri: AtUri, cascading = false) { - await this.db - .deleteFrom('duplicate_record') - .where('uri', '=', uri.toString()) - .execute() - const deleted = await this.params.deleteFn(this.db, uri) - if (!deleted) return - this.aggregateOnCommit(deleted) - if (cascading) { - await this.db - .deleteFrom('duplicate_record') - .where('duplicateOf', '=', uri.toString()) - .execute() - await this.handleNotifs({ deleted }) - return - } else { - const found = await this.db - .selectFrom('duplicate_record') - // @TODO remove ipld_block dependency from app-view - .innerJoin('ipld_block', (join) => - join - .onRef('ipld_block.cid', '=', 'duplicate_record.cid') - .on('ipld_block.creator', '=', uri.host), - ) - .where('duplicateOf', '=', uri.toString()) - .orderBy('duplicate_record.indexedAt', 'asc') - .limit(1) - .selectAll() - .executeTakeFirst() - - if (!found) { - return this.handleNotifs({ deleted }) - } - const record = cborToLexRecord(found.content) - if (!this.matchesSchema(record)) { - return this.handleNotifs({ deleted }) - } - const inserted = await this.params.insertFn( - this.db, - new AtUri(found.uri), - CID.parse(found.cid), - record, - found.indexedAt, - ) - if (inserted) { - this.aggregateOnCommit(inserted) - } - await this.handleNotifs({ deleted, inserted: inserted ?? undefined }) - } - } - - async handleNotifs(op: { deleted?: S; inserted?: S }) { - let notifs: UserNotification[] = [] - const runOnCommit: ((db: Database) => Promise)[] = [] - if (op.deleted) { - const forDelete = this.params.notifsForDelete( - op.deleted, - op.inserted ?? null, - ) - if (forDelete.toDelete.length > 0) { - // Notifs can be deleted in background: they are expensive to delete and - // listNotifications already excludes notifs with missing records. - runOnCommit.push(async (db) => { - await db.db - .deleteFrom('user_notification') - .where('recordUri', 'in', forDelete.toDelete) - .execute() - }) - } - notifs = forDelete.notifs - } else if (op.inserted) { - notifs = this.params.notifsForInsert(op.inserted) - } - if (notifs.length > 0) { - runOnCommit.push(async (db) => { - await db.db.insertInto('user_notification').values(notifs).execute() - }) - } - if (runOnCommit.length) { - // Need to ensure notif deletion always happens before creation, otherwise delete may clobber in a race. - this.appDb.onCommit(() => { - this.backgroundQueue.add(async (db) => { - for (const fn of runOnCommit) { - await fn(db) - } - }) - }) - } - } - - aggregateOnCommit(indexed: S) { - const { updateAggregates } = this.params - if (!updateAggregates) return - this.appDb.onCommit(() => { - this.backgroundQueue.add((db) => updateAggregates(db.db, indexed)) - }) - } -} - -export default RecordProcessor - -export class NoopProcessor extends RecordProcessor { - constructor( - lexId: string, - appDb: Database, - backgroundQueue: BackgroundQueue, - ) { - super(appDb, backgroundQueue, { - lexId, - insertFn: async () => null, - deleteFn: async () => null, - findDuplicate: async () => null, - notifsForInsert: () => [], - notifsForDelete: () => ({ notifs: [], toDelete: [] }), - }) - } -} diff --git a/packages/pds/src/app-view/services/indexing/util.ts b/packages/pds/src/app-view/services/indexing/util.ts deleted file mode 100644 index 5ce78959f8f..00000000000 --- a/packages/pds/src/app-view/services/indexing/util.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { isValidISODateString } from 'iso-datestring-validator' - -// Normalize date strings to simplified ISO so that the lexical sort preserves temporal sort. -// Rather than failing on an invalid date format, returns valid unix epoch. -export function toSimplifiedISOSafe(dateStr: string) { - const date = new Date(dateStr) - if (isNaN(date.getTime())) { - return new Date(0).toISOString() - } - const iso = date.toISOString() - if (!isValidISODateString(iso)) { - // Occurs in rare cases, e.g. where resulting UTC year is negative. These also don't preserve lexical sort. - return new Date(0).toISOString() - } - return iso // YYYY-MM-DDTHH:mm:ss.sssZ -} From 1100756c063ca98745b7b7af5401de62f526f189 Mon Sep 17 00:00:00 2001 From: dholms Date: Tue, 19 Sep 2023 15:00:30 -0500 Subject: [PATCH 02/31] remove message queue & refactor background queue --- packages/bsky/package.json | 1 - .../src/services/indexing/plugins/block.ts | 2 +- .../indexing/plugins/feed-generator.ts | 2 +- .../src/services/indexing/plugins/follow.ts | 2 +- .../src/services/indexing/plugins/like.ts | 2 +- .../services/indexing/plugins/list-block.ts | 2 +- .../services/indexing/plugins/list-item.ts | 2 +- .../src/services/indexing/plugins/list.ts | 2 +- .../src/services/indexing/plugins/post.ts | 2 +- .../src/services/indexing/plugins/repost.ts | 2 +- .../services/indexing/plugins/thread-gate.ts | 2 +- packages/bsky/src/services/label/index.ts | 2 +- packages/common/package.json | 1 + .../indexing/util.ts => common/src/dates.ts} | 0 packages/common/src/index.ts | 1 + packages/dev-env/src/pds.ts | 10 ---- packages/pds/package.json | 1 - .../src/app-view/event-stream/consumers.ts | 38 -------------- .../pds/src/app-view/services/label/index.ts | 3 +- .../background-queue.ts => background.ts} | 4 +- packages/pds/src/context.ts | 8 +-- .../pds/src/event-stream/message-queue.ts | 48 ----------------- packages/pds/src/event-stream/messages.ts | 51 ------------------- packages/pds/src/event-stream/types.ts | 38 -------------- packages/pds/src/index.ts | 8 +-- packages/pds/src/labeler/base.ts | 2 +- packages/pds/src/labeler/hive.ts | 2 +- packages/pds/src/labeler/keyword.ts | 2 +- packages/pds/src/services/index.ts | 12 +---- packages/pds/src/services/moderation/index.ts | 15 ++---- packages/pds/src/services/moderation/views.ts | 6 +-- packages/pds/src/services/record/index.ts | 30 +++-------- packages/pds/src/services/repo/blobs.ts | 2 +- packages/pds/src/services/repo/index.ts | 9 +--- pnpm-lock.yaml | 9 ++-- 35 files changed, 40 insertions(+), 283 deletions(-) rename packages/{bsky/src/services/indexing/util.ts => common/src/dates.ts} (100%) delete mode 100644 packages/pds/src/app-view/event-stream/consumers.ts rename packages/pds/src/{event-stream/background-queue.ts => background.ts} (92%) delete mode 100644 packages/pds/src/event-stream/message-queue.ts delete mode 100644 packages/pds/src/event-stream/messages.ts delete mode 100644 packages/pds/src/event-stream/types.ts diff --git a/packages/bsky/package.json b/packages/bsky/package.json index e9309577801..c0eb6952d28 100644 --- a/packages/bsky/package.json +++ b/packages/bsky/package.json @@ -44,7 +44,6 @@ "http-errors": "^2.0.0", "http-terminator": "^3.2.0", "ioredis": "^5.3.2", - "iso-datestring-validator": "^2.2.2", "kysely": "^0.22.0", "multiformats": "^9.9.0", "p-queue": "^6.6.2", diff --git a/packages/bsky/src/services/indexing/plugins/block.ts b/packages/bsky/src/services/indexing/plugins/block.ts index c0c8fe9d770..bf8ae9e5029 100644 --- a/packages/bsky/src/services/indexing/plugins/block.ts +++ b/packages/bsky/src/services/indexing/plugins/block.ts @@ -1,11 +1,11 @@ import { Selectable } from 'kysely' import { AtUri } from '@atproto/syntax' +import { toSimplifiedISOSafe } from '@atproto/common' import { CID } from 'multiformats/cid' import * as Block from '../../../lexicon/types/app/bsky/graph/block' import * as lex from '../../../lexicon/lexicons' import { DatabaseSchema, DatabaseSchemaType } from '../../../db/database-schema' import RecordProcessor from '../processor' -import { toSimplifiedISOSafe } from '../util' import { PrimaryDatabase } from '../../../db' import { BackgroundQueue } from '../../../background' import { NotificationServer } from '../../../notifications' diff --git a/packages/bsky/src/services/indexing/plugins/feed-generator.ts b/packages/bsky/src/services/indexing/plugins/feed-generator.ts index a0c32ff9129..e4ae5eb4f5a 100644 --- a/packages/bsky/src/services/indexing/plugins/feed-generator.ts +++ b/packages/bsky/src/services/indexing/plugins/feed-generator.ts @@ -1,5 +1,6 @@ import { Selectable } from 'kysely' import { AtUri } from '@atproto/syntax' +import { toSimplifiedISOSafe } from '@atproto/common' import { CID } from 'multiformats/cid' import * as FeedGenerator from '../../../lexicon/types/app/bsky/feed/generator' import * as lex from '../../../lexicon/lexicons' @@ -7,7 +8,6 @@ import { PrimaryDatabase } from '../../../db' import { DatabaseSchema, DatabaseSchemaType } from '../../../db/database-schema' import { BackgroundQueue } from '../../../background' import RecordProcessor from '../processor' -import { toSimplifiedISOSafe } from '../util' import { NotificationServer } from '../../../notifications' const lexId = lex.ids.AppBskyFeedGenerator diff --git a/packages/bsky/src/services/indexing/plugins/follow.ts b/packages/bsky/src/services/indexing/plugins/follow.ts index d6cf1996f98..e9a344db2fd 100644 --- a/packages/bsky/src/services/indexing/plugins/follow.ts +++ b/packages/bsky/src/services/indexing/plugins/follow.ts @@ -1,11 +1,11 @@ import { Selectable } from 'kysely' import { AtUri } from '@atproto/syntax' +import { toSimplifiedISOSafe } from '@atproto/common' import { CID } from 'multiformats/cid' import * as Follow from '../../../lexicon/types/app/bsky/graph/follow' import * as lex from '../../../lexicon/lexicons' import { DatabaseSchema, DatabaseSchemaType } from '../../../db/database-schema' import RecordProcessor from '../processor' -import { toSimplifiedISOSafe } from '../util' import { PrimaryDatabase } from '../../../db' import { countAll, excluded } from '../../../db/util' import { BackgroundQueue } from '../../../background' diff --git a/packages/bsky/src/services/indexing/plugins/like.ts b/packages/bsky/src/services/indexing/plugins/like.ts index 7899b48b1fd..01e0fa5c4fd 100644 --- a/packages/bsky/src/services/indexing/plugins/like.ts +++ b/packages/bsky/src/services/indexing/plugins/like.ts @@ -1,11 +1,11 @@ import { Selectable } from 'kysely' import { AtUri } from '@atproto/syntax' +import { toSimplifiedISOSafe } from '@atproto/common' import { CID } from 'multiformats/cid' import * as Like from '../../../lexicon/types/app/bsky/feed/like' import * as lex from '../../../lexicon/lexicons' import { DatabaseSchema, DatabaseSchemaType } from '../../../db/database-schema' import RecordProcessor from '../processor' -import { toSimplifiedISOSafe } from '../util' import { countAll, excluded } from '../../../db/util' import { PrimaryDatabase } from '../../../db' import { BackgroundQueue } from '../../../background' diff --git a/packages/bsky/src/services/indexing/plugins/list-block.ts b/packages/bsky/src/services/indexing/plugins/list-block.ts index 4285ca8d4bc..33dc7cfc51a 100644 --- a/packages/bsky/src/services/indexing/plugins/list-block.ts +++ b/packages/bsky/src/services/indexing/plugins/list-block.ts @@ -1,5 +1,6 @@ import { Selectable } from 'kysely' import { AtUri } from '@atproto/syntax' +import { toSimplifiedISOSafe } from '@atproto/common' import { CID } from 'multiformats/cid' import * as ListBlock from '../../../lexicon/types/app/bsky/graph/listblock' import * as lex from '../../../lexicon/lexicons' @@ -8,7 +9,6 @@ import { DatabaseSchema, DatabaseSchemaType } from '../../../db/database-schema' import RecordProcessor from '../processor' import { BackgroundQueue } from '../../../background' import { NotificationServer } from '../../../notifications' -import { toSimplifiedISOSafe } from '../util' const lexId = lex.ids.AppBskyGraphListblock type IndexedListBlock = Selectable diff --git a/packages/bsky/src/services/indexing/plugins/list-item.ts b/packages/bsky/src/services/indexing/plugins/list-item.ts index 231fb761e16..2ab125062a7 100644 --- a/packages/bsky/src/services/indexing/plugins/list-item.ts +++ b/packages/bsky/src/services/indexing/plugins/list-item.ts @@ -1,11 +1,11 @@ import { Selectable } from 'kysely' import { AtUri } from '@atproto/syntax' +import { toSimplifiedISOSafe } from '@atproto/common' import { CID } from 'multiformats/cid' import * as ListItem from '../../../lexicon/types/app/bsky/graph/listitem' import * as lex from '../../../lexicon/lexicons' import { DatabaseSchema, DatabaseSchemaType } from '../../../db/database-schema' import RecordProcessor from '../processor' -import { toSimplifiedISOSafe } from '../util' import { InvalidRequestError } from '@atproto/xrpc-server' import { PrimaryDatabase } from '../../../db' import { BackgroundQueue } from '../../../background' diff --git a/packages/bsky/src/services/indexing/plugins/list.ts b/packages/bsky/src/services/indexing/plugins/list.ts index c74c09c274f..293c457c4fb 100644 --- a/packages/bsky/src/services/indexing/plugins/list.ts +++ b/packages/bsky/src/services/indexing/plugins/list.ts @@ -1,11 +1,11 @@ import { Selectable } from 'kysely' import { AtUri } from '@atproto/syntax' +import { toSimplifiedISOSafe } from '@atproto/common' import { CID } from 'multiformats/cid' import * as List from '../../../lexicon/types/app/bsky/graph/list' import * as lex from '../../../lexicon/lexicons' import { DatabaseSchema, DatabaseSchemaType } from '../../../db/database-schema' import RecordProcessor from '../processor' -import { toSimplifiedISOSafe } from '../util' import { PrimaryDatabase } from '../../../db' import { BackgroundQueue } from '../../../background' import { NotificationServer } from '../../../notifications' diff --git a/packages/bsky/src/services/indexing/plugins/post.ts b/packages/bsky/src/services/indexing/plugins/post.ts index f57bc10179b..a3e5b486957 100644 --- a/packages/bsky/src/services/indexing/plugins/post.ts +++ b/packages/bsky/src/services/indexing/plugins/post.ts @@ -1,6 +1,7 @@ import { Insertable, Selectable, sql } from 'kysely' import { CID } from 'multiformats/cid' import { AtUri } from '@atproto/syntax' +import { toSimplifiedISOSafe } from '@atproto/common' import { jsonStringToLex } from '@atproto/lexicon' import { Record as PostRecord, @@ -19,7 +20,6 @@ import * as lex from '../../../lexicon/lexicons' import { DatabaseSchema, DatabaseSchemaType } from '../../../db/database-schema' import RecordProcessor from '../processor' import { Notification } from '../../../db/tables/notification' -import { toSimplifiedISOSafe } from '../util' import { PrimaryDatabase } from '../../../db' import { countAll, excluded } from '../../../db/util' import { BackgroundQueue } from '../../../background' diff --git a/packages/bsky/src/services/indexing/plugins/repost.ts b/packages/bsky/src/services/indexing/plugins/repost.ts index aa93d7b0f61..9c46b9b3376 100644 --- a/packages/bsky/src/services/indexing/plugins/repost.ts +++ b/packages/bsky/src/services/indexing/plugins/repost.ts @@ -1,11 +1,11 @@ import { Selectable } from 'kysely' import { CID } from 'multiformats/cid' import { AtUri } from '@atproto/syntax' +import { toSimplifiedISOSafe } from '@atproto/common' import * as Repost from '../../../lexicon/types/app/bsky/feed/repost' import * as lex from '../../../lexicon/lexicons' import { DatabaseSchema, DatabaseSchemaType } from '../../../db/database-schema' import RecordProcessor from '../processor' -import { toSimplifiedISOSafe } from '../util' import { PrimaryDatabase } from '../../../db' import { countAll, excluded } from '../../../db/util' import { BackgroundQueue } from '../../../background' diff --git a/packages/bsky/src/services/indexing/plugins/thread-gate.ts b/packages/bsky/src/services/indexing/plugins/thread-gate.ts index fb0928f2459..37f3ddb062e 100644 --- a/packages/bsky/src/services/indexing/plugins/thread-gate.ts +++ b/packages/bsky/src/services/indexing/plugins/thread-gate.ts @@ -1,11 +1,11 @@ import { AtUri } from '@atproto/syntax' import { InvalidRequestError } from '@atproto/xrpc-server' +import { toSimplifiedISOSafe } from '@atproto/common' import { CID } from 'multiformats/cid' import * as Threadgate from '../../../lexicon/types/app/bsky/feed/threadgate' import * as lex from '../../../lexicon/lexicons' import { DatabaseSchema, DatabaseSchemaType } from '../../../db/database-schema' import RecordProcessor from '../processor' -import { toSimplifiedISOSafe } from '../util' import { PrimaryDatabase } from '../../../db' import { BackgroundQueue } from '../../../background' import { NotificationServer } from '../../../notifications' diff --git a/packages/bsky/src/services/label/index.ts b/packages/bsky/src/services/label/index.ts index 855038ab14c..7d351b95011 100644 --- a/packages/bsky/src/services/label/index.ts +++ b/packages/bsky/src/services/label/index.ts @@ -1,9 +1,9 @@ import { sql } from 'kysely' import { AtUri } from '@atproto/syntax' +import { toSimplifiedISOSafe } from '@atproto/common' import { Database } from '../../db' import { Label, isSelfLabels } from '../../lexicon/types/com/atproto/label/defs' import { ids } from '../../lexicon/lexicons' -import { toSimplifiedISOSafe } from '../indexing/util' import { LabelCache } from '../../label-cache' export type Labels = Record diff --git a/packages/common/package.json b/packages/common/package.json index c7f518c1a99..604678cc6eb 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -22,6 +22,7 @@ "@atproto/common-web": "workspace:^", "@ipld/dag-cbor": "^7.0.3", "cbor-x": "^1.5.1", + "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "pino": "^8.15.0", "zod": "3.21.4" diff --git a/packages/bsky/src/services/indexing/util.ts b/packages/common/src/dates.ts similarity index 100% rename from packages/bsky/src/services/indexing/util.ts rename to packages/common/src/dates.ts diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index fd049fc2e5d..524a090c5ab 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -1,4 +1,5 @@ export * from '@atproto/common-web' +export * from './dates' export * from './fs' export * from './ipld' export * from './ipld-multi' diff --git a/packages/dev-env/src/pds.ts b/packages/dev-env/src/pds.ts index 97b8ceb7452..2c374d61eb4 100644 --- a/packages/dev-env/src/pds.ts +++ b/packages/dev-env/src/pds.ts @@ -2,7 +2,6 @@ import getPort from 'get-port' import * as ui8 from 'uint8arrays' import * as pds from '@atproto/pds' import { Secp256k1Keypair, randomStr } from '@atproto/crypto' -import { MessageDispatcher } from '@atproto/pds/src/event-stream/message-queue' import { AtpAgent } from '@atproto/api' import { Client as PlcClient } from '@did-plc/lib' import { DAY, HOUR } from '@atproto/common-web' @@ -82,15 +81,6 @@ export class TestPds { : pds.Database.memory() await db.migrateToLatestOrThrow() - if ( - config.bskyAppViewEndpoint && - config.bskyAppViewProxy && - !cfg.enableInProcessAppView - ) { - // Disable communication to app view within pds - MessageDispatcher.prototype.send = async () => {} - } - const server = pds.PDS.create({ db, blobstore, diff --git a/packages/pds/package.json b/packages/pds/package.json index 422880426f9..85453574c68 100644 --- a/packages/pds/package.json +++ b/packages/pds/package.json @@ -50,7 +50,6 @@ "http-errors": "^2.0.0", "http-terminator": "^3.2.0", "ioredis": "^5.3.2", - "iso-datestring-validator": "^2.2.2", "jsonwebtoken": "^8.5.1", "kysely": "^0.22.0", "multiformats": "^9.9.0", diff --git a/packages/pds/src/app-view/event-stream/consumers.ts b/packages/pds/src/app-view/event-stream/consumers.ts deleted file mode 100644 index 48fc963fb09..00000000000 --- a/packages/pds/src/app-view/event-stream/consumers.ts +++ /dev/null @@ -1,38 +0,0 @@ -import AppContext from '../../context' -import Database from '../../db' -import { - DeleteRecord, - IndexRecord, - DeleteRepo, -} from '../../event-stream/messages' - -// Used w/ in-process PDS as alternative to the repo subscription -export const listen = (ctx: AppContext) => { - ctx.messageDispatcher.listen('index_record', { - async listener(input: { db: Database; message: IndexRecord }) { - const { db, message } = input - const indexingService = ctx.services.appView.indexing(db) - await indexingService.indexRecord( - message.uri, - message.cid, - message.obj, - message.action, - message.timestamp, - ) - }, - }) - ctx.messageDispatcher.listen('delete_record', { - async listener(input: { db: Database; message: DeleteRecord }) { - const { db, message } = input - const indexingService = ctx.services.appView.indexing(db) - await indexingService.deleteRecord(message.uri, message.cascading) - }, - }) - ctx.messageDispatcher.listen('delete_repo', { - async listener(input: { db: Database; message: DeleteRepo }) { - const { db, message } = input - const indexingService = ctx.services.appView.indexing(db) - await indexingService.deleteForUser(message.did) - }, - }) -} diff --git a/packages/pds/src/app-view/services/label/index.ts b/packages/pds/src/app-view/services/label/index.ts index 338475ba633..24ae99034c1 100644 --- a/packages/pds/src/app-view/services/label/index.ts +++ b/packages/pds/src/app-view/services/label/index.ts @@ -1,6 +1,6 @@ import { sql } from 'kysely' -import { CID } from 'multiformats/cid' import { AtUri } from '@atproto/syntax' +import { toSimplifiedISOSafe } from '@atproto/common' import Database from '../../../db' import { Label, @@ -8,7 +8,6 @@ import { } from '../../../lexicon/types/com/atproto/label/defs' import { ids } from '../../../lexicon/lexicons' import { LabelCache } from '../../../label-cache' -import { toSimplifiedISOSafe } from '../indexing/util' export type Labels = Record diff --git a/packages/pds/src/event-stream/background-queue.ts b/packages/pds/src/background.ts similarity index 92% rename from packages/pds/src/event-stream/background-queue.ts rename to packages/pds/src/background.ts index aa7671ccc07..65d8cbd473b 100644 --- a/packages/pds/src/event-stream/background-queue.ts +++ b/packages/pds/src/background.ts @@ -1,6 +1,6 @@ import PQueue from 'p-queue' -import Database from '../db' -import { dbLogger } from '../logger' +import Database from './db' +import { dbLogger } from './logger' // A simple queue for in-process, out-of-band/backgrounded work diff --git a/packages/pds/src/context.ts b/packages/pds/src/context.ts index 1f414845ea1..c8ac8c41ba2 100644 --- a/packages/pds/src/context.ts +++ b/packages/pds/src/context.ts @@ -13,10 +13,9 @@ import { ModerationMailer } from './mailer/moderation' import { BlobStore } from '@atproto/repo' import { ImageUriBuilder } from './image/uri' import { Services } from './services' -import { MessageDispatcher } from './event-stream/message-queue' import { Sequencer, SequencerLeader } from './sequencer' import { Labeler } from './labeler' -import { BackgroundQueue } from './event-stream/background-queue' +import { BackgroundQueue } from './background' import DidSqlCache from './did-cache' import { MountedAlgos } from './feed-gen/types' import { Crawlers } from './crawlers' @@ -39,7 +38,6 @@ export class AppContext { mailer: ServerMailer moderationMailer: ModerationMailer services: Services - messageDispatcher: MessageDispatcher sequencer: Sequencer sequencerLeader: SequencerLeader | null labeler: Labeler @@ -124,10 +122,6 @@ export class AppContext { return this.opts.services } - get messageDispatcher(): MessageDispatcher { - return this.opts.messageDispatcher - } - get sequencer(): Sequencer { return this.opts.sequencer } diff --git a/packages/pds/src/event-stream/message-queue.ts b/packages/pds/src/event-stream/message-queue.ts deleted file mode 100644 index 20aa5d5b862..00000000000 --- a/packages/pds/src/event-stream/message-queue.ts +++ /dev/null @@ -1,48 +0,0 @@ -import Database from '../db' -import { dbLogger as log } from '../logger' -import { MessageQueue, Listenable, Listener, MessageOfType } from './types' - -// @NOTE A message dispatcher for loose coupling within db transactions. -// Messages are handled immediately. This should not be around for long. -export class MessageDispatcher implements MessageQueue { - private destroyed = false - private listeners: Map = new Map() - - async send( - tx: Database, - message: MessageOfType | MessageOfType[], - ): Promise { - if (this.destroyed) return - const messages = Array.isArray(message) ? message : [message] - for (const msg of messages) { - await this.handleMessage(tx, msg) - } - } - - listen>( - topic: T, - listenable: Listenable, - ) { - const listeners = this.listeners.get(topic) ?? [] - listeners.push(listenable.listener as Listener) // @TODO avoid upcast - this.listeners.set(topic, listeners) - } - - destroy(): void { - this.destroyed = true - } - - private async handleMessage(db: Database, message: MessageOfType) { - const listeners = this.listeners.get(message.type) - if (!listeners?.length) { - return log.error({ message }, `no listeners for event: ${message.type}`) - } - for (const listener of listeners) { - await listener({ message, db }) - } - } - - // Unused by MessageDispatcher - async processNext(): Promise {} - async processAll(): Promise {} -} diff --git a/packages/pds/src/event-stream/messages.ts b/packages/pds/src/event-stream/messages.ts deleted file mode 100644 index 9c5f00baca6..00000000000 --- a/packages/pds/src/event-stream/messages.ts +++ /dev/null @@ -1,51 +0,0 @@ -// Below specific to message dispatcher - -import { CID } from 'multiformats/cid' -import { AtUri } from '@atproto/syntax' -import { WriteOpAction } from '@atproto/repo' - -export type IndexRecord = { - type: 'index_record' - action: WriteOpAction.Create | WriteOpAction.Update - uri: AtUri - cid: CID - obj: unknown - timestamp: string -} - -export type DeleteRecord = { - type: 'delete_record' - uri: AtUri - cascading: boolean -} - -export type DeleteRepo = { - type: 'delete_repo' - did: string -} - -export const indexRecord = ( - uri: AtUri, - cid: CID, - obj: unknown, - action: WriteOpAction.Create | WriteOpAction.Update, - timestamp: string, -): IndexRecord => ({ - type: 'index_record', - uri, - cid, - obj, - action, - timestamp, -}) - -export const deleteRecord = (uri: AtUri, cascading: boolean): DeleteRecord => ({ - type: 'delete_record', - uri, - cascading, -}) - -export const deleteRepo = (did: string): DeleteRepo => ({ - type: 'delete_repo', - did, -}) diff --git a/packages/pds/src/event-stream/types.ts b/packages/pds/src/event-stream/types.ts deleted file mode 100644 index aad8fc5f01b..00000000000 --- a/packages/pds/src/event-stream/types.ts +++ /dev/null @@ -1,38 +0,0 @@ -import Database from '../db' - -export type MessageOfType = { - type: T - [s: string]: unknown -} - -export type Listener = (ctx: { - message: M - db: Database -}) => Promise - -export interface Listenable { - listener: Listener -} - -export abstract class Consumer - implements Listenable -{ - abstract dispatch(ctx: { - db: Database - message: M - }): Promise - get listener() { - return this.dispatch.bind(this) - } -} - -export interface MessageQueue { - send(tx: Database, message: MessageOfType | MessageOfType[]): Promise - listen>( - topic: T, - listenable: Listenable, - ): void - processNext(): Promise - processAll(): Promise - destroy(): void -} diff --git a/packages/pds/src/index.ts b/packages/pds/src/index.ts index a156d8f7b8e..5e8688c1610 100644 --- a/packages/pds/src/index.ts +++ b/packages/pds/src/index.ts @@ -21,7 +21,6 @@ import { Options as XrpcServerOptions, } from '@atproto/xrpc-server' import { DAY, HOUR, MINUTE } from '@atproto/common' -import * as appviewConsumers from './app-view/event-stream/consumers' import inProcessAppView from './app-view/api' import API from './api' import * as basicRoutes from './basic-routes' @@ -35,7 +34,6 @@ import { ServerConfig } from './config' import { ServerMailer } from './mailer' import { ModerationMailer } from './mailer/moderation' import { createServer } from './lexicon' -import { MessageDispatcher } from './event-stream/message-queue' import { ImageUriBuilder } from './image/uri' import { BlobDiskCache, ImageProcessingServer } from './image/server' import { createServices } from './services' @@ -47,7 +45,7 @@ import { ImageProcessingServerInvalidator, } from './image/invalidator' import { Labeler, HiveLabeler, KeywordLabeler } from './labeler' -import { BackgroundQueue } from './event-stream/background-queue' +import { BackgroundQueue } from './background' import DidSqlCache from './did-cache' import { MountedAlgos } from './feed-gen/types' import { Crawlers } from './crawlers' @@ -114,7 +112,6 @@ export class PDS { backupNameservers: config.handleResolveNameservers, }) - const messageDispatcher = new MessageDispatcher() const sequencer = new Sequencer(db) const sequencerLeader = config.sequencerLeaderEnabled ? new SequencerLeader(db, config.sequencerLeaderLockId) @@ -205,7 +202,6 @@ export class PDS { const services = createServices({ repoSigningKey, - messageDispatcher, blobstore, imgUriBuilder, imgInvalidator, @@ -238,7 +234,6 @@ export class PDS { didCache, cfg: config, auth, - messageDispatcher, sequencer, sequencerLeader, labeler, @@ -348,7 +343,6 @@ export class PDS { } } }, 500) - appviewConsumers.listen(this.ctx) this.ctx.sequencerLeader?.run() await this.ctx.sequencer.start() await this.ctx.db.startListeningToChannels() diff --git a/packages/pds/src/labeler/base.ts b/packages/pds/src/labeler/base.ts index 8a52ed2c717..b3efefad327 100644 --- a/packages/pds/src/labeler/base.ts +++ b/packages/pds/src/labeler/base.ts @@ -3,7 +3,7 @@ import { BlobStore, cidForRecord } from '@atproto/repo' import { dedupe, getFieldsFromRecord } from './util' import { AtUri } from '@atproto/syntax' import { labelerLogger as log } from '../logger' -import { BackgroundQueue } from '../event-stream/background-queue' +import { BackgroundQueue } from '../background' import { CID } from 'multiformats/cid' export abstract class Labeler { diff --git a/packages/pds/src/labeler/hive.ts b/packages/pds/src/labeler/hive.ts index e4a0bc48045..b14b84f3479 100644 --- a/packages/pds/src/labeler/hive.ts +++ b/packages/pds/src/labeler/hive.ts @@ -5,7 +5,7 @@ import { Labeler } from './base' import Database from '../db' import { BlobStore } from '@atproto/repo' import { keywordLabeling } from './util' -import { BackgroundQueue } from '../event-stream/background-queue' +import { BackgroundQueue } from '../background' import { CID } from 'multiformats/cid' const HIVE_ENDPOINT = 'https://api.thehive.ai/api/v2/task/sync' diff --git a/packages/pds/src/labeler/keyword.ts b/packages/pds/src/labeler/keyword.ts index 8838e87486a..0a6bf9ca3d2 100644 --- a/packages/pds/src/labeler/keyword.ts +++ b/packages/pds/src/labeler/keyword.ts @@ -2,7 +2,7 @@ import { BlobStore } from '@atproto/repo' import Database from '../db' import { Labeler } from './base' import { keywordLabeling } from './util' -import { BackgroundQueue } from '../event-stream/background-queue' +import { BackgroundQueue } from '../background' export class KeywordLabeler extends Labeler { keywords: Record diff --git a/packages/pds/src/services/index.ts b/packages/pds/src/services/index.ts index b49693cc8cf..582a082d129 100644 --- a/packages/pds/src/services/index.ts +++ b/packages/pds/src/services/index.ts @@ -2,7 +2,6 @@ import { AtpAgent } from '@atproto/api' import * as crypto from '@atproto/crypto' import { BlobStore } from '@atproto/repo' import Database from '../db' -import { MessageDispatcher } from '../event-stream/message-queue' import { ImageUriBuilder } from '../image/uri' import { ImageInvalidator } from '../image/invalidator' import { AccountService } from './account' @@ -13,17 +12,15 @@ import { ModerationService } from './moderation' import { ActorService } from '../app-view/services/actor' import { GraphService } from '../app-view/services/graph' import { FeedService } from '../app-view/services/feed' -import { IndexingService } from '../app-view/services/indexing' import { Labeler } from '../labeler' import { LabelService } from '../app-view/services/label' -import { BackgroundQueue } from '../event-stream/background-queue' +import { BackgroundQueue } from '../background' import { Crawlers } from '../crawlers' import { LabelCache } from '../label-cache' import { LocalService } from './local' export function createServices(resources: { repoSigningKey: crypto.Keypair - messageDispatcher: MessageDispatcher blobstore: BlobStore imgUriBuilder: ImageUriBuilder imgInvalidator: ImageInvalidator @@ -37,7 +34,6 @@ export function createServices(resources: { }): Services { const { repoSigningKey, - messageDispatcher, blobstore, imgUriBuilder, imgInvalidator, @@ -52,10 +48,9 @@ export function createServices(resources: { return { account: AccountService.creator(), auth: AuthService.creator(), - record: RecordService.creator(messageDispatcher), + record: RecordService.creator(), repo: RepoService.creator( repoSigningKey, - messageDispatcher, blobstore, backgroundQueue, crawlers, @@ -68,7 +63,6 @@ export function createServices(resources: { appviewCdnUrlPattern, ), moderation: ModerationService.creator( - messageDispatcher, blobstore, imgUriBuilder, imgInvalidator, @@ -77,7 +71,6 @@ export function createServices(resources: { actor: ActorService.creator(imgUriBuilder, labelCache), graph: GraphService.creator(imgUriBuilder), feed: FeedService.creator(imgUriBuilder, labelCache), - indexing: IndexingService.creator(backgroundQueue), label: LabelService.creator(labelCache), }, } @@ -92,7 +85,6 @@ export type Services = { moderation: FromDb appView: { feed: FromDb - indexing: FromDb actor: FromDb graph: FromDb label: FromDb diff --git a/packages/pds/src/services/moderation/index.ts b/packages/pds/src/services/moderation/index.ts index 0a5ba6d02b0..424b1b2ad93 100644 --- a/packages/pds/src/services/moderation/index.ts +++ b/packages/pds/src/services/moderation/index.ts @@ -4,7 +4,6 @@ import { BlobStore } from '@atproto/repo' import { AtUri } from '@atproto/syntax' import { InvalidRequestError } from '@atproto/xrpc-server' import Database from '../../db' -import { MessageQueue } from '../../event-stream/types' import { ModerationAction, ModerationReport } from '../../db/tables/moderation' import { RecordService } from '../record' import { ModerationViews } from './views' @@ -17,32 +16,24 @@ import { addHoursToDate } from '../../util/date' export class ModerationService { constructor( public db: Database, - public messageDispatcher: MessageQueue, public blobstore: BlobStore, public imgUriBuilder: ImageUriBuilder, public imgInvalidator: ImageInvalidator, ) {} static creator( - messageDispatcher: MessageQueue, blobstore: BlobStore, imgUriBuilder: ImageUriBuilder, imgInvalidator: ImageInvalidator, ) { return (db: Database) => - new ModerationService( - db, - messageDispatcher, - blobstore, - imgUriBuilder, - imgInvalidator, - ) + new ModerationService(db, blobstore, imgUriBuilder, imgInvalidator) } - views = new ModerationViews(this.db, this.messageDispatcher) + views = new ModerationViews(this.db) services = { - record: RecordService.creator(this.messageDispatcher), + record: RecordService.creator(), } async getAction(id: number): Promise { diff --git a/packages/pds/src/services/moderation/views.ts b/packages/pds/src/services/moderation/views.ts index 5676e086f35..8f0fec04ac7 100644 --- a/packages/pds/src/services/moderation/views.ts +++ b/packages/pds/src/services/moderation/views.ts @@ -2,7 +2,6 @@ import { Selectable } from 'kysely' import { ArrayEl, cborBytesToRecord } from '@atproto/common' import { AtUri } from '@atproto/syntax' import Database from '../../db' -import { MessageQueue } from '../../event-stream/types' import { DidHandle } from '../../db/tables/did-handle' import { RepoRoot } from '../../db/tables/repo-root' import { @@ -23,14 +22,13 @@ import { AccountService } from '../account' import { RecordService } from '../record' import { ModerationReportRowWithHandle } from '.' import { getSelfLabels } from '../../app-view/services/label' -import { jsonStringToLex } from '@atproto/lexicon' export class ModerationViews { - constructor(private db: Database, private messageDispatcher: MessageQueue) {} + constructor(private db: Database) {} services = { account: AccountService.creator(), - record: RecordService.creator(this.messageDispatcher), + record: RecordService.creator(), } repo(result: RepoResult, opts: ModViewOptions): Promise diff --git a/packages/pds/src/services/record/index.ts b/packages/pds/src/services/record/index.ts index 857ae7f1d95..3cdb7bbc05e 100644 --- a/packages/pds/src/services/record/index.ts +++ b/packages/pds/src/services/record/index.ts @@ -6,19 +6,13 @@ import { dbLogger as log } from '../../logger' import Database from '../../db' import { notSoftDeletedClause } from '../../db/util' import { Backlink } from '../../db/tables/backlink' -import { MessageQueue } from '../../event-stream/types' -import { - indexRecord, - deleteRecord, - deleteRepo, -} from '../../event-stream/messages' import { ids } from '../../lexicon/lexicons' export class RecordService { - constructor(public db: Database, public messageDispatcher: MessageQueue) {} + constructor(public db: Database) {} - static creator(messageDispatcher: MessageQueue) { - return (db: Database) => new RecordService(db, messageDispatcher) + static creator() { + return (db: Database) => new RecordService(db) } async indexRecord( @@ -70,30 +64,19 @@ export class RecordService { } await this.addBacklinks(backlinks) - // Send to indexers - await this.messageDispatcher.send( - this.db, - indexRecord(uri, cid, obj, action, record.indexedAt), - ) - log.info({ uri }, 'indexed record') } - async deleteRecord(uri: AtUri, cascading = false) { + async deleteRecord(uri: AtUri) { this.db.assertTransaction() log.debug({ uri }, 'deleting indexed record') const deleteQuery = this.db.db .deleteFrom('record') .where('uri', '=', uri.toString()) - .execute() - await this.db.db + const backlinkQuery = this.db.db .deleteFrom('backlink') .where('uri', '=', uri.toString()) - .execute() - await Promise.all([ - this.messageDispatcher.send(this.db, deleteRecord(uri, cascading)), - deleteQuery, - ]) + await Promise.all([deleteQuery.execute(), backlinkQuery.execute()]) log.info({ uri }, 'deleted indexed record') } @@ -233,7 +216,6 @@ export class RecordService { async deleteForActor(did: string) { // Not done in transaction because it would be too long, prone to contention. // Also, this can safely be run multiple times if it fails. - await this.messageDispatcher.send(this.db, deleteRepo(did)) // Needs record table await this.db.db.deleteFrom('record').where('did', '=', did).execute() await this.db.db .deleteFrom('user_notification') diff --git a/packages/pds/src/services/repo/blobs.ts b/packages/pds/src/services/repo/blobs.ts index 7078f66a76e..a366f4def1f 100644 --- a/packages/pds/src/services/repo/blobs.ts +++ b/packages/pds/src/services/repo/blobs.ts @@ -13,7 +13,7 @@ import { Blob as BlobTable } from '../../db/tables/blob' import * as img from '../../image' import { BlobRef } from '@atproto/lexicon' import { PreparedDelete, PreparedUpdate } from '../../repo' -import { BackgroundQueue } from '../../event-stream/background-queue' +import { BackgroundQueue } from '../../background' export class RepoBlobs { constructor( diff --git a/packages/pds/src/services/repo/index.ts b/packages/pds/src/services/repo/index.ts index 8b8db8eb6be..568707e7ce3 100644 --- a/packages/pds/src/services/repo/index.ts +++ b/packages/pds/src/services/repo/index.ts @@ -4,7 +4,6 @@ import { BlobStore, CommitData, Repo, WriteOpAction } from '@atproto/repo' import { InvalidRequestError } from '@atproto/xrpc-server' import { AtUri } from '@atproto/syntax' import Database from '../../db' -import { MessageQueue } from '../../event-stream/types' import SqlRepoStorage from '../../sql-repo-storage' import { BadCommitSwapError, @@ -18,7 +17,7 @@ import { RecordService } from '../record' import * as sequencer from '../../sequencer' import { Labeler } from '../../labeler' import { wait } from '@atproto/common' -import { BackgroundQueue } from '../../event-stream/background-queue' +import { BackgroundQueue } from '../../background' import { Crawlers } from '../../crawlers' export class RepoService { @@ -27,7 +26,6 @@ export class RepoService { constructor( public db: Database, public repoSigningKey: crypto.Keypair, - public messageDispatcher: MessageQueue, public blobstore: BlobStore, public backgroundQueue: BackgroundQueue, public crawlers: Crawlers, @@ -38,7 +36,6 @@ export class RepoService { static creator( keypair: crypto.Keypair, - messageDispatcher: MessageQueue, blobstore: BlobStore, backgroundQueue: BackgroundQueue, crawlers: Crawlers, @@ -48,7 +45,6 @@ export class RepoService { new RepoService( db, keypair, - messageDispatcher, blobstore, backgroundQueue, crawlers, @@ -57,7 +53,7 @@ export class RepoService { } services = { - record: RecordService.creator(this.messageDispatcher), + record: RecordService.creator(), } private async serviceTx( @@ -68,7 +64,6 @@ export class RepoService { const srvc = new RepoService( dbTxn, this.repoSigningKey, - this.messageDispatcher, this.blobstore, this.backgroundQueue, this.crawlers, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fb80b8c6a35..5a009d78b4a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -213,9 +213,6 @@ importers: ioredis: specifier: ^5.3.2 version: 5.3.2 - iso-datestring-validator: - specifier: ^2.2.2 - version: 2.2.2 kysely: specifier: ^0.22.0 version: 0.22.0 @@ -292,6 +289,9 @@ importers: cbor-x: specifier: ^1.5.1 version: 1.5.1 + iso-datestring-validator: + specifier: ^2.2.2 + version: 2.2.2 multiformats: specifier: ^9.9.0 version: 9.9.0 @@ -543,9 +543,6 @@ importers: ioredis: specifier: ^5.3.2 version: 5.3.2 - iso-datestring-validator: - specifier: ^2.2.2 - version: 2.2.2 jsonwebtoken: specifier: ^8.5.1 version: 8.5.1 From 3cc383ca2dca7828c877d1058a383daebca8545b Mon Sep 17 00:00:00 2001 From: dholms Date: Tue, 19 Sep 2023 15:37:40 -0500 Subject: [PATCH 03/31] wip --- .../app-view/api/app/bsky/actor/getProfile.ts | 55 +--- .../api/app/bsky/actor/getProfiles.ts | 39 +-- .../api/app/bsky/actor/getSuggestions.ts | 69 +--- .../api/app/bsky/actor/searchActors.ts | 79 +---- .../app/bsky/actor/searchActorsTypeahead.ts | 75 +---- .../api/app/bsky/feed/getActorFeeds.ts | 59 +--- .../api/app/bsky/feed/getActorLikes.ts | 75 +---- .../api/app/bsky/feed/getAuthorFeed.ts | 125 +------ .../src/app-view/api/app/bsky/feed/getFeed.ts | 208 +----------- .../api/app/bsky/feed/getFeedGenerator.ts | 68 +--- .../api/app/bsky/feed/getFeedGenerators.ts | 31 +- .../app-view/api/app/bsky/feed/getLikes.ts | 73 +---- .../api/app/bsky/feed/getPostThread.ts | 307 ++---------------- .../app-view/api/app/bsky/feed/getPosts.ts | 37 +-- .../api/app/bsky/feed/getRepostedBy.ts | 63 +--- .../app-view/api/app/bsky/feed/getTimeline.ts | 102 +----- .../app-view/api/app/bsky/graph/getBlocks.ts | 62 +--- .../api/app/bsky/graph/getFollowers.ts | 88 +---- .../app-view/api/app/bsky/graph/getFollows.ts | 88 +---- .../app-view/api/app/bsky/graph/getList.ts | 83 +---- .../api/app/bsky/graph/getListMutes.ts | 50 +-- .../app-view/api/app/bsky/graph/getLists.ts | 58 +--- .../app-view/api/app/bsky/graph/getMutes.ts | 57 +--- .../src/app-view/api/app/bsky/unspecced.ts | 181 ++--------- .../pds/src/app-view/services/label/index.ts | 1 - packages/pds/src/config.ts | 29 +- packages/pds/src/context.ts | 28 +- packages/pds/src/index.ts | 5 +- 28 files changed, 176 insertions(+), 2019 deletions(-) diff --git a/packages/pds/src/app-view/api/app/bsky/actor/getProfile.ts b/packages/pds/src/app-view/api/app/bsky/actor/getProfile.ts index 540a8cc3779..4d510bda720 100644 --- a/packages/pds/src/app-view/api/app/bsky/actor/getProfile.ts +++ b/packages/pds/src/app-view/api/app/bsky/actor/getProfile.ts @@ -1,6 +1,4 @@ -import { InvalidRequestError } from '@atproto/xrpc-server' import { Server } from '../../../../../lexicon' -import { softDeleted } from '../../../../../db/util' import AppContext from '../../../../../context' import { authPassthru } from '../../../../../api/com/atproto/admin/util' import { OutputSchema } from '../../../../../lexicon/types/app/bsky/actor/getProfile' @@ -13,57 +11,16 @@ export default function (server: Server, ctx: AppContext) { handler: async ({ req, auth, params }) => { const requester = auth.credentials.type === 'access' ? auth.credentials.did : null - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.actor.getProfile( - params, - requester - ? await ctx.serviceAuthHeaders(requester) - : authPassthru(req), - ) - if (res.data.did === requester) { - return await handleReadAfterWrite( - ctx, - requester, - res, - getProfileMunge, - ) - } - return { - encoding: 'application/json', - body: res.data, - } - } - - // As long as user has triage permission, we know that they are a moderator user and can see taken down profiles - const canViewTakendownProfile = - auth.credentials.type === 'role' && auth.credentials.triage - const { actor } = params - const { db, services } = ctx - const actorService = services.appView.actor(db) - - const actorRes = await actorService.getActor(actor, true) - - if (!actorRes) { - throw new InvalidRequestError('Profile not found') - } - if (!canViewTakendownProfile && softDeleted(actorRes)) { - throw new InvalidRequestError( - 'Account has been taken down', - 'AccountTakedown', - ) - } - const profile = await actorService.views.profileDetailed( - actorRes, - requester, - { includeSoftDeleted: canViewTakendownProfile }, + const res = await ctx.appviewAgent.api.app.bsky.actor.getProfile( + params, + requester ? await ctx.serviceAuthHeaders(requester) : authPassthru(req), ) - if (!profile) { - throw new InvalidRequestError('Profile not found') + if (res.data.did === requester) { + return await handleReadAfterWrite(ctx, requester, res, getProfileMunge) } - return { encoding: 'application/json', - body: profile, + body: res.data, } }, }) diff --git a/packages/pds/src/app-view/api/app/bsky/actor/getProfiles.ts b/packages/pds/src/app-view/api/app/bsky/actor/getProfiles.ts index 0750a6109d7..08f30dfe690 100644 --- a/packages/pds/src/app-view/api/app/bsky/actor/getProfiles.ts +++ b/packages/pds/src/app-view/api/app/bsky/actor/getProfiles.ts @@ -9,40 +9,17 @@ export default function (server: Server, ctx: AppContext) { auth: ctx.accessVerifier, handler: async ({ auth, params }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.actor.getProfiles( - params, - await ctx.serviceAuthHeaders(requester), - ) - const hasSelf = res.data.profiles.some((prof) => prof.did === requester) - if (hasSelf) { - return await handleReadAfterWrite( - ctx, - requester, - res, - getProfilesMunge, - ) - } - return { - encoding: 'application/json', - body: res.data, - } + const res = await ctx.appviewAgent.api.app.bsky.actor.getProfiles( + params, + await ctx.serviceAuthHeaders(requester), + ) + const hasSelf = res.data.profiles.some((prof) => prof.did === requester) + if (hasSelf) { + return await handleReadAfterWrite(ctx, requester, res, getProfilesMunge) } - - const { actors } = params - const { db, services } = ctx - const actorService = services.appView.actor(db) - - const actorsRes = await actorService.getActors(actors) - return { encoding: 'application/json', - body: { - profiles: await actorService.views.hydrateProfilesDetailed( - actorsRes, - requester, - ), - }, + body: res.data, } }, }) diff --git a/packages/pds/src/app-view/api/app/bsky/actor/getSuggestions.ts b/packages/pds/src/app-view/api/app/bsky/actor/getSuggestions.ts index f80110dacb0..9047634dd30 100644 --- a/packages/pds/src/app-view/api/app/bsky/actor/getSuggestions.ts +++ b/packages/pds/src/app-view/api/app/bsky/actor/getSuggestions.ts @@ -1,5 +1,4 @@ import AppContext from '../../../../../context' -import { notSoftDeletedClause } from '../../../../../db/util' import { Server } from '../../../../../lexicon' export default function (server: Server, ctx: AppContext) { @@ -7,71 +6,13 @@ export default function (server: Server, ctx: AppContext) { auth: ctx.accessVerifier, handler: async ({ params, auth }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.actor.getSuggestions( - params, - await ctx.serviceAuthHeaders(requester), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - - const { limit, cursor } = params - - const db = ctx.db.db - const { services } = ctx - const { ref } = db.dynamic - - const graphService = ctx.services.appView.graph(ctx.db) - - let suggestionsQb = db - .selectFrom('suggested_follow') - .innerJoin('did_handle', 'suggested_follow.did', 'did_handle.did') - .innerJoin('repo_root', 'repo_root.did', 'did_handle.did') - .innerJoin('profile_agg', 'profile_agg.did', 'did_handle.did') - .where(notSoftDeletedClause(ref('repo_root'))) - .where('did_handle.did', '!=', requester) - .whereNotExists((qb) => - qb - .selectFrom('follow') - .selectAll() - .where('creator', '=', requester) - .whereRef('subjectDid', '=', ref('did_handle.did')), - ) - .whereNotExists( - graphService.blockQb(requester, [ref('did_handle.did')]), - ) - .selectAll('did_handle') - .select('profile_agg.postsCount as postsCount') - .limit(limit) - .orderBy('suggested_follow.order', 'asc') - - if (cursor) { - const cursorRow = await db - .selectFrom('suggested_follow') - .where('did', '=', cursor) - .selectAll() - .executeTakeFirst() - if (cursorRow) { - suggestionsQb = suggestionsQb.where( - 'suggested_follow.order', - '>', - cursorRow.order, - ) - } - } - - const suggestionsRes = await suggestionsQb.execute() + const res = await ctx.appviewAgent.api.app.bsky.actor.getSuggestions( + params, + await ctx.serviceAuthHeaders(requester), + ) return { encoding: 'application/json', - body: { - cursor: suggestionsRes.at(-1)?.did, - actors: await services.appView - .actor(ctx.db) - .views.hydrateProfiles(suggestionsRes, requester), - }, + body: res.data, } }, }) diff --git a/packages/pds/src/app-view/api/app/bsky/actor/searchActors.ts b/packages/pds/src/app-view/api/app/bsky/actor/searchActors.ts index 0c1e07c014a..ff88cd8d233 100644 --- a/packages/pds/src/app-view/api/app/bsky/actor/searchActors.ts +++ b/packages/pds/src/app-view/api/app/bsky/actor/searchActors.ts @@ -1,90 +1,19 @@ -import { sql } from 'kysely' import AppContext from '../../../../../context' -import Database from '../../../../../db' -import { DidHandle } from '../../../../../db/tables/did-handle' import { Server } from '../../../../../lexicon' -import * as Method from '../../../../../lexicon/types/app/bsky/actor/searchActors' -import { - cleanTerm, - getUserSearchQueryPg, - getUserSearchQuerySqlite, - SearchKeyset, -} from '../../../../../services/util/search' export default function (server: Server, ctx: AppContext) { server.app.bsky.actor.searchActors({ auth: ctx.accessVerifier, handler: async ({ auth, params }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.actor.searchActors( - params, - await ctx.serviceAuthHeaders(requester), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - - const { services, db } = ctx - let { term, limit } = params - const { cursor } = params - - term = cleanTerm(term || '') - limit = Math.min(limit ?? 25, 100) - - if (!term) { - return { - encoding: 'application/json', - body: { - actors: [], - }, - } - } - - const results = - db.dialect === 'pg' - ? await getResultsPg(db, { term, limit, cursor }) - : await getResultsSqlite(db, { term, limit, cursor }) - - const keyset = new SearchKeyset(sql``, sql``) - - const actors = await services.appView - .actor(db) - .views.hydrateProfiles(results, requester) - - const filtered = actors.filter( - (actor) => !actor.viewer?.blocking && !actor.viewer?.blockedBy, + const res = await ctx.appviewAgent.api.app.bsky.actor.searchActors( + params, + await ctx.serviceAuthHeaders(requester), ) - return { encoding: 'application/json', - body: { - cursor: keyset.packFromResult(results), - actors: filtered, - }, + body: res.data, } }, }) } - -const getResultsPg: GetResultsFn = async (db, { term, limit, cursor }) => { - return await getUserSearchQueryPg(db, { term: term || '', limit, cursor }) - .select('distance') - .selectAll('did_handle') - .execute() -} - -const getResultsSqlite: GetResultsFn = async (db, { term, limit, cursor }) => { - return await getUserSearchQuerySqlite(db, { term: term || '', limit, cursor }) - .leftJoin('profile', 'profile.creator', 'did_handle.did') - .select(sql`0`.as('distance')) - .selectAll('did_handle') - .execute() -} - -type GetResultsFn = ( - db: Database, - opts: Method.QueryParams & { limit: number }, -) => Promise<(DidHandle & { distance: number })[]> diff --git a/packages/pds/src/app-view/api/app/bsky/actor/searchActorsTypeahead.ts b/packages/pds/src/app-view/api/app/bsky/actor/searchActorsTypeahead.ts index bcc80d6acc3..5853cf3104e 100644 --- a/packages/pds/src/app-view/api/app/bsky/actor/searchActorsTypeahead.ts +++ b/packages/pds/src/app-view/api/app/bsky/actor/searchActorsTypeahead.ts @@ -1,83 +1,20 @@ import AppContext from '../../../../../context' -import Database from '../../../../../db' import { Server } from '../../../../../lexicon' -import * as Method from '../../../../../lexicon/types/app/bsky/actor/searchActorsTypeahead' -import { - cleanTerm, - getUserSearchQuerySimplePg, - getUserSearchQuerySqlite, -} from '../../../../../services/util/search' -import { DidHandle } from '../../../../../db/tables/did-handle' export default function (server: Server, ctx: AppContext) { server.app.bsky.actor.searchActorsTypeahead({ auth: ctx.accessVerifier, handler: async ({ params, auth }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const res = - await ctx.appviewAgent.api.app.bsky.actor.searchActorsTypeahead( - params, - await ctx.serviceAuthHeaders(requester), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - - const { services, db } = ctx - let { term, limit } = params - - term = cleanTerm(term || '') - limit = Math.min(limit ?? 25, 100) - - if (!term) { - return { - encoding: 'application/json', - body: { - actors: [], - }, - } - } - - const results = - ctx.db.dialect === 'pg' - ? await getResultsPg(ctx.db, { term, limit }) - : await getResultsSqlite(ctx.db, { term, limit }) - - const actors = await services.appView - .actor(db) - .views.hydrateProfilesBasic(results, requester) - - const filtered = actors.filter( - (actor) => !actor.viewer?.blocking && !actor.viewer?.blockedBy, - ) - + const res = + await ctx.appviewAgent.api.app.bsky.actor.searchActorsTypeahead( + params, + await ctx.serviceAuthHeaders(requester), + ) return { encoding: 'application/json', - body: { - actors: filtered, - }, + body: res.data, } }, }) } - -const getResultsPg: GetResultsFn = async (db, { term, limit }) => { - return await getUserSearchQuerySimplePg(db, { term: term || '', limit }) - .selectAll('did_handle') - .execute() -} - -const getResultsSqlite: GetResultsFn = async (db, { term, limit }) => { - return await getUserSearchQuerySqlite(db, { term: term || '', limit }) - .leftJoin('profile', 'profile.creator', 'did_handle.did') - .selectAll('did_handle') - .execute() -} - -type GetResultsFn = ( - db: Database, - opts: Method.QueryParams & { limit: number }, -) => Promise diff --git a/packages/pds/src/app-view/api/app/bsky/feed/getActorFeeds.ts b/packages/pds/src/app-view/api/app/bsky/feed/getActorFeeds.ts index f6ded6d2c0e..57d7d72b6e4 100644 --- a/packages/pds/src/app-view/api/app/bsky/feed/getActorFeeds.ts +++ b/packages/pds/src/app-view/api/app/bsky/feed/getActorFeeds.ts @@ -1,69 +1,18 @@ import { Server } from '../../../../../lexicon' import AppContext from '../../../../../context' -import { TimeCidKeyset, paginate } from '../../../../../db/pagination' -import { InvalidRequestError } from '@atproto/xrpc-server' export default function (server: Server, ctx: AppContext) { server.app.bsky.feed.getActorFeeds({ auth: ctx.accessVerifier, handler: async ({ auth, params }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.feed.getActorFeeds( - params, - await ctx.serviceAuthHeaders(requester), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - - const { actor, limit, cursor } = params - - const actorService = ctx.services.appView.actor(ctx.db) - const feedService = ctx.services.appView.feed(ctx.db) - - const creatorRes = await actorService.getActor(actor) - if (!creatorRes) { - throw new InvalidRequestError(`Actor not found: ${actor}`) - } - - const { ref } = ctx.db.db.dynamic - let feedsQb = feedService - .selectFeedGeneratorQb(requester) - .where('feed_generator.creator', '=', creatorRes.did) - - const keyset = new TimeCidKeyset( - ref('feed_generator.createdAt'), - ref('feed_generator.cid'), + const res = await ctx.appviewAgent.api.app.bsky.feed.getActorFeeds( + params, + await ctx.serviceAuthHeaders(requester), ) - feedsQb = paginate(feedsQb, { - limit, - cursor, - keyset, - }) - - const [feedsRes, creatorProfile] = await Promise.all([ - feedsQb.execute(), - actorService.views.profile(creatorRes, requester), - ]) - if (!creatorProfile) { - throw new InvalidRequestError(`Actor not found: ${actor}`) - } - - const profiles = { [creatorProfile.did]: creatorProfile } - - const feeds = feedsRes.map((row) => - feedService.views.formatFeedGeneratorView(row, profiles), - ) - return { encoding: 'application/json', - body: { - cursor: keyset.packFromResult(feedsRes), - feeds, - }, + body: res.data, } }, }) diff --git a/packages/pds/src/app-view/api/app/bsky/feed/getActorLikes.ts b/packages/pds/src/app-view/api/app/bsky/feed/getActorLikes.ts index 5e67e9c6dcf..6e56bc81214 100644 --- a/packages/pds/src/app-view/api/app/bsky/feed/getActorLikes.ts +++ b/packages/pds/src/app-view/api/app/bsky/feed/getActorLikes.ts @@ -1,9 +1,5 @@ -import { InvalidRequestError } from '@atproto/xrpc-server' import { Server } from '../../../../../lexicon' -import { FeedKeyset } from '../util/feed' -import { paginate } from '../../../../../db/pagination' import AppContext from '../../../../../context' -import { FeedRow } from '../../../../services/feed' import { OutputSchema } from '../../../../../lexicon/types/app/bsky/feed/getAuthorFeed' import { handleReadAfterWrite } from '../util/read-after-write' import { authPassthru } from '../../../../../api/com/atproto/admin/util' @@ -16,75 +12,16 @@ export default function (server: Server, ctx: AppContext) { const requester = auth.credentials.type === 'access' ? auth.credentials.did : null - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.feed.getActorLikes( - params, - requester - ? await ctx.serviceAuthHeaders(requester) - : authPassthru(req), - ) - if (requester) { - return await handleReadAfterWrite(ctx, requester, res, getAuthorMunge) - } - return { - encoding: 'application/json', - body: res.data, - } - } - - const { actor, limit, cursor } = params - - const { ref } = ctx.db.db.dynamic - const actorService = ctx.services.appView.actor(ctx.db) - const feedService = ctx.services.appView.feed(ctx.db) - const graphService = ctx.services.appView.graph(ctx.db) - - // maybe resolve did first - const actorRes = await actorService.getActor(actor) - if (!actorRes) { - throw new InvalidRequestError('Profile not found') - } - const actorDid = actorRes.did - - if (!requester || requester !== actorDid) { - throw new InvalidRequestError('Profile not found') - } - - // defaults to posts, reposts, and replies - let feedItemsQb = feedService - .selectFeedItemQb() - .innerJoin('like', 'like.subject', 'feed_item.uri') - .where('like.creator', '=', actorDid) - - // for access-based auth, enforce blocks + const res = await ctx.appviewAgent.api.app.bsky.feed.getActorLikes( + params, + requester ? await ctx.serviceAuthHeaders(requester) : authPassthru(req), + ) if (requester) { - feedItemsQb = feedItemsQb.whereNotExists( - graphService.blockQb(requester, [ref('post.creator')]), - ) + return await handleReadAfterWrite(ctx, requester, res, getAuthorMunge) } - - const keyset = new FeedKeyset( - ref('feed_item.sortAt'), - ref('feed_item.cid'), - ) - - feedItemsQb = paginate(feedItemsQb, { - limit, - cursor, - keyset, - }) - - const feedItems: FeedRow[] = await feedItemsQb.execute() - const feed = await feedService.hydrateFeed(feedItems, requester, { - includeSoftDeleted: auth.credentials.type === 'role', // show takendown content to mods - }) - return { encoding: 'application/json', - body: { - feed, - cursor: keyset.packFromResult(feedItems), - }, + body: res.data, } }, }) diff --git a/packages/pds/src/app-view/api/app/bsky/feed/getAuthorFeed.ts b/packages/pds/src/app-view/api/app/bsky/feed/getAuthorFeed.ts index 7850092dc28..735004f52e3 100644 --- a/packages/pds/src/app-view/api/app/bsky/feed/getAuthorFeed.ts +++ b/packages/pds/src/app-view/api/app/bsky/feed/getAuthorFeed.ts @@ -1,9 +1,5 @@ -import { InvalidRequestError } from '@atproto/xrpc-server' import { Server } from '../../../../../lexicon' -import { FeedKeyset } from '../util/feed' -import { paginate } from '../../../../../db/pagination' import AppContext from '../../../../../context' -import { FeedRow } from '../../../../services/feed' import { OutputSchema } from '../../../../../lexicon/types/app/bsky/feed/getAuthorFeed' import { handleReadAfterWrite } from '../util/read-after-write' import { authPassthru } from '../../../../../api/com/atproto/admin/util' @@ -16,130 +12,21 @@ export default function (server: Server, ctx: AppContext) { handler: async ({ req, params, auth }) => { const requester = auth.credentials.type === 'access' ? auth.credentials.did : null - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.feed.getAuthorFeed( - params, - requester - ? await ctx.serviceAuthHeaders(requester) - : authPassthru(req), - ) - if (requester) { - return await handleReadAfterWrite(ctx, requester, res, getAuthorMunge) - } - return { - encoding: 'application/json', - body: res.data, - } - } - - const { actor, limit, cursor } = params - - const { ref } = ctx.db.db.dynamic - const accountService = ctx.services.account(ctx.db) - const actorService = ctx.services.appView.actor(ctx.db) - const feedService = ctx.services.appView.feed(ctx.db) - const graphService = ctx.services.appView.graph(ctx.db) - - // maybe resolve did first - const actorRes = await actorService.getActor(actor) - if (!actorRes) { - throw new InvalidRequestError('Profile not found') - } - const actorDid = actorRes.did - - // defaults to posts, reposts, and replies - let feedItemsQb = feedService - .selectFeedItemQb() - .where('originatorDid', '=', actorDid) - - if (params.filter === 'posts_with_media') { - feedItemsQb = feedItemsQb - // and only your own posts/reposts - .where('post.creator', '=', actorDid) - // only posts with media - .whereExists((qb) => - qb - .selectFrom('post_embed_image') - .select('post_embed_image.postUri') - .whereRef('post_embed_image.postUri', '=', 'feed_item.postUri'), - ) - } else if (params.filter === 'posts_no_replies') { - feedItemsQb = feedItemsQb - // only posts, no replies - .where((qb) => - qb - .where('post.replyParent', 'is', null) - .orWhere('type', '=', 'repost'), - ) - } - - // for access-based auth, enforce blocks and mutes + const res = await ctx.appviewAgent.api.app.bsky.feed.getAuthorFeed( + params, + requester ? await ctx.serviceAuthHeaders(requester) : authPassthru(req), + ) if (requester) { - await assertNoBlocks(ctx, { requester, actor }) - feedItemsQb = feedItemsQb - .where((qb) => - // hide reposts of muted content - qb - .where('type', '=', 'post') - .orWhere((qb) => - accountService.whereNotMuted(qb, requester, [ - ref('post.creator'), - ]), - ), - ) - .whereNotExists( - graphService.blockQb(requester, [ref('post.creator')]), - ) + return await handleReadAfterWrite(ctx, requester, res, getAuthorMunge) } - - const keyset = new FeedKeyset( - ref('feed_item.sortAt'), - ref('feed_item.cid'), - ) - - feedItemsQb = paginate(feedItemsQb, { - limit, - cursor, - keyset, - }) - - const feedItems: FeedRow[] = await feedItemsQb.execute() - const feed = await feedService.hydrateFeed(feedItems, requester, { - includeSoftDeleted: auth.credentials.type === 'role', // show takendown content to mods - }) - return { encoding: 'application/json', - body: { - feed, - cursor: keyset.packFromResult(feedItems), - }, + body: res.data, } }, }) } -// throws when there's a block between the two users -async function assertNoBlocks( - ctx: AppContext, - opts: { requester: string; actor: string }, -) { - const { requester, actor } = opts - const graphService = ctx.services.appView.graph(ctx.db) - const blocks = await graphService.getBlocks(requester, actor) - if (blocks.blocking) { - throw new InvalidRequestError( - `Requester has blocked actor: ${actor}`, - 'BlockedActor', - ) - } else if (blocks.blockedBy) { - throw new InvalidRequestError( - `Requester is blocked by actor: $${actor}`, - 'BlockedByActor', - ) - } -} - const getAuthorMunge = async ( ctx: AppContext, original: OutputSchema, diff --git a/packages/pds/src/app-view/api/app/bsky/feed/getFeed.ts b/packages/pds/src/app-view/api/app/bsky/feed/getFeed.ts index 5f9eb9d975c..a5cf4176d4e 100644 --- a/packages/pds/src/app-view/api/app/bsky/feed/getFeed.ts +++ b/packages/pds/src/app-view/api/app/bsky/feed/getFeed.ts @@ -1,217 +1,25 @@ -import { - InvalidRequestError, - UpstreamFailureError, - createServiceAuthHeaders, - ServerTimer, - serverTimingHeader, -} from '@atproto/xrpc-server' -import { ResponseType, XRPCError } from '@atproto/xrpc' -import { getFeedGen } from '@atproto/identity' -import { AtpAgent, AppBskyFeedGetFeedSkeleton } from '@atproto/api' -import { SkeletonFeedPost } from '../../../../../lexicon/types/app/bsky/feed/defs' -import { QueryParams as GetFeedParams } from '../../../../../lexicon/types/app/bsky/feed/getFeed' -import { OutputSchema as SkeletonOutput } from '../../../../../lexicon/types/app/bsky/feed/getFeedSkeleton' import { Server } from '../../../../../lexicon' import AppContext from '../../../../../context' -import { FeedRow } from '../../../../services/feed' -import { AlgoResponse } from '../../../../../feed-gen/types' -import { getDidDoc } from '../util/resolver' export default function (server: Server, ctx: AppContext) { - const isProxyableFeed = (feed: string): boolean => { - return feed in ctx.algos - } - server.app.bsky.feed.getFeed({ auth: ctx.accessVerifier, handler: async ({ params, auth }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const { data: feed } = - await ctx.appviewAgent.api.app.bsky.feed.getFeedGenerator( - { feed: params.feed }, - await ctx.serviceAuthHeaders(requester), - ) - const res = await ctx.appviewAgent.api.app.bsky.feed.getFeed( - params, - await ctx.serviceAuthHeaders(requester, feed.view.did), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - let algoRes: AlgoResponse - const timerSkele = new ServerTimer('skele').start() - - if (ctx.cfg.bskyAppViewEndpoint && isProxyableFeed(params.feed)) { - // this is a temporary solution to smart proxy bsky feeds to the appview - const res = await ctx.appviewAgent.api.app.bsky.feed.getFeedSkeleton( - params, + const { data: feed } = + await ctx.appviewAgent.api.app.bsky.feed.getFeedGenerator( + { feed: params.feed }, await ctx.serviceAuthHeaders(requester), ) - algoRes = await filterMutesAndBlocks( - ctx, - res.data, - params.limit, - requester, - ) - } else { - algoRes = await skeletonFromFeedGen(ctx, params, requester) - } - - timerSkele.stop() - - const feedService = ctx.services.appView.feed(ctx.db) - const { feedItems, ...rest } = algoRes - - const timerHydr = new ServerTimer('hydr').start() - const hydrated = await feedService.hydrateFeed(feedItems, requester) - timerHydr.stop() - + const res = await ctx.appviewAgent.api.app.bsky.feed.getFeed( + params, + await ctx.serviceAuthHeaders(requester, feed.view.did), + ) return { encoding: 'application/json', - body: { - ...rest, - feed: hydrated, - }, - headers: { - 'server-timing': serverTimingHeader([timerSkele, timerHydr]), - }, + body: res.data, } }, }) } - -async function skeletonFromFeedGen( - ctx: AppContext, - params: GetFeedParams, - requester: string, -): Promise { - const { feed } = params - // Resolve and fetch feed skeleton - const found = await ctx.db.db - .selectFrom('feed_generator') - .where('uri', '=', feed) - .select('feedDid') - .executeTakeFirst() - if (!found) { - throw new InvalidRequestError('could not find feed') - } - const feedDid = found.feedDid - - const doc = await getDidDoc(ctx, feedDid) - const fgEndpoint = getFeedGen(doc) - if (!fgEndpoint) { - throw new InvalidRequestError( - `invalid feed generator service details in did document: ${feedDid}`, - ) - } - - const agent = new AtpAgent({ service: fgEndpoint }) - const headers = await createServiceAuthHeaders({ - iss: requester, - aud: feedDid, - keypair: ctx.repoSigningKey, - }) - - let skeleton: SkeletonOutput - try { - const result = await agent.api.app.bsky.feed.getFeedSkeleton( - params, - headers, - ) - skeleton = result.data - } catch (err) { - if (err instanceof AppBskyFeedGetFeedSkeleton.UnknownFeedError) { - throw new InvalidRequestError(err.message, 'UnknownFeed') - } - if (err instanceof XRPCError) { - if (err.status === ResponseType.Unknown) { - throw new UpstreamFailureError('feed unavailable') - } - if (err.status === ResponseType.InvalidResponse) { - throw new UpstreamFailureError( - 'feed provided an invalid response', - 'InvalidFeedResponse', - ) - } - } - throw err - } - - return filterMutesAndBlocks(ctx, skeleton, params.limit, requester) -} - -export async function filterMutesAndBlocks( - ctx: AppContext, - skeleton: SkeletonOutput, - limit: number, - requester: string, -) { - const { feed: skeletonFeed, ...rest } = skeleton - - const { ref } = ctx.db.db.dynamic - const feedService = ctx.services.appView.feed(ctx.db) - const graphService = ctx.services.appView.graph(ctx.db) - const accountService = ctx.services.account(ctx.db) - const feedItemUris = skeletonFeed.map(getSkeleFeedItemUri) - - const feedItems = feedItemUris.length - ? await feedService - .selectFeedItemQb() - .where('feed_item.uri', 'in', feedItemUris) - .where((qb) => - // Hide posts and reposts of or by muted actors - accountService.whereNotMuted(qb, requester, [ - ref('post.creator'), - ref('originatorDid'), - ]), - ) - .whereNotExists( - graphService.blockQb(requester, [ - ref('post.creator'), - ref('originatorDid'), - ]), - ) - .execute() - : [] - - const orderedItems = getOrderedFeedItems(skeletonFeed, feedItems, limit) - return { - ...rest, - feedItems: orderedItems, - } -} - -function getSkeleFeedItemUri(item: SkeletonFeedPost) { - if (typeof item.reason?.repost === 'string') { - return item.reason.repost - } - return item.post -} - -function getOrderedFeedItems( - skeletonItems: SkeletonFeedPost[], - feedItems: FeedRow[], - limit: number, -) { - const SKIP = [] - const feedItemsByUri = feedItems.reduce((acc, item) => { - return Object.assign(acc, { [item.uri]: item }) - }, {} as Record) - // enforce limit param in the case that the feedgen does not - if (skeletonItems.length > limit) { - skeletonItems = skeletonItems.slice(0, limit) - } - return skeletonItems.flatMap((item) => { - const uri = getSkeleFeedItemUri(item) - const feedItem = feedItemsByUri[uri] - if (!feedItem || item.post !== feedItem.postUri) { - // Couldn't find the record, or skeleton repost referenced the wrong post - return SKIP - } - return feedItem - }) -} diff --git a/packages/pds/src/app-view/api/app/bsky/feed/getFeedGenerator.ts b/packages/pds/src/app-view/api/app/bsky/feed/getFeedGenerator.ts index 66e0cc8fdc5..210b1be54ef 100644 --- a/packages/pds/src/app-view/api/app/bsky/feed/getFeedGenerator.ts +++ b/packages/pds/src/app-view/api/app/bsky/feed/getFeedGenerator.ts @@ -1,9 +1,3 @@ -import { InvalidRequestError } from '@atproto/xrpc-server' -import { - DidDocument, - PoorlyFormattedDidDocumentError, - getFeedGen, -} from '@atproto/identity' import { Server } from '../../../../../lexicon' import AppContext from '../../../../../context' @@ -12,67 +6,13 @@ export default function (server: Server, ctx: AppContext) { auth: ctx.accessVerifier, handler: async ({ params, auth }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.feed.getFeedGenerator( - params, - await ctx.serviceAuthHeaders(requester), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - - const { feed } = params - - const feedService = ctx.services.appView.feed(ctx.db) - - const got = await feedService.getFeedGeneratorInfos([feed], requester) - const feedInfo = got[feed] - if (!feedInfo) { - throw new InvalidRequestError('could not find feed') - } - - const feedDid = feedInfo.feedDid - let resolved: DidDocument | null - try { - resolved = await ctx.idResolver.did.resolve(feedDid) - } catch (err) { - if (err instanceof PoorlyFormattedDidDocumentError) { - throw new InvalidRequestError(`invalid did document: ${feedDid}`) - } - throw err - } - if (!resolved) { - throw new InvalidRequestError( - `could not resolve did document: ${feedDid}`, - ) - } - - const fgEndpoint = getFeedGen(resolved) - if (!fgEndpoint) { - throw new InvalidRequestError( - `invalid feed generator service details in did document: ${feedDid}`, - ) - } - - const profiles = await feedService.getActorInfos( - [feedInfo.creator], - requester, - ) - const feedView = feedService.views.formatFeedGeneratorView( - feedInfo, - profiles, + const res = await ctx.appviewAgent.api.app.bsky.feed.getFeedGenerator( + params, + await ctx.serviceAuthHeaders(requester), ) - return { encoding: 'application/json', - body: { - view: feedView, - // @TODO temporarily hard-coding to true while external feedgens catch-up on describeFeedGenerator - isOnline: true, - isValid: true, - }, + body: res.data, } }, }) diff --git a/packages/pds/src/app-view/api/app/bsky/feed/getFeedGenerators.ts b/packages/pds/src/app-view/api/app/bsky/feed/getFeedGenerators.ts index 3cf7f8eef11..36353f015b7 100644 --- a/packages/pds/src/app-view/api/app/bsky/feed/getFeedGenerators.ts +++ b/packages/pds/src/app-view/api/app/bsky/feed/getFeedGenerators.ts @@ -6,36 +6,13 @@ export default function (server: Server, ctx: AppContext) { auth: ctx.accessVerifier, handler: async ({ params, auth }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.feed.getFeedGenerators( - params, - await ctx.serviceAuthHeaders(requester), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - - const { feeds } = params - - const feedService = ctx.services.appView.feed(ctx.db) - - const genInfos = await feedService.getFeedGeneratorInfos(feeds, requester) - const genList = Object.values(genInfos) - - const creators = genList.map((gen) => gen.creator) - const profiles = await feedService.getActorInfos(creators, requester) - - const feedViews = genList.map((gen) => - feedService.views.formatFeedGeneratorView(gen, profiles), + const res = await ctx.appviewAgent.api.app.bsky.feed.getFeedGenerators( + params, + await ctx.serviceAuthHeaders(requester), ) - return { encoding: 'application/json', - body: { - feeds: feedViews, - }, + body: res.data, } }, }) diff --git a/packages/pds/src/app-view/api/app/bsky/feed/getLikes.ts b/packages/pds/src/app-view/api/app/bsky/feed/getLikes.ts index e6658863bed..a78469355d5 100644 --- a/packages/pds/src/app-view/api/app/bsky/feed/getLikes.ts +++ b/packages/pds/src/app-view/api/app/bsky/feed/getLikes.ts @@ -1,83 +1,18 @@ -import { mapDefined } from '@atproto/common' import { Server } from '../../../../../lexicon' -import { paginate, TimeCidKeyset } from '../../../../../db/pagination' import AppContext from '../../../../../context' -import { notSoftDeletedClause } from '../../../../../db/util' export default function (server: Server, ctx: AppContext) { server.app.bsky.feed.getLikes({ auth: ctx.accessVerifier, handler: async ({ params, auth }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.feed.getLikes( - params, - await ctx.serviceAuthHeaders(requester), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - - const { uri, limit, cursor, cid } = params - const { services, db } = ctx - const { ref } = db.db.dynamic - - const graphService = ctx.services.appView.graph(ctx.db) - - let builder = db.db - .selectFrom('like') - .where('like.subject', '=', uri) - .innerJoin('did_handle as creator', 'creator.did', 'like.creator') - .innerJoin( - 'repo_root as creator_repo', - 'creator_repo.did', - 'like.creator', - ) - .where(notSoftDeletedClause(ref('creator_repo'))) - .whereNotExists(graphService.blockQb(requester, [ref('like.creator')])) - .selectAll('creator') - .select([ - 'like.cid as cid', - 'like.createdAt as createdAt', - 'like.indexedAt as indexedAt', - ]) - - if (cid) { - builder = builder.where('like.subjectCid', '=', cid) - } - - const keyset = new TimeCidKeyset(ref('like.createdAt'), ref('like.cid')) - builder = paginate(builder, { - limit, - cursor, - keyset, - }) - - const likesRes = await builder.execute() - const actors = await services.appView - .actor(db) - .views.profiles(likesRes, requester) - - const likes = mapDefined(likesRes, (row) => - actors[row.did] - ? { - createdAt: row.createdAt, - indexedAt: row.indexedAt, - actor: actors[row.did], - } - : undefined, + const res = await ctx.appviewAgent.api.app.bsky.feed.getLikes( + params, + await ctx.serviceAuthHeaders(requester), ) - return { encoding: 'application/json', - body: { - uri, - cid, - cursor: keyset.packFromResult(likesRes), - likes, - }, + body: res.data, } }, }) diff --git a/packages/pds/src/app-view/api/app/bsky/feed/getPostThread.ts b/packages/pds/src/app-view/api/app/bsky/feed/getPostThread.ts index fb5276f2452..9b86df3dc5e 100644 --- a/packages/pds/src/app-view/api/app/bsky/feed/getPostThread.ts +++ b/packages/pds/src/app-view/api/app/bsky/feed/getPostThread.ts @@ -1,27 +1,11 @@ import { AtUri } from '@atproto/syntax' import { AppBskyFeedGetPostThread } from '@atproto/api' import { Headers } from '@atproto/xrpc' -import { InvalidRequestError } from '@atproto/xrpc-server' import { Server } from '../../../../../lexicon' import AppContext from '../../../../../context' +import { FeedRow } from '../../../../services/feed' import { - ActorInfoMap, - PostEmbedViews, - FeedRow, - FeedService, - PostInfoMap, - PostBlocksMap, -} from '../../../../services/feed' -import { - getAncestorsAndSelfQb, - getDescendentsQb, -} from '../../../../services/feed/util' -import { Labels } from '../../../../services/label' -import { - BlockedPost, - NotFoundPost, ThreadViewPost, - isNotFoundPost, isThreadViewPost, } from '../../../../../lexicon/types/app/bsky/feed/defs' import { Record as PostRecord } from '../../../../../lexicon/types/app/bsky/feed/post' @@ -51,277 +35,46 @@ export default function (server: Server, ctx: AppContext) { auth: ctx.accessVerifier, handler: async ({ params, auth }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - try { - const res = await ctx.appviewAgent.api.app.bsky.feed.getPostThread( - params, - await ctx.serviceAuthHeaders(requester), - ) - return await handleReadAfterWrite( + try { + const res = await ctx.appviewAgent.api.app.bsky.feed.getPostThread( + params, + await ctx.serviceAuthHeaders(requester), + ) + return await handleReadAfterWrite( + ctx, + requester, + res, + getPostThreadMunge, + ) + } catch (err) { + if (err instanceof AppBskyFeedGetPostThread.NotFoundError) { + const local = await readAfterWriteNotFound( ctx, + params, requester, - res, - getPostThreadMunge, + err.headers, ) - } catch (err) { - if (err instanceof AppBskyFeedGetPostThread.NotFoundError) { - const local = await readAfterWriteNotFound( - ctx, - params, - requester, - err.headers, - ) - if (local === null) { - throw err - } else { - return { - encoding: 'application/json', - body: local.data, - headers: local.lag - ? { - 'Atproto-Upstream-Lag': local.lag.toString(10), - } - : undefined, - } - } - } else { + if (local === null) { throw err + } else { + return { + encoding: 'application/json', + body: local.data, + headers: local.lag + ? { + 'Atproto-Upstream-Lag': local.lag.toString(10), + } + : undefined, + } } + } else { + throw err } } - - const { uri, depth, parentHeight } = params - - const feedService = ctx.services.appView.feed(ctx.db) - const labelService = ctx.services.appView.label(ctx.db) - - const threadData = await getThreadData(ctx, uri, depth, parentHeight) - if (!threadData) { - throw new InvalidRequestError(`Post not found: ${uri}`, 'NotFound') - } - const relevant = getRelevantIds(threadData) - const [actors, posts, labels] = await Promise.all([ - feedService.getActorInfos(Array.from(relevant.dids), requester, { - skipLabels: true, - }), - feedService.getPostInfos(Array.from(relevant.uris), requester), - labelService.getLabelsForSubjects([...relevant.uris, ...relevant.dids]), - ]) - const blocks = await feedService.blocksForPosts(posts) - const embeds = await feedService.embedsForPosts(posts, blocks, requester) - - const thread = composeThread( - threadData, - feedService, - posts, - actors, - embeds, - blocks, - labels, - ) - - if (isNotFoundPost(thread)) { - // @TODO technically this could be returned as a NotFoundPost based on lexicon - throw new InvalidRequestError(`Post not found: ${uri}`, 'NotFound') - } - - return { - encoding: 'application/json', - body: { thread }, - } }, }) } -const composeThread = ( - threadData: PostThread, - feedService: FeedService, - posts: PostInfoMap, - actors: ActorInfoMap, - embeds: PostEmbedViews, - blocks: PostBlocksMap, - labels: Labels, -): ThreadViewPost | NotFoundPost | BlockedPost => { - const post = feedService.views.formatPostView( - threadData.post.postUri, - actors, - posts, - embeds, - labels, - ) - - if (!post || blocks[post.uri]?.reply) { - return { - $type: 'app.bsky.feed.defs#notFoundPost', - uri: threadData.post.postUri, - notFound: true, - } - } - - if (post.author.viewer?.blocking || post.author.viewer?.blockedBy) { - return { - $type: 'app.bsky.feed.defs#blockedPost', - uri: threadData.post.postUri, - blocked: true, - author: { - did: post.author.did, - viewer: post.author.viewer - ? { - blockedBy: post.author.viewer?.blockedBy, - blocking: post.author.viewer?.blocking, - } - : undefined, - }, - } - } - - let parent: ThreadViewPost | NotFoundPost | BlockedPost | undefined - if (threadData.parent) { - if (threadData.parent instanceof ParentNotFoundError) { - parent = { - $type: 'app.bsky.feed.defs#notFoundPost', - uri: threadData.parent.uri, - notFound: true, - } - } else { - parent = composeThread( - threadData.parent, - feedService, - posts, - actors, - embeds, - blocks, - labels, - ) - } - } - - let replies: (ThreadViewPost | NotFoundPost | BlockedPost)[] | undefined - if (threadData.replies) { - replies = threadData.replies.flatMap((reply) => { - const thread = composeThread( - reply, - feedService, - posts, - actors, - embeds, - blocks, - labels, - ) - // e.g. don't bother including #postNotFound reply placeholders for takedowns. either way matches api contract. - const skip = [] - return isNotFoundPost(thread) ? skip : thread - }) - } - - return { - $type: 'app.bsky.feed.defs#threadViewPost', - post, - parent, - replies, - } -} - -const getRelevantIds = ( - thread: PostThread, -): { dids: Set; uris: Set } => { - const dids = new Set() - const uris = new Set() - if (thread.parent && !(thread.parent instanceof ParentNotFoundError)) { - const fromParent = getRelevantIds(thread.parent) - fromParent.dids.forEach((did) => dids.add(did)) - fromParent.uris.forEach((uri) => uris.add(uri)) - } - if (thread.replies) { - for (const reply of thread.replies) { - const fromChild = getRelevantIds(reply) - fromChild.dids.forEach((did) => dids.add(did)) - fromChild.uris.forEach((uri) => uris.add(uri)) - } - } - dids.add(thread.post.postAuthorDid) - uris.add(thread.post.postUri) - return { dids, uris } -} - -const getThreadData = async ( - ctx: AppContext, - uri: string, - depth: number, - parentHeight: number, -): Promise => { - const feedService = ctx.services.appView.feed(ctx.db) - const [parents, children] = await Promise.all([ - getAncestorsAndSelfQb(ctx.db.db, { uri, parentHeight }) - .selectFrom('ancestor') - .innerJoin( - feedService.selectPostQb().as('post'), - 'post.uri', - 'ancestor.uri', - ) - .selectAll('post') - .execute(), - getDescendentsQb(ctx.db.db, { uri, depth }) - .selectFrom('descendent') - .innerJoin( - feedService.selectPostQb().as('post'), - 'post.uri', - 'descendent.uri', - ) - .selectAll('post') - .orderBy('sortAt', 'desc') - .execute(), - ]) - const parentsByUri = parents.reduce((acc, parent) => { - return Object.assign(acc, { [parent.postUri]: parent }) - }, {} as Record) - const childrenByParentUri = children.reduce((acc, child) => { - if (!child.replyParent) return acc - acc[child.replyParent] ??= [] - acc[child.replyParent].push(child) - return acc - }, {} as Record) - const post = parentsByUri[uri] - if (!post) return null - return { - post, - parent: post.replyParent - ? getParentData(parentsByUri, post.replyParent, parentHeight) - : undefined, - replies: getChildrenData(childrenByParentUri, uri, depth), - } -} - -const getParentData = ( - postsByUri: Record, - uri: string, - depth: number, -): PostThread | ParentNotFoundError | undefined => { - if (depth === 0) return undefined - const post = postsByUri[uri] - if (!post) return new ParentNotFoundError(uri) - return { - post, - parent: post.replyParent - ? getParentData(postsByUri, post.replyParent, depth - 1) - : undefined, - replies: [], - } -} - -const getChildrenData = ( - childrenByParentUri: Record, - uri: string, - depth: number, -): PostThread[] | undefined => { - if (depth === 0) return undefined - const children = childrenByParentUri[uri] ?? [] - return children.map((row) => ({ - post: row, - replies: getChildrenData(childrenByParentUri, row.postUri, depth - 1), - })) -} - class ParentNotFoundError extends Error { constructor(public uri: string) { super(`Parent not found: ${uri}`) diff --git a/packages/pds/src/app-view/api/app/bsky/feed/getPosts.ts b/packages/pds/src/app-view/api/app/bsky/feed/getPosts.ts index 25bc3d652e4..f394ae57a08 100644 --- a/packages/pds/src/app-view/api/app/bsky/feed/getPosts.ts +++ b/packages/pds/src/app-view/api/app/bsky/feed/getPosts.ts @@ -1,45 +1,18 @@ -import * as common from '@atproto/common' import { Server } from '../../../../../lexicon' import AppContext from '../../../../../context' -import { PostView } from '../../../../../lexicon/types/app/bsky/feed/defs' export default function (server: Server, ctx: AppContext) { server.app.bsky.feed.getPosts({ auth: ctx.accessVerifier, handler: async ({ params, auth }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.feed.getPosts( - params, - await ctx.serviceAuthHeaders(requester), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - - const uris = common.dedupeStrs(params.uris) - - const postViews = await ctx.services.appView - .feed(ctx.db) - .getPostViews(uris, requester) - - const posts: PostView[] = [] - for (const uri of uris) { - const post = postViews[uri] - const isBlocked = - post?.author.viewer?.blockedBy === true || - typeof post?.author.viewer?.blocking === 'string' - - if (post && !isBlocked) { - posts.push(post) - } - } - + const res = await ctx.appviewAgent.api.app.bsky.feed.getPosts( + params, + await ctx.serviceAuthHeaders(requester), + ) return { encoding: 'application/json', - body: { posts }, + body: res.data, } }, }) diff --git a/packages/pds/src/app-view/api/app/bsky/feed/getRepostedBy.ts b/packages/pds/src/app-view/api/app/bsky/feed/getRepostedBy.ts index f00f9a1a2b5..9704597ad27 100644 --- a/packages/pds/src/app-view/api/app/bsky/feed/getRepostedBy.ts +++ b/packages/pds/src/app-view/api/app/bsky/feed/getRepostedBy.ts @@ -1,73 +1,18 @@ import { Server } from '../../../../../lexicon' -import { paginate, TimeCidKeyset } from '../../../../../db/pagination' import AppContext from '../../../../../context' -import { notSoftDeletedClause } from '../../../../../db/util' export default function (server: Server, ctx: AppContext) { server.app.bsky.feed.getRepostedBy({ auth: ctx.accessVerifier, handler: async ({ params, auth }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.feed.getRepostedBy( - params, - await ctx.serviceAuthHeaders(requester), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - - const { uri, limit, cursor, cid } = params - const { services, db } = ctx - const { ref } = db.db.dynamic - - const graphService = ctx.services.appView.graph(ctx.db) - - let builder = db.db - .selectFrom('repost') - .where('repost.subject', '=', uri) - .innerJoin('did_handle as creator', 'creator.did', 'repost.creator') - .innerJoin( - 'repo_root as creator_repo', - 'creator_repo.did', - 'repost.creator', - ) - .where(notSoftDeletedClause(ref('creator_repo'))) - .whereNotExists( - graphService.blockQb(requester, [ref('repost.creator')]), - ) - .selectAll('creator') - .select(['repost.cid as cid', 'repost.createdAt as createdAt']) - - if (cid) { - builder = builder.where('repost.subjectCid', '=', cid) - } - - const keyset = new TimeCidKeyset( - ref('repost.createdAt'), - ref('repost.cid'), + const res = await ctx.appviewAgent.api.app.bsky.feed.getRepostedBy( + params, + await ctx.serviceAuthHeaders(requester), ) - builder = paginate(builder, { - limit, - cursor, - keyset, - }) - - const repostedByRes = await builder.execute() - const repostedBy = await services.appView - .actor(db) - .views.hydrateProfiles(repostedByRes, requester) - return { encoding: 'application/json', - body: { - uri, - cid, - repostedBy, - cursor: keyset.packFromResult(repostedByRes), - }, + body: res.data, } }, }) diff --git a/packages/pds/src/app-view/api/app/bsky/feed/getTimeline.ts b/packages/pds/src/app-view/api/app/bsky/feed/getTimeline.ts index 5b1e29168a1..08676b3a943 100644 --- a/packages/pds/src/app-view/api/app/bsky/feed/getTimeline.ts +++ b/packages/pds/src/app-view/api/app/bsky/feed/getTimeline.ts @@ -1,10 +1,5 @@ -import { InvalidRequestError } from '@atproto/xrpc-server' import { Server } from '../../../../../lexicon' -import { FeedAlgorithm, FeedKeyset, getFeedDateThreshold } from '../util/feed' -import { paginate } from '../../../../../db/pagination' import AppContext from '../../../../../context' -import { FeedRow } from '../../../../services/feed' -import { filterMutesAndBlocks } from './getFeed' import { OutputSchema } from '../../../../../lexicon/types/app/bsky/feed/getTimeline' import { handleReadAfterWrite } from '../util/read-after-write' import { LocalRecords } from '../../../../../services/local' @@ -14,100 +9,11 @@ export default function (server: Server, ctx: AppContext) { auth: ctx.accessVerifier, handler: async ({ params, auth }) => { const requester = auth.credentials.did - const { algorithm, limit, cursor } = params - if (algorithm && algorithm !== FeedAlgorithm.ReverseChronological) { - throw new InvalidRequestError(`Unsupported algorithm: ${algorithm}`) - } - - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.feed.getTimeline( - params, - await ctx.serviceAuthHeaders(requester), - ) - return await handleReadAfterWrite(ctx, requester, res, getTimelineMunge) - } - - if (ctx.cfg.bskyAppViewEndpoint) { - const res = - await ctx.appviewAgent.api.app.bsky.unspecced.getTimelineSkeleton( - { limit, cursor }, - await ctx.serviceAuthHeaders(requester), - ) - const filtered = await filterMutesAndBlocks( - ctx, - res.data, - limit, - requester, - ) - const hydrated = await ctx.services.appView - .feed(ctx.db) - .hydrateFeed(filtered.feedItems, requester) - return { - encoding: 'application/json', - body: { - cursor: filtered.cursor, - feed: hydrated, - }, - } - } - - const db = ctx.db.db - const { ref } = db.dynamic - - const accountService = ctx.services.account(ctx.db) - const feedService = ctx.services.appView.feed(ctx.db) - const graphService = ctx.services.appView.graph(ctx.db) - - const followingIdsSubquery = db - .selectFrom('follow') - .select('follow.subjectDid') - .where('follow.creator', '=', requester) - - const keyset = new FeedKeyset( - ref('feed_item.sortAt'), - ref('feed_item.cid'), + const res = await ctx.appviewAgent.api.app.bsky.feed.getTimeline( + params, + await ctx.serviceAuthHeaders(requester), ) - const sortFrom = keyset.unpack(cursor)?.primary - - let feedItemsQb = feedService - .selectFeedItemQb() - .where((qb) => - qb - .where('originatorDid', '=', requester) - .orWhere('originatorDid', 'in', followingIdsSubquery), - ) - .where((qb) => - // Hide posts and reposts of or by muted actors - accountService.whereNotMuted(qb, requester, [ - ref('post.creator'), - ref('originatorDid'), - ]), - ) - .whereNotExists( - graphService.blockQb(requester, [ - ref('post.creator'), - ref('originatorDid'), - ]), - ) - .where('feed_item.sortAt', '>', getFeedDateThreshold(sortFrom)) - - feedItemsQb = paginate(feedItemsQb, { - limit, - cursor, - keyset, - tryIndex: true, - }) - - const feedItems: FeedRow[] = await feedItemsQb.execute() - const feed = await feedService.hydrateFeed(feedItems, requester) - - return { - encoding: 'application/json', - body: { - feed, - cursor: keyset.packFromResult(feedItems), - }, - } + return await handleReadAfterWrite(ctx, requester, res, getTimelineMunge) }, }) } diff --git a/packages/pds/src/app-view/api/app/bsky/graph/getBlocks.ts b/packages/pds/src/app-view/api/app/bsky/graph/getBlocks.ts index 11acf6352a5..52f7b4d6f7f 100644 --- a/packages/pds/src/app-view/api/app/bsky/graph/getBlocks.ts +++ b/packages/pds/src/app-view/api/app/bsky/graph/getBlocks.ts @@ -1,72 +1,18 @@ import { Server } from '../../../../../lexicon' -import { paginate, TimeCidKeyset } from '../../../../../db/pagination' import AppContext from '../../../../../context' -import { notSoftDeletedClause } from '../../../../../db/util' export default function (server: Server, ctx: AppContext) { server.app.bsky.graph.getBlocks({ auth: ctx.accessVerifier, handler: async ({ params, auth }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.graph.getBlocks( - params, - await ctx.serviceAuthHeaders(requester), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - - const { limit, cursor } = params - const { services, db } = ctx - const { ref } = db.db.dynamic - - let blocksReq = ctx.db.db - .selectFrom('actor_block') - .where('actor_block.creator', '=', requester) - .innerJoin( - 'did_handle as subject', - 'subject.did', - 'actor_block.subjectDid', - ) - .innerJoin( - 'repo_root as subject_repo', - 'subject_repo.did', - 'actor_block.subjectDid', - ) - .where(notSoftDeletedClause(ref('subject_repo'))) - .selectAll('subject') - .select([ - 'actor_block.cid as cid', - 'actor_block.createdAt as createdAt', - ]) - - const keyset = new TimeCidKeyset( - ref('actor_block.createdAt'), - ref('actor_block.cid'), + const res = await ctx.appviewAgent.api.app.bsky.graph.getBlocks( + params, + await ctx.serviceAuthHeaders(requester), ) - blocksReq = paginate(blocksReq, { - limit, - cursor, - keyset, - }) - - const blocksRes = await blocksReq.execute() - - const actorService = services.appView.actor(db) - const blocks = await actorService.views.hydrateProfiles( - blocksRes, - requester, - ) - return { encoding: 'application/json', - body: { - blocks, - cursor: keyset.packFromResult(blocksRes), - }, + body: res.data, } }, }) diff --git a/packages/pds/src/app-view/api/app/bsky/graph/getFollowers.ts b/packages/pds/src/app-view/api/app/bsky/graph/getFollowers.ts index 73a38e83df2..0c7f23869eb 100644 --- a/packages/pds/src/app-view/api/app/bsky/graph/getFollowers.ts +++ b/packages/pds/src/app-view/api/app/bsky/graph/getFollowers.ts @@ -1,8 +1,5 @@ -import { InvalidRequestError } from '@atproto/xrpc-server' import { Server } from '../../../../../lexicon' -import { paginate, TimeCidKeyset } from '../../../../../db/pagination' import AppContext from '../../../../../context' -import { notSoftDeletedClause } from '../../../../../db/util' import { authPassthru } from '../../../../../api/com/atproto/admin/util' export default function (server: Server, ctx: AppContext) { @@ -11,90 +8,13 @@ export default function (server: Server, ctx: AppContext) { handler: async ({ req, params, auth }) => { const requester = auth.credentials.type === 'access' ? auth.credentials.did : null - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.graph.getFollowers( - params, - requester - ? await ctx.serviceAuthHeaders(requester) - : authPassthru(req), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - - const canViewTakendownProfile = - auth.credentials.type === 'role' && auth.credentials.triage - const { actor, limit, cursor } = params - const { services, db } = ctx - const { ref } = db.db.dynamic - - const actorService = services.appView.actor(db) - const graphService = services.appView.graph(db) - - const subjectRes = await actorService.getActor( - actor, - canViewTakendownProfile, + const res = await ctx.appviewAgent.api.app.bsky.graph.getFollowers( + params, + requester ? await ctx.serviceAuthHeaders(requester) : authPassthru(req), ) - if (!subjectRes) { - throw new InvalidRequestError(`Actor not found: ${actor}`) - } - - let followersReq = ctx.db.db - .selectFrom('follow') - .where('follow.subjectDid', '=', subjectRes.did) - .innerJoin('did_handle as creator', 'creator.did', 'follow.creator') - .innerJoin( - 'repo_root as creator_repo', - 'creator_repo.did', - 'follow.creator', - ) - .if(!canViewTakendownProfile, (qb) => - qb.where(notSoftDeletedClause(ref('creator_repo'))), - ) - .whereNotExists( - graphService.blockQb(requester, [ref('follow.creator')]), - ) - .whereNotExists( - graphService.blockRefQb( - ref('follow.subjectDid'), - ref('follow.creator'), - ), - ) - .selectAll('creator') - .select(['follow.cid as cid', 'follow.createdAt as createdAt']) - - const keyset = new TimeCidKeyset( - ref('follow.createdAt'), - ref('follow.cid'), - ) - followersReq = paginate(followersReq, { - limit, - cursor, - keyset, - }) - - const followersRes = await followersReq.execute() - const [followers, subject] = await Promise.all([ - actorService.views.hydrateProfiles(followersRes, requester, { - includeSoftDeleted: canViewTakendownProfile, - }), - actorService.views.profile(subjectRes, requester, { - includeSoftDeleted: canViewTakendownProfile, - }), - ]) - if (!subject) { - throw new InvalidRequestError(`Actor not found: ${actor}`) - } - return { encoding: 'application/json', - body: { - subject, - followers, - cursor: keyset.packFromResult(followersRes), - }, + body: res.data, } }, }) diff --git a/packages/pds/src/app-view/api/app/bsky/graph/getFollows.ts b/packages/pds/src/app-view/api/app/bsky/graph/getFollows.ts index 3ebd47f6210..5871a213302 100644 --- a/packages/pds/src/app-view/api/app/bsky/graph/getFollows.ts +++ b/packages/pds/src/app-view/api/app/bsky/graph/getFollows.ts @@ -1,8 +1,5 @@ -import { InvalidRequestError } from '@atproto/xrpc-server' import { Server } from '../../../../../lexicon' -import { paginate, TimeCidKeyset } from '../../../../../db/pagination' import AppContext from '../../../../../context' -import { notSoftDeletedClause } from '../../../../../db/util' import { authPassthru } from '../../../../../api/com/atproto/admin/util' export default function (server: Server, ctx: AppContext) { @@ -11,90 +8,13 @@ export default function (server: Server, ctx: AppContext) { handler: async ({ req, params, auth }) => { const requester = auth.credentials.type === 'access' ? auth.credentials.did : null - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.graph.getFollows( - params, - requester - ? await ctx.serviceAuthHeaders(requester) - : authPassthru(req), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - - const canViewTakendownProfile = - auth.credentials.type === 'role' && auth.credentials.triage - const { actor, limit, cursor } = params - const { services, db } = ctx - const { ref } = db.db.dynamic - - const actorService = services.appView.actor(db) - const graphService = services.appView.graph(db) - - const creatorRes = await actorService.getActor( - actor, - canViewTakendownProfile, + const res = await ctx.appviewAgent.api.app.bsky.graph.getFollows( + params, + requester ? await ctx.serviceAuthHeaders(requester) : authPassthru(req), ) - if (!creatorRes) { - throw new InvalidRequestError(`Actor not found: ${actor}`) - } - - let followsReq = ctx.db.db - .selectFrom('follow') - .where('follow.creator', '=', creatorRes.did) - .innerJoin('did_handle as subject', 'subject.did', 'follow.subjectDid') - .innerJoin( - 'repo_root as subject_repo', - 'subject_repo.did', - 'follow.subjectDid', - ) - .if(!canViewTakendownProfile, (qb) => - qb.where(notSoftDeletedClause(ref('subject_repo'))), - ) - .whereNotExists( - graphService.blockQb(requester, [ref('follow.subjectDid')]), - ) - .whereNotExists( - graphService.blockRefQb( - ref('follow.subjectDid'), - ref('follow.creator'), - ), - ) - .selectAll('subject') - .select(['follow.cid as cid', 'follow.createdAt as createdAt']) - - const keyset = new TimeCidKeyset( - ref('follow.createdAt'), - ref('follow.cid'), - ) - followsReq = paginate(followsReq, { - limit, - cursor, - keyset, - }) - - const followsRes = await followsReq.execute() - const [follows, subject] = await Promise.all([ - actorService.views.hydrateProfiles(followsRes, requester, { - includeSoftDeleted: canViewTakendownProfile, - }), - actorService.views.profile(creatorRes, requester, { - includeSoftDeleted: canViewTakendownProfile, - }), - ]) - if (!subject) { - throw new InvalidRequestError(`Actor not found: ${actor}`) - } - return { encoding: 'application/json', - body: { - subject, - follows, - cursor: keyset.packFromResult(followsRes), - }, + body: res.data, } }, }) diff --git a/packages/pds/src/app-view/api/app/bsky/graph/getList.ts b/packages/pds/src/app-view/api/app/bsky/graph/getList.ts index a7a1556ec18..ba9b9f9346f 100644 --- a/packages/pds/src/app-view/api/app/bsky/graph/getList.ts +++ b/packages/pds/src/app-view/api/app/bsky/graph/getList.ts @@ -1,6 +1,4 @@ -import { InvalidRequestError } from '@atproto/xrpc-server' import { Server } from '../../../../../lexicon' -import { paginate, TimeCidKeyset } from '../../../../../db/pagination' import AppContext from '../../../../../context' export default function (server: Server, ctx: AppContext) { @@ -8,86 +6,13 @@ export default function (server: Server, ctx: AppContext) { auth: ctx.accessVerifier, handler: async ({ params, auth }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.graph.getList( - params, - await ctx.serviceAuthHeaders(requester), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - - const { list, limit, cursor } = params - const { services, db } = ctx - const { ref } = db.db.dynamic - - const graphService = ctx.services.appView.graph(ctx.db) - - const listRes = await graphService - .getListsQb(requester) - .where('list.uri', '=', list) - .executeTakeFirst() - if (!listRes) { - throw new InvalidRequestError(`List not found: ${list}`) - } - - let itemsReq = graphService - .getListItemsQb() - .where('list_item.listUri', '=', list) - .where('list_item.creator', '=', listRes.creator) - - const keyset = new TimeCidKeyset( - ref('list_item.createdAt'), - ref('list_item.cid'), + const res = await ctx.appviewAgent.api.app.bsky.graph.getList( + params, + await ctx.serviceAuthHeaders(requester), ) - itemsReq = paginate(itemsReq, { - limit, - cursor, - keyset, - }) - const itemsRes = await itemsReq.execute() - - const actorService = services.appView.actor(db) - const profiles = await actorService.views.hydrateProfiles( - itemsRes, - requester, - ) - - const items = profiles.map((subject) => ({ subject })) - - const creator = await actorService.views.profile(listRes, requester) - if (!creator) { - throw new InvalidRequestError(`Actor not found: ${listRes.handle}`) - } - - const subject = { - uri: listRes.uri, - cid: listRes.cid, - creator, - name: listRes.name, - purpose: listRes.purpose, - description: listRes.description ?? undefined, - descriptionFacets: listRes.descriptionFacets - ? JSON.parse(listRes.descriptionFacets) - : undefined, - avatar: listRes.avatarCid - ? ctx.imgUriBuilder.getCommonSignedUri('avatar', listRes.avatarCid) - : undefined, - indexedAt: listRes.indexedAt, - viewer: { - muted: !!listRes.viewerMuted, - }, - } - return { encoding: 'application/json', - body: { - items, - list: subject, - cursor: keyset.packFromResult(itemsRes), - }, + body: res.data, } }, }) diff --git a/packages/pds/src/app-view/api/app/bsky/graph/getListMutes.ts b/packages/pds/src/app-view/api/app/bsky/graph/getListMutes.ts index 673b2c61b38..adae96d58b0 100644 --- a/packages/pds/src/app-view/api/app/bsky/graph/getListMutes.ts +++ b/packages/pds/src/app-view/api/app/bsky/graph/getListMutes.ts @@ -1,5 +1,4 @@ import { Server } from '../../../../../lexicon' -import { paginate, TimeCidKeyset } from '../../../../../db/pagination' import AppContext from '../../../../../context' export default function (server: Server, ctx: AppContext) { @@ -7,54 +6,13 @@ export default function (server: Server, ctx: AppContext) { auth: ctx.accessVerifier, handler: async ({ params, auth }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.graph.getListMutes( - params, - await ctx.serviceAuthHeaders(requester), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - - const { limit, cursor } = params - const { db } = ctx - const { ref } = db.db.dynamic - - const graphService = ctx.services.appView.graph(ctx.db) - - let listsReq = graphService - .getListsQb(requester) - .whereExists( - ctx.db.db - .selectFrom('list_mute') - .where('list_mute.mutedByDid', '=', requester) - .whereRef('list_mute.listUri', '=', ref('list.uri')) - .selectAll(), - ) - - const keyset = new TimeCidKeyset(ref('list.createdAt'), ref('list.cid')) - listsReq = paginate(listsReq, { - limit, - cursor, - keyset, - }) - const listsRes = await listsReq.execute() - - const actorService = ctx.services.appView.actor(ctx.db) - const profiles = await actorService.views.profiles(listsRes, requester) - - const lists = listsRes.map((row) => - graphService.formatListView(row, profiles), + const res = await ctx.appviewAgent.api.app.bsky.graph.getListMutes( + params, + await ctx.serviceAuthHeaders(requester), ) - return { encoding: 'application/json', - body: { - lists, - cursor: keyset.packFromResult(listsRes), - }, + body: res.data, } }, }) diff --git a/packages/pds/src/app-view/api/app/bsky/graph/getLists.ts b/packages/pds/src/app-view/api/app/bsky/graph/getLists.ts index c6653726459..893b6d5a9ad 100644 --- a/packages/pds/src/app-view/api/app/bsky/graph/getLists.ts +++ b/packages/pds/src/app-view/api/app/bsky/graph/getLists.ts @@ -1,6 +1,4 @@ -import { InvalidRequestError } from '@atproto/xrpc-server' import { Server } from '../../../../../lexicon' -import { paginate, TimeCidKeyset } from '../../../../../db/pagination' import AppContext from '../../../../../context' export default function (server: Server, ctx: AppContext) { @@ -8,61 +6,13 @@ export default function (server: Server, ctx: AppContext) { auth: ctx.accessVerifier, handler: async ({ params, auth }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.graph.getLists( - params, - await ctx.serviceAuthHeaders(requester), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - - const { actor, limit, cursor } = params - const { services, db } = ctx - const { ref } = db.db.dynamic - - const actorService = services.appView.actor(db) - const graphService = services.appView.graph(db) - - const creatorRes = await actorService.getActor(actor) - if (!creatorRes) { - throw new InvalidRequestError(`Actor not found: ${actor}`) - } - - let listsReq = graphService - .getListsQb(requester) - .where('list.creator', '=', creatorRes.did) - - const keyset = new TimeCidKeyset(ref('list.createdAt'), ref('list.cid')) - listsReq = paginate(listsReq, { - limit, - cursor, - keyset, - }) - - const [listsRes, creator] = await Promise.all([ - listsReq.execute(), - actorService.views.profile(creatorRes, requester), - ]) - if (!creator) { - throw new InvalidRequestError(`Actor not found: ${actor}`) - } - const profileMap = { - [creator.did]: creator, - } - - const lists = listsRes.map((row) => - graphService.formatListView(row, profileMap), + const res = await ctx.appviewAgent.api.app.bsky.graph.getLists( + params, + await ctx.serviceAuthHeaders(requester), ) - return { encoding: 'application/json', - body: { - lists, - cursor: keyset.packFromResult(listsRes), - }, + body: res.data, } }, }) diff --git a/packages/pds/src/app-view/api/app/bsky/graph/getMutes.ts b/packages/pds/src/app-view/api/app/bsky/graph/getMutes.ts index a082666a968..596fef9dd15 100644 --- a/packages/pds/src/app-view/api/app/bsky/graph/getMutes.ts +++ b/packages/pds/src/app-view/api/app/bsky/graph/getMutes.ts @@ -1,68 +1,19 @@ import { Server } from '../../../../../lexicon' -import { paginate, TimeCidKeyset } from '../../../../../db/pagination' import AppContext from '../../../../../context' -import { notSoftDeletedClause } from '../../../../../db/util' export default function (server: Server, ctx: AppContext) { server.app.bsky.graph.getMutes({ auth: ctx.accessVerifier, handler: async ({ auth, params }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.app.bsky.graph.getMutes( - params, - await ctx.serviceAuthHeaders(requester), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - - const { limit, cursor } = params - const { services, db } = ctx - const { ref } = ctx.db.db.dynamic - - let mutesReq = ctx.db.db - .selectFrom('mute') - .innerJoin('did_handle as actor', 'actor.did', 'mute.did') - .innerJoin('repo_root', 'repo_root.did', 'mute.did') - .where(notSoftDeletedClause(ref('repo_root'))) - .where('mute.mutedByDid', '=', requester) - .selectAll('actor') - .select('mute.createdAt as createdAt') - - const keyset = new CreatedAtDidKeyset( - ref('mute.createdAt'), - ref('mute.did'), + const res = await ctx.appviewAgent.api.app.bsky.graph.getMutes( + params, + await ctx.serviceAuthHeaders(requester), ) - mutesReq = paginate(mutesReq, { - limit, - cursor, - keyset, - }) - - const mutesRes = await mutesReq.execute() - - // @NOTE calling into app-view, will eventually be replaced - const actorService = services.appView.actor(db) - return { encoding: 'application/json', - body: { - cursor: keyset.packFromResult(mutesRes), - mutes: await actorService.views.hydrateProfiles(mutesRes, requester), - }, + body: res.data, } }, }) } - -export class CreatedAtDidKeyset extends TimeCidKeyset<{ - createdAt: string - did: string // dids are treated identically to cids in TimeCidKeyset -}> { - labelResult(result: { createdAt: string; did: string }) { - return { primary: result.createdAt, secondary: result.did } - } -} diff --git a/packages/pds/src/app-view/api/app/bsky/unspecced.ts b/packages/pds/src/app-view/api/app/bsky/unspecced.ts index 9dc3f77aba4..1352d831dc5 100644 --- a/packages/pds/src/app-view/api/app/bsky/unspecced.ts +++ b/packages/pds/src/app-view/api/app/bsky/unspecced.ts @@ -1,24 +1,7 @@ -import { NotEmptyArray } from '@atproto/common' import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server' import { Server } from '../../../../lexicon' -import { GenericKeyset, paginate } from '../../../../db/pagination' +import { GenericKeyset } from '../../../../db/pagination' import AppContext from '../../../../context' -import { FeedRow } from '../../../services/feed' -import { - isPostView, - GeneratorView, -} from '../../../../lexicon/types/app/bsky/feed/defs' -import { isViewRecord } from '../../../../lexicon/types/app/bsky/embed/record' -import { countAll, valuesList } from '../../../../db/util' -import { FeedKeyset } from './util/feed' - -const NO_WHATS_HOT_LABELS: NotEmptyArray = [ - '!no-promote', - 'corpse', - 'self-harm', -] - -const NSFW_LABELS = ['porn', 'sexual', 'nudity', 'underwear'] // @NOTE currently relies on the hot-classic feed being configured on the pds // THIS IS A TEMPORARY UNSPECCED ROUTE @@ -27,103 +10,27 @@ export default function (server: Server, ctx: AppContext) { auth: ctx.accessVerifier, handler: async ({ params, auth }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const hotClassicUri = Object.keys(ctx.algos).find((uri) => - uri.endsWith('/hot-classic'), - ) - if (!hotClassicUri) { - return { - encoding: 'application/json', - body: { feed: [] }, - } - } - const { data: feed } = - await ctx.appviewAgent.api.app.bsky.feed.getFeedGenerator( - { feed: hotClassicUri }, - await ctx.serviceAuthHeaders(requester), - ) - const res = await ctx.appviewAgent.api.app.bsky.feed.getFeed( - { feed: hotClassicUri, limit: params.limit, cursor: params.cursor }, - await ctx.serviceAuthHeaders(requester, feed.view.did), - ) + const hotClassicUri = Object.keys(ctx.algos).find((uri) => + uri.endsWith('/hot-classic'), + ) + if (!hotClassicUri) { return { encoding: 'application/json', - body: res.data, + body: { feed: [] }, } } - - const { limit, cursor, includeNsfw } = params - const db = ctx.db.db - const { ref } = db.dynamic - - const accountService = ctx.services.account(ctx.db) - const feedService = ctx.services.appView.feed(ctx.db) - const graphService = ctx.services.appView.graph(ctx.db) - - const labelsToFilter = includeNsfw - ? NO_WHATS_HOT_LABELS - : [...NO_WHATS_HOT_LABELS, ...NSFW_LABELS] - - const postsQb = feedService - .selectPostQb() - .leftJoin('post_agg', 'post_agg.uri', 'post.uri') - .where('post_agg.likeCount', '>=', 12) - .where('post.replyParent', 'is', null) - .whereNotExists((qb) => - qb - .selectFrom('label') - .selectAll() - .whereRef('val', 'in', valuesList(labelsToFilter)) - .where('neg', '=', 0) - .where((clause) => - clause - .whereRef('label.uri', '=', ref('post.creator')) - .orWhereRef('label.uri', '=', ref('post.uri')), - ), + const { data: feed } = + await ctx.appviewAgent.api.app.bsky.feed.getFeedGenerator( + { feed: hotClassicUri }, + await ctx.serviceAuthHeaders(requester), ) - .where((qb) => - accountService.whereNotMuted(qb, requester, [ref('post.creator')]), - ) - .whereNotExists(graphService.blockQb(requester, [ref('post.creator')])) - - const keyset = new FeedKeyset(ref('sortAt'), ref('cid')) - - let feedQb = ctx.db.db.selectFrom(postsQb.as('feed_items')).selectAll() - feedQb = paginate(feedQb, { limit, cursor, keyset }) - - const feedItems: FeedRow[] = await feedQb.execute() - const feed = await feedService.hydrateFeed(feedItems, requester) - - // filter out any quote post where the internal post has a filtered label - const noLabeledQuotePosts = feed.filter((post) => { - const quoteView = post.post.embed?.record - if (!quoteView || !isViewRecord(quoteView)) return true - for (const label of quoteView.labels || []) { - if (labelsToFilter.includes(label.val)) return false - } - return true - }) - - // remove record embeds in our response - const noRecordEmbeds = noLabeledQuotePosts.map((post) => { - delete post.post.record['embed'] - if (post.reply) { - if (isPostView(post.reply.parent)) { - delete post.reply.parent.record['embed'] - } - if (isPostView(post.reply.root)) { - delete post.reply.root.record['embed'] - } - } - return post - }) - + const res = await ctx.appviewAgent.api.app.bsky.feed.getFeed( + { feed: hotClassicUri, limit: params.limit, cursor: params.cursor }, + await ctx.serviceAuthHeaders(requester, feed.view.did), + ) return { encoding: 'application/json', - body: { - feed: noRecordEmbeds, - cursor: keyset.packFromResult(feedItems), - }, + body: res.data, } }, }) @@ -132,62 +39,14 @@ export default function (server: Server, ctx: AppContext) { auth: ctx.accessVerifier, handler: async ({ auth, params }) => { const requester = auth.credentials.did - const db = ctx.db.db - const { limit, cursor, query } = params - const { ref } = db.dynamic - const feedService = ctx.services.appView.feed(ctx.db) - - let inner = ctx.db.db - .selectFrom('feed_generator') - .select([ - 'uri', - 'cid', - ctx.db.db - .selectFrom('like') - .whereRef('like.subject', '=', ref('feed_generator.uri')) - .select(countAll.as('count')) - .as('likeCount'), - ]) - - if (query) { - // like is case-insensitive is sqlite, and ilike is not supported - const operator = ctx.db.dialect === 'pg' ? 'ilike' : 'like' - inner = inner.where((qb) => - qb - .where('feed_generator.displayName', operator, `%${query}%`) - .orWhere('feed_generator.description', operator, `%${query}%`), + const res = + await ctx.appviewAgent.api.app.bsky.unspecced.getPopularFeedGenerators( + params, + await ctx.serviceAuthHeaders(requester), ) - } - - let builder = ctx.db.db.selectFrom(inner.as('feed_gens')).selectAll() - - const keyset = new LikeCountKeyset(ref('likeCount'), ref('cid')) - builder = paginate(builder, { limit, cursor, keyset, direction: 'desc' }) - - const res = await builder.execute() - - const genInfos = await feedService.getFeedGeneratorInfos( - res.map((feed) => feed.uri), - requester, - ) - - const creators = Object.values(genInfos).map((gen) => gen.creator) - const profiles = await feedService.getActorInfos(creators, requester) - - const genViews: GeneratorView[] = [] - for (const row of res) { - const gen = genInfos[row.uri] - if (!gen) continue - const view = feedService.views.formatFeedGeneratorView(gen, profiles) - genViews.push(view) - } - return { encoding: 'application/json', - body: { - cursor: keyset.packFromResult(res), - feeds: genViews, - }, + body: res.data, } }, }) diff --git a/packages/pds/src/app-view/services/label/index.ts b/packages/pds/src/app-view/services/label/index.ts index 338475ba633..a3534666d5d 100644 --- a/packages/pds/src/app-view/services/label/index.ts +++ b/packages/pds/src/app-view/services/label/index.ts @@ -1,5 +1,4 @@ import { sql } from 'kysely' -import { CID } from 'multiformats/cid' import { AtUri } from '@atproto/syntax' import Database from '../../../db' import { diff --git a/packages/pds/src/config.ts b/packages/pds/src/config.ts index acca18b580a..c67c322e50c 100644 --- a/packages/pds/src/config.ts +++ b/packages/pds/src/config.ts @@ -69,10 +69,9 @@ export interface ServerConfigValues { // this is really only used in test environments dbTxLockNonce?: string - bskyAppViewEndpoint?: string + bskyAppViewEndpoint: string + bskyAppViewDid: string bskyAppViewModeration?: boolean - bskyAppViewDid?: string - bskyAppViewProxy: boolean bskyAppViewCdnUrlPattern?: string crawlersToNotify?: string[] @@ -228,12 +227,17 @@ export class ServerConfig { const bskyAppViewEndpoint = nonemptyString( process.env.BSKY_APP_VIEW_ENDPOINT, ) + if (typeof bskyAppViewEndpoint !== 'string') { + throw new Error( + 'No value provided for process.env.BSKY_APP_VIEW_ENDPOINT', + ) + } + const bskyAppViewDid = nonemptyString(process.env.BSKY_APP_VIEW_DID) + if (typeof bskyAppViewDid !== 'string') { + throw new Error('No value provided for process.env.BSKY_APP_VIEW_DID') + } const bskyAppViewModeration = process.env.BSKY_APP_VIEW_MODERATION === 'true' ? true : false - const bskyAppViewDid = nonemptyString(process.env.BSKY_APP_VIEW_DID) - const bskyAppViewProxy = - process.env.BSKY_APP_VIEW_PROXY === 'true' ? true : false - const bskyAppViewCdnUrlPattern = nonemptyString( process.env.BSKY_APP_VIEW_CDN_URL_PATTERN, ) @@ -294,9 +298,8 @@ export class ServerConfig { sequencerLeaderEnabled, dbTxLockNonce, bskyAppViewEndpoint, - bskyAppViewModeration, bskyAppViewDid, - bskyAppViewProxy, + bskyAppViewModeration, bskyAppViewCdnUrlPattern, crawlersToNotify, ...overrides, @@ -537,16 +540,12 @@ export class ServerConfig { return this.cfg.bskyAppViewEndpoint } - get bskyAppViewModeration() { - return this.cfg.bskyAppViewModeration - } - get bskyAppViewDid() { return this.cfg.bskyAppViewDid } - get bskyAppViewProxy() { - return this.cfg.bskyAppViewProxy + get bskyAppViewModeration() { + return this.cfg.bskyAppViewModeration } get bskyAppViewCdnUrlPattern() { diff --git a/packages/pds/src/context.ts b/packages/pds/src/context.ts index 1f414845ea1..13e765aaf77 100644 --- a/packages/pds/src/context.ts +++ b/packages/pds/src/context.ts @@ -1,4 +1,3 @@ -import express from 'express' import { Redis } from 'ioredis' import * as plc from '@did-plc/lib' import * as crypto from '@atproto/crypto' @@ -46,7 +45,7 @@ export class AppContext { labelCache: LabelCache runtimeFlags: RuntimeFlags backgroundQueue: BackgroundQueue - appviewAgent?: AtpAgent + appviewAgent: AtpAgent crawlers: Crawlers algos: MountedAlgos }, @@ -168,6 +167,10 @@ export class AppContext { return this.opts.didCache } + get appviewAgent(): AtpAgent { + return this.opts.appviewAgent + } + get algos(): MountedAlgos { return this.opts.algos } @@ -184,27 +187,6 @@ export class AppContext { }) } - get appviewAgent(): AtpAgent { - if (!this.opts.appviewAgent) { - throw new Error('Could not find bsky appview endpoint') - } - return this.opts.appviewAgent - } - - canProxyRead(): boolean { - if (!this.cfg.bskyAppViewProxy || !this.cfg.bskyAppViewEndpoint) { - return false - } - return true - } - - canProxyFeedConstruction(req: express.Request): boolean { - return ( - this.cfg.bskyAppViewEndpoint !== undefined && - req.get('x-appview-proxy') !== undefined - ) - } - shouldProxyModeration(): boolean { return ( this.cfg.bskyAppViewEndpoint !== undefined && diff --git a/packages/pds/src/index.ts b/packages/pds/src/index.ts index a156d8f7b8e..09a077ce9f7 100644 --- a/packages/pds/src/index.ts +++ b/packages/pds/src/index.ts @@ -198,10 +198,7 @@ export class PDS { } const labelCache = new LabelCache(db) - - const appviewAgent = config.bskyAppViewEndpoint - ? new AtpAgent({ service: config.bskyAppViewEndpoint }) - : undefined + const appviewAgent = new AtpAgent({ service: config.bskyAppViewEndpoint }) const services = createServices({ repoSigningKey, From 9514a040f4efc2e737c7350724fe9be4de0f972e Mon Sep 17 00:00:00 2001 From: dholms Date: Wed, 20 Sep 2023 11:29:40 -0500 Subject: [PATCH 04/31] remove all canProxyReadc --- .../api/com/atproto/identity/resolveHandle.ts | 5 +- .../pds/src/api/com/atproto/repo/getRecord.ts | 16 +- .../app/bsky/notification/getUnreadCount.ts | 49 +----- .../bsky/notification/listNotifications.ts | 161 +----------------- 4 files changed, 15 insertions(+), 216 deletions(-) diff --git a/packages/pds/src/api/com/atproto/identity/resolveHandle.ts b/packages/pds/src/api/com/atproto/identity/resolveHandle.ts index 494287dc096..b45c28f9dbd 100644 --- a/packages/pds/src/api/com/atproto/identity/resolveHandle.ts +++ b/packages/pds/src/api/com/atproto/identity/resolveHandle.ts @@ -33,10 +33,7 @@ export default function (server: Server, ctx: AppContext) { } // this is not someone on our server, but we help with resolving anyway - - if (!did && ctx.canProxyRead()) { - did = await tryResolveFromAppview(ctx.appviewAgent, handle) - } + did = await tryResolveFromAppview(ctx.appviewAgent, handle) if (!did) { did = await ctx.idResolver.handle.resolve(handle) diff --git a/packages/pds/src/api/com/atproto/repo/getRecord.ts b/packages/pds/src/api/com/atproto/repo/getRecord.ts index 2d73244b0d9..84b2475e4ca 100644 --- a/packages/pds/src/api/com/atproto/repo/getRecord.ts +++ b/packages/pds/src/api/com/atproto/repo/getRecord.ts @@ -1,5 +1,4 @@ import { AtUri } from '@atproto/syntax' -import { InvalidRequestError } from '@atproto/xrpc-server' import { Server } from '../../../../lexicon' import AppContext from '../../../../context' @@ -26,17 +25,10 @@ export default function (server: Server, ctx: AppContext) { } } - if (ctx.canProxyRead()) { - const res = await ctx.appviewAgent.api.com.atproto.repo.getRecord(params) - return { - encoding: 'application/json', - body: res.data, - } - } else { - const uri = AtUri.make(did || repo, collection, rkey) - throw new InvalidRequestError( - `Could not locate record: ${uri.toString()}`, - ) + const res = await ctx.appviewAgent.api.com.atproto.repo.getRecord(params) + return { + encoding: 'application/json', + body: res.data, } }) } diff --git a/packages/pds/src/app-view/api/app/bsky/notification/getUnreadCount.ts b/packages/pds/src/app-view/api/app/bsky/notification/getUnreadCount.ts index 262cfa06fe0..61c2b48f1e3 100644 --- a/packages/pds/src/app-view/api/app/bsky/notification/getUnreadCount.ts +++ b/packages/pds/src/app-view/api/app/bsky/notification/getUnreadCount.ts @@ -1,6 +1,4 @@ -import { InvalidRequestError } from '@atproto/xrpc-server' import { Server } from '../../../../../lexicon' -import { countAll, notSoftDeletedClause } from '../../../../../db/util' import AppContext from '../../../../../context' export default function (server: Server, ctx: AppContext) { @@ -8,51 +6,14 @@ export default function (server: Server, ctx: AppContext) { auth: ctx.accessVerifier, handler: async ({ auth, params }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const res = - await ctx.appviewAgent.api.app.bsky.notification.getUnreadCount( - params, - await ctx.serviceAuthHeaders(requester), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - - const { seenAt } = params - const { ref } = ctx.db.db.dynamic - if (seenAt) { - throw new InvalidRequestError('The seenAt parameter is unsupported') - } - - const accountService = ctx.services.account(ctx.db) - - const result = await ctx.db.db - .selectFrom('user_notification as notif') - .select(countAll.as('count')) - .innerJoin('user_account', 'user_account.did', 'notif.userDid') - .innerJoin('user_state', 'user_state.did', 'user_account.did') - .innerJoin( - 'repo_root as author_repo', - 'author_repo.did', - 'notif.author', - ) - .innerJoin('record', 'record.uri', 'notif.recordUri') - .where(notSoftDeletedClause(ref('author_repo'))) - .where(notSoftDeletedClause(ref('record'))) - .where('notif.userDid', '=', requester) - .where((qb) => - accountService.whereNotMuted(qb, requester, [ref('notif.author')]), + const res = + await ctx.appviewAgent.api.app.bsky.notification.getUnreadCount( + params, + await ctx.serviceAuthHeaders(requester), ) - .whereRef('notif.indexedAt', '>', 'user_state.lastSeenNotifs') - .executeTakeFirst() - - const count = result?.count ?? 0 - return { encoding: 'application/json', - body: { count }, + body: res.data, } }, }) diff --git a/packages/pds/src/app-view/api/app/bsky/notification/listNotifications.ts b/packages/pds/src/app-view/api/app/bsky/notification/listNotifications.ts index d3e44a90aa2..eefb3d29a48 100644 --- a/packages/pds/src/app-view/api/app/bsky/notification/listNotifications.ts +++ b/packages/pds/src/app-view/api/app/bsky/notification/listNotifications.ts @@ -1,171 +1,20 @@ -import { sql } from 'kysely' -import { InvalidRequestError } from '@atproto/xrpc-server' -import * as common from '@atproto/common' import { Server } from '../../../../../lexicon' -import { paginate, TimeCidKeyset } from '../../../../../db/pagination' import AppContext from '../../../../../context' -import { notSoftDeletedClause, valuesList } from '../../../../../db/util' -import { getSelfLabels } from '../../../../services/label' export default function (server: Server, ctx: AppContext) { server.app.bsky.notification.listNotifications({ auth: ctx.accessVerifier, handler: async ({ params, auth }) => { const requester = auth.credentials.did - if (ctx.canProxyRead()) { - const res = - await ctx.appviewAgent.api.app.bsky.notification.listNotifications( - params, - await ctx.serviceAuthHeaders(requester), - ) - return { - encoding: 'application/json', - body: res.data, - } - } - - const { limit, cursor, seenAt } = params - const { ref } = ctx.db.db.dynamic - if (seenAt) { - throw new InvalidRequestError('The seenAt parameter is unsupported') - } - - const accountService = ctx.services.account(ctx.db) - const graphService = ctx.services.appView.graph(ctx.db) - - let notifBuilder = ctx.db.db - .selectFrom('user_notification as notif') - .innerJoin('did_handle as author', 'author.did', 'notif.author') - .innerJoin( - 'repo_root as author_repo', - 'author_repo.did', - 'notif.author', - ) - .innerJoin('record', 'record.uri', 'notif.recordUri') - .where(notSoftDeletedClause(ref('author_repo'))) - .where(notSoftDeletedClause(ref('record'))) - .where('notif.userDid', '=', requester) - .where((qb) => - accountService.whereNotMuted(qb, requester, [ref('notif.author')]), + const res = + await ctx.appviewAgent.api.app.bsky.notification.listNotifications( + params, + await ctx.serviceAuthHeaders(requester), ) - .whereNotExists(graphService.blockQb(requester, [ref('notif.author')])) - .where((clause) => - clause - .where('reasonSubject', 'is', null) - .orWhereExists( - ctx.db.db - .selectFrom('record as subject') - .selectAll() - .whereRef('subject.uri', '=', ref('notif.reasonSubject')), - ), - ) - .select([ - 'notif.recordUri as uri', - 'notif.recordCid as cid', - 'author.did as authorDid', - 'author.handle as authorHandle', - 'notif.reason as reason', - 'notif.reasonSubject as reasonSubject', - 'notif.indexedAt as indexedAt', - ]) - - const keyset = new NotifsKeyset( - ref('notif.indexedAt'), - ref('notif.recordCid'), - ) - notifBuilder = paginate(notifBuilder, { - cursor, - limit, - keyset, - }) - - const userStateQuery = ctx.db.db - .selectFrom('user_state') - .selectAll() - .where('did', '=', requester) - .executeTakeFirst() - - const [userState, notifs] = await Promise.all([ - userStateQuery, - notifBuilder.execute(), - ]) - - if (!userState) { - throw new InvalidRequestError(`Could not find user: ${requester}`) - } - - const recordTuples = notifs.map((notif) => { - return sql`${notif.authorDid}, ${notif.cid}` - }) - - const emptyBlocksResult: { cid: string; bytes: Uint8Array }[] = [] - const blocksQb = recordTuples.length - ? ctx.db.db - .selectFrom('ipld_block') - .whereRef(sql`(creator, cid)`, 'in', valuesList(recordTuples)) - .select(['cid', 'content as bytes']) - : null - - const actorService = ctx.services.appView.actor(ctx.db) - - // @NOTE calling into app-view, will eventually be replaced - const labelService = ctx.services.appView.label(ctx.db) - const recordUris = notifs.map((notif) => notif.uri) - const [blocks, authors, labels] = await Promise.all([ - blocksQb ? blocksQb.execute() : emptyBlocksResult, - actorService.views.profiles( - notifs.map((notif) => ({ - did: notif.authorDid, - handle: notif.authorHandle, - })), - requester, - ), - labelService.getLabelsForUris(recordUris), - ]) - - const bytesByCid = blocks.reduce((acc, block) => { - acc[block.cid] = block.bytes - return acc - }, {} as Record) - - const notifications = common.mapDefined(notifs, (notif) => { - const bytes = bytesByCid[notif.cid] - const author = authors[notif.authorDid] - if (!bytes || !author) return undefined - const record = common.cborBytesToRecord(bytes) - const recordLabels = labels[notif.uri] ?? [] - const recordSelfLabels = getSelfLabels({ - uri: notif.uri, - cid: notif.cid, - record, - }) - return { - uri: notif.uri, - cid: notif.cid, - author: authors[notif.authorDid], - reason: notif.reason, - reasonSubject: notif.reasonSubject || undefined, - record, - isRead: notif.indexedAt <= userState.lastSeenNotifs, - indexedAt: notif.indexedAt, - labels: [...recordLabels, ...recordSelfLabels], - } - }) - return { encoding: 'application/json', - body: { - notifications, - cursor: keyset.packFromResult(notifs), - }, + body: res.data, } }, }) } - -type NotifRow = { indexedAt: string; cid: string } -class NotifsKeyset extends TimeCidKeyset { - labelResult(result: NotifRow) { - return { primary: result.indexedAt, secondary: result.cid } - } -} From 7e6bc463d15cf9d59b82b07f61f341eead101ebc Mon Sep 17 00:00:00 2001 From: dholms Date: Wed, 20 Sep 2023 15:21:48 -0500 Subject: [PATCH 05/31] finish cleanup --- .../app/bsky/feed/describeFeedGenerator.ts | 21 - .../api/app/bsky/feed/getPostThread.ts | 13 - .../pds/src/app-view/api/app/bsky/index.ts | 2 - .../src/app-view/api/app/bsky/unspecced.ts | 24 +- .../src/app-view/api/app/bsky/util/feed.ts | 19 - .../pds/src/app-view/services/actor/index.ts | 73 -- .../pds/src/app-view/services/actor/views.ts | 374 ---------- .../pds/src/app-view/services/feed/index.ts | 685 ------------------ .../pds/src/app-view/services/feed/types.ts | 107 --- .../pds/src/app-view/services/feed/util.ts | 65 -- .../pds/src/app-view/services/feed/views.ts | 344 --------- .../pds/src/app-view/services/graph/index.ts | 174 ----- .../services/indexing/plugins/post.ts | 10 +- packages/pds/src/context.ts | 6 - packages/pds/src/feed-gen/best-of-follows.ts | 83 --- packages/pds/src/feed-gen/bsky-team.ts | 46 -- packages/pds/src/feed-gen/hot-classic.ts | 59 -- packages/pds/src/feed-gen/index.ts | 20 - packages/pds/src/feed-gen/mutuals.ts | 60 -- packages/pds/src/feed-gen/types.ts | 16 - packages/pds/src/feed-gen/whats-hot.ts | 111 --- packages/pds/src/feed-gen/with-friends.ts | 67 -- packages/pds/src/index.ts | 14 +- packages/pds/src/services/index.ts | 9 - 24 files changed, 4 insertions(+), 2398 deletions(-) delete mode 100644 packages/pds/src/app-view/api/app/bsky/feed/describeFeedGenerator.ts delete mode 100644 packages/pds/src/app-view/api/app/bsky/util/feed.ts delete mode 100644 packages/pds/src/app-view/services/actor/index.ts delete mode 100644 packages/pds/src/app-view/services/actor/views.ts delete mode 100644 packages/pds/src/app-view/services/feed/index.ts delete mode 100644 packages/pds/src/app-view/services/feed/types.ts delete mode 100644 packages/pds/src/app-view/services/feed/util.ts delete mode 100644 packages/pds/src/app-view/services/feed/views.ts delete mode 100644 packages/pds/src/app-view/services/graph/index.ts delete mode 100644 packages/pds/src/feed-gen/best-of-follows.ts delete mode 100644 packages/pds/src/feed-gen/bsky-team.ts delete mode 100644 packages/pds/src/feed-gen/hot-classic.ts delete mode 100644 packages/pds/src/feed-gen/index.ts delete mode 100644 packages/pds/src/feed-gen/mutuals.ts delete mode 100644 packages/pds/src/feed-gen/types.ts delete mode 100644 packages/pds/src/feed-gen/whats-hot.ts delete mode 100644 packages/pds/src/feed-gen/with-friends.ts diff --git a/packages/pds/src/app-view/api/app/bsky/feed/describeFeedGenerator.ts b/packages/pds/src/app-view/api/app/bsky/feed/describeFeedGenerator.ts deleted file mode 100644 index 89a8a1f48dd..00000000000 --- a/packages/pds/src/app-view/api/app/bsky/feed/describeFeedGenerator.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Server } from '../../../../../lexicon' -import AppContext from '../../../../../context' -import { MethodNotImplementedError } from '@atproto/xrpc-server' - -export default function (server: Server, ctx: AppContext) { - server.app.bsky.feed.describeFeedGenerator(async () => { - if (!ctx.cfg.feedGenDid) { - throw new MethodNotImplementedError() - } - - const feeds = Object.keys(ctx.algos).map((uri) => ({ uri })) - - return { - encoding: 'application/json', - body: { - did: ctx.cfg.feedGenDid, - feeds, - }, - } - }) -} diff --git a/packages/pds/src/app-view/api/app/bsky/feed/getPostThread.ts b/packages/pds/src/app-view/api/app/bsky/feed/getPostThread.ts index 9b86df3dc5e..be1e8004bac 100644 --- a/packages/pds/src/app-view/api/app/bsky/feed/getPostThread.ts +++ b/packages/pds/src/app-view/api/app/bsky/feed/getPostThread.ts @@ -3,7 +3,6 @@ import { AppBskyFeedGetPostThread } from '@atproto/api' import { Headers } from '@atproto/xrpc' import { Server } from '../../../../../lexicon' import AppContext from '../../../../../context' -import { FeedRow } from '../../../../services/feed' import { ThreadViewPost, isThreadViewPost, @@ -24,12 +23,6 @@ import { handleReadAfterWrite, } from '../util/read-after-write' -export type PostThread = { - post: FeedRow - parent?: PostThread | ParentNotFoundError - replies?: PostThread[] -} - export default function (server: Server, ctx: AppContext) { server.app.bsky.feed.getPostThread({ auth: ctx.accessVerifier, @@ -75,12 +68,6 @@ export default function (server: Server, ctx: AppContext) { }) } -class ParentNotFoundError extends Error { - constructor(public uri: string) { - super(`Parent not found: ${uri}`) - } -} - // READ AFTER WRITE // ---------------- diff --git a/packages/pds/src/app-view/api/app/bsky/index.ts b/packages/pds/src/app-view/api/app/bsky/index.ts index 96b0b5de06c..69cf8432224 100644 --- a/packages/pds/src/app-view/api/app/bsky/index.ts +++ b/packages/pds/src/app-view/api/app/bsky/index.ts @@ -6,7 +6,6 @@ import getSuggestedFeeds from './feed/getSuggestedFeeds' import getAuthorFeed from './feed/getAuthorFeed' import getFeedGenerator from './feed/getFeedGenerator' import getFeedGenerators from './feed/getFeedGenerators' -import describeFeedGenerator from './feed/describeFeedGenerator' import getFeed from './feed/getFeed' import getLikes from './feed/getLikes' import getListFeed from './feed/getListFeed' @@ -45,7 +44,6 @@ export default function (server: Server, ctx: AppContext) { getAuthorFeed(server, ctx) getFeedGenerator(server, ctx) getFeedGenerators(server, ctx) - describeFeedGenerator(server, ctx) getFeed(server, ctx) getLikes(server, ctx) getListFeed(server, ctx) diff --git a/packages/pds/src/app-view/api/app/bsky/unspecced.ts b/packages/pds/src/app-view/api/app/bsky/unspecced.ts index 1352d831dc5..222b58ffde5 100644 --- a/packages/pds/src/app-view/api/app/bsky/unspecced.ts +++ b/packages/pds/src/app-view/api/app/bsky/unspecced.ts @@ -3,34 +3,14 @@ import { Server } from '../../../../lexicon' import { GenericKeyset } from '../../../../db/pagination' import AppContext from '../../../../context' -// @NOTE currently relies on the hot-classic feed being configured on the pds // THIS IS A TEMPORARY UNSPECCED ROUTE export default function (server: Server, ctx: AppContext) { server.app.bsky.unspecced.getPopular({ auth: ctx.accessVerifier, - handler: async ({ params, auth }) => { - const requester = auth.credentials.did - const hotClassicUri = Object.keys(ctx.algos).find((uri) => - uri.endsWith('/hot-classic'), - ) - if (!hotClassicUri) { - return { - encoding: 'application/json', - body: { feed: [] }, - } - } - const { data: feed } = - await ctx.appviewAgent.api.app.bsky.feed.getFeedGenerator( - { feed: hotClassicUri }, - await ctx.serviceAuthHeaders(requester), - ) - const res = await ctx.appviewAgent.api.app.bsky.feed.getFeed( - { feed: hotClassicUri, limit: params.limit, cursor: params.cursor }, - await ctx.serviceAuthHeaders(requester, feed.view.did), - ) + handler: async () => { return { encoding: 'application/json', - body: res.data, + body: { feed: [] }, } }, }) diff --git a/packages/pds/src/app-view/api/app/bsky/util/feed.ts b/packages/pds/src/app-view/api/app/bsky/util/feed.ts deleted file mode 100644 index 9d9d0323b95..00000000000 --- a/packages/pds/src/app-view/api/app/bsky/util/feed.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { TimeCidKeyset } from '../../../../../db/pagination' -import { FeedRow } from '../../../../services/feed' - -export enum FeedAlgorithm { - ReverseChronological = 'reverse-chronological', -} - -export class FeedKeyset extends TimeCidKeyset { - labelResult(result: FeedRow) { - return { primary: result.sortAt, secondary: result.cid } - } -} - -// For users with sparse feeds, avoid scanning more than one week for a single page -export const getFeedDateThreshold = (from: string | undefined, days = 1) => { - const timelineDateThreshold = from ? new Date(from) : new Date() - timelineDateThreshold.setDate(timelineDateThreshold.getDate() - days) - return timelineDateThreshold.toISOString() -} diff --git a/packages/pds/src/app-view/services/actor/index.ts b/packages/pds/src/app-view/services/actor/index.ts deleted file mode 100644 index e6c6fd6e756..00000000000 --- a/packages/pds/src/app-view/services/actor/index.ts +++ /dev/null @@ -1,73 +0,0 @@ -import Database from '../../../db' -import { DidHandle } from '../../../db/tables/did-handle' -import { notSoftDeletedClause } from '../../../db/util' -import { ActorViews } from './views' -import { ImageUriBuilder } from '../../../image/uri' -import { LabelCache } from '../../../label-cache' - -export class ActorService { - constructor( - public db: Database, - public imgUriBuilder: ImageUriBuilder, - public labelCache: LabelCache, - ) {} - - static creator(imgUriBuilder: ImageUriBuilder, labelCache: LabelCache) { - return (db: Database) => new ActorService(db, imgUriBuilder, labelCache) - } - - views = new ActorViews(this.db, this.imgUriBuilder, this.labelCache) - - async getActor( - handleOrDid: string, - includeSoftDeleted = false, - ): Promise { - const actors = await this.getActors([handleOrDid], includeSoftDeleted) - return actors[0] || null - } - - async getActors( - handleOrDids: string[], - includeSoftDeleted = false, - ): Promise { - const { ref } = this.db.db.dynamic - const dids: string[] = [] - const handles: string[] = [] - const order: Record = {} - handleOrDids.forEach((item, i) => { - if (item.startsWith('did:')) { - order[item] = i - dids.push(item) - } else { - order[item.toLowerCase()] = i - handles.push(item.toLowerCase()) - } - }) - const results = await this.db.db - .selectFrom('did_handle') - .innerJoin('repo_root', 'repo_root.did', 'did_handle.did') - .if(!includeSoftDeleted, (qb) => - qb.where(notSoftDeletedClause(ref('repo_root'))), - ) - .where((qb) => { - if (dids.length) { - qb = qb.orWhere('did_handle.did', 'in', dids) - } - if (handles.length) { - qb = qb.orWhere('did_handle.handle', 'in', handles) - } - return qb - }) - .selectAll('did_handle') - .select('takedownId') - .execute() - - return results.sort((a, b) => { - const orderA = order[a.did] ?? order[a.handle.toLowerCase()] - const orderB = order[b.did] ?? order[b.handle.toLowerCase()] - return orderA - orderB - }) - } -} - -type ActorResult = DidHandle & { takedownId: number | null } diff --git a/packages/pds/src/app-view/services/actor/views.ts b/packages/pds/src/app-view/services/actor/views.ts deleted file mode 100644 index 3e4a1a91f7f..00000000000 --- a/packages/pds/src/app-view/services/actor/views.ts +++ /dev/null @@ -1,374 +0,0 @@ -import { mapDefined } from '@atproto/common' -import { cborToLexRecord } from '@atproto/repo' -import { - ProfileViewDetailed, - ProfileView, - ProfileViewBasic, -} from '../../../lexicon/types/app/bsky/actor/defs' -import { DidHandle } from '../../../db/tables/did-handle' -import Database from '../../../db' -import { ImageUriBuilder } from '../../../image/uri' -import { LabelService, getSelfLabels } from '../label' -import { GraphService } from '../graph' -import { LabelCache } from '../../../label-cache' -import { notSoftDeletedClause } from '../../../db/util' - -export class ActorViews { - constructor( - private db: Database, - private imgUriBuilder: ImageUriBuilder, - private labelCache: LabelCache, - ) {} - - services = { - label: LabelService.creator(this.labelCache)(this.db), - graph: GraphService.creator(this.imgUriBuilder)(this.db), - } - - async profilesDetailed( - results: ActorResult[], - viewer: string | null, - opts?: { skipLabels?: boolean; includeSoftDeleted?: boolean }, - ): Promise> { - if (results.length === 0) return {} - - const { ref } = this.db.db.dynamic - const { skipLabels = false, includeSoftDeleted = false } = opts ?? {} - - const dids = results.map((r) => r.did) - - const profileInfosQb = this.db.db - .selectFrom('did_handle') - .where('did_handle.did', 'in', dids) - .innerJoin('repo_root', 'repo_root.did', 'did_handle.did') - .leftJoin('profile', 'profile.creator', 'did_handle.did') - .leftJoin('profile_agg', 'profile_agg.did', 'did_handle.did') - .leftJoin('ipld_block', (join) => - join - .onRef('ipld_block.cid', '=', 'profile.cid') - .onRef('ipld_block.creator', '=', 'profile.creator'), - ) - .if(!includeSoftDeleted, (qb) => - qb.where(notSoftDeletedClause(ref('repo_root'))), - ) - .select([ - 'did_handle.did as did', - 'did_handle.handle as handle', - 'profile.uri as profileUri', - 'profile.cid as profileCid', - 'profile.displayName as displayName', - 'profile.description as description', - 'profile.avatarCid as avatarCid', - 'profile.bannerCid as bannerCid', - 'profile.indexedAt as indexedAt', - 'profile_agg.followsCount as followsCount', - 'profile_agg.followersCount as followersCount', - 'profile_agg.postsCount as postsCount', - 'ipld_block.content as profileBytes', - this.db.db - .selectFrom('follow') - .where('creator', '=', viewer ?? '') - .whereRef('subjectDid', '=', ref('did_handle.did')) - .select('uri') - .as('requesterFollowing'), - this.db.db - .selectFrom('follow') - .whereRef('creator', '=', ref('did_handle.did')) - .where('subjectDid', '=', viewer ?? '') - .select('uri') - .as('requesterFollowedBy'), - this.db.db - .selectFrom('actor_block') - .where('creator', '=', viewer ?? '') - .whereRef('subjectDid', '=', ref('did_handle.did')) - .select('uri') - .as('requesterBlocking'), - this.db.db - .selectFrom('actor_block') - .whereRef('creator', '=', ref('did_handle.did')) - .where('subjectDid', '=', viewer ?? '') - .select('uri') - .as('requesterBlockedBy'), - this.db.db - .selectFrom('mute') - .whereRef('did', '=', ref('did_handle.did')) - .where('mutedByDid', '=', viewer ?? '') - .select('did') - .as('requesterMuted'), - this.db.db - .selectFrom('list_item') - .innerJoin('list_mute', 'list_mute.listUri', 'list_item.listUri') - .where('list_mute.mutedByDid', '=', viewer ?? '') - .whereRef('list_item.subjectDid', '=', ref('did_handle.did')) - .select('list_item.listUri') - .limit(1) - .as('requesterMutedByList'), - ]) - - const [profileInfos, labels] = await Promise.all([ - profileInfosQb.execute(), - this.services.label.getLabelsForSubjects(skipLabels ? [] : dids), - ]) - - const listUris: string[] = profileInfos - .map((a) => a.requesterMutedByList) - .filter((list) => !!list) - const listViews = await this.services.graph.getListViews(listUris, viewer) - - return profileInfos.reduce((acc, cur) => { - const avatar = cur?.avatarCid - ? this.imgUriBuilder.getCommonSignedUri('avatar', cur.avatarCid) - : undefined - const banner = cur?.bannerCid - ? this.imgUriBuilder.getCommonSignedUri('banner', cur.bannerCid) - : undefined - const mutedByList = - cur.requesterMutedByList && listViews[cur.requesterMutedByList] - ? this.services.graph.formatListViewBasic( - listViews[cur.requesterMutedByList], - ) - : undefined - const actorLabels = labels[cur.did] ?? [] - const selfLabels = getSelfLabels({ - uri: cur.profileUri, - cid: cur.profileCid, - record: cur.profileBytes && cborToLexRecord(cur.profileBytes), - }) - const profile = { - did: cur.did, - handle: cur.handle, - displayName: truncateUtf8(cur?.displayName, 64) || undefined, - description: truncateUtf8(cur?.description, 256) || undefined, - avatar, - banner, - followsCount: cur?.followsCount || 0, - followersCount: cur?.followersCount || 0, - postsCount: cur?.postsCount || 0, - indexedAt: cur?.indexedAt || undefined, - viewer: { - muted: !!cur?.requesterMuted || !!cur?.requesterMutedByList, - mutedByList, - blockedBy: !!cur.requesterBlockedBy, - blocking: cur.requesterBlocking || undefined, - following: cur?.requesterFollowing || undefined, - followedBy: cur?.requesterFollowedBy || undefined, - }, - labels: skipLabels ? undefined : [...actorLabels, ...selfLabels], - } - acc[cur.did] = profile - return acc - }, {} as Record) - } - - async hydrateProfilesDetailed( - results: ActorResult[], - viewer: string, - opts?: { skipLabels?: boolean; includeSoftDeleted?: boolean }, - ): Promise { - const profiles = await this.profilesDetailed(results, viewer, opts) - return mapDefined(results, (result) => profiles[result.did]) - } - - async profileDetailed( - result: ActorResult, - viewer: string | null, - opts?: { skipLabels?: boolean; includeSoftDeleted?: boolean }, - ): Promise { - const profiles = await this.profilesDetailed([result], viewer, opts) - return profiles[result.did] ?? null - } - - async profiles( - results: ActorResult[], - viewer: string | null, - opts?: { skipLabels?: boolean; includeSoftDeleted?: boolean }, - ): Promise> { - if (results.length === 0) return {} - - const { ref } = this.db.db.dynamic - const { skipLabels = false, includeSoftDeleted = false } = opts ?? {} - const dids = results.map((r) => r.did) - - const profileInfosQb = this.db.db - .selectFrom('did_handle') - .where('did_handle.did', 'in', dids) - .innerJoin('repo_root', 'repo_root.did', 'did_handle.did') - .leftJoin('profile', 'profile.creator', 'did_handle.did') - .leftJoin('ipld_block', (join) => - join - .onRef('ipld_block.cid', '=', 'profile.cid') - .onRef('ipld_block.creator', '=', 'profile.creator'), - ) - .if(!includeSoftDeleted, (qb) => - qb.where(notSoftDeletedClause(ref('repo_root'))), - ) - .select([ - 'did_handle.did as did', - 'did_handle.handle as handle', - 'profile.uri as profileUri', - 'profile.cid as profileCid', - 'profile.displayName as displayName', - 'profile.description as description', - 'profile.avatarCid as avatarCid', - 'profile.indexedAt as indexedAt', - 'ipld_block.content as profileBytes', - this.db.db - .selectFrom('follow') - .where('creator', '=', viewer ?? '') - .whereRef('subjectDid', '=', ref('did_handle.did')) - .select('uri') - .as('requesterFollowing'), - this.db.db - .selectFrom('follow') - .whereRef('creator', '=', ref('did_handle.did')) - .where('subjectDid', '=', viewer ?? '') - .select('uri') - .as('requesterFollowedBy'), - this.db.db - .selectFrom('actor_block') - .where('creator', '=', viewer ?? '') - .whereRef('subjectDid', '=', ref('did_handle.did')) - .select('uri') - .as('requesterBlocking'), - this.db.db - .selectFrom('actor_block') - .whereRef('creator', '=', ref('did_handle.did')) - .where('subjectDid', '=', viewer ?? '') - .select('uri') - .as('requesterBlockedBy'), - this.db.db - .selectFrom('mute') - .whereRef('did', '=', ref('did_handle.did')) - .where('mutedByDid', '=', viewer ?? '') - .select('did') - .as('requesterMuted'), - this.db.db - .selectFrom('list_item') - .innerJoin('list_mute', 'list_mute.listUri', 'list_item.listUri') - .where('list_mute.mutedByDid', '=', viewer ?? '') - .whereRef('list_item.subjectDid', '=', ref('did_handle.did')) - .select('list_item.listUri') - .limit(1) - .as('requesterMutedByList'), - ]) - - const [profileInfos, labels] = await Promise.all([ - profileInfosQb.execute(), - this.services.label.getLabelsForSubjects(skipLabels ? [] : dids), - ]) - - const listUris: string[] = profileInfos - .map((a) => a.requesterMutedByList) - .filter((list) => !!list) - const listViews = await this.services.graph.getListViews(listUris, viewer) - - return profileInfos.reduce((acc, cur) => { - const avatar = cur.avatarCid - ? this.imgUriBuilder.getCommonSignedUri('avatar', cur.avatarCid) - : undefined - const mutedByList = - cur.requesterMutedByList && listViews[cur.requesterMutedByList] - ? this.services.graph.formatListViewBasic( - listViews[cur.requesterMutedByList], - ) - : undefined - const actorLabels = labels[cur.did] ?? [] - const selfLabels = getSelfLabels({ - uri: cur.profileUri, - cid: cur.profileCid, - record: cur.profileBytes && cborToLexRecord(cur.profileBytes), - }) - const profile = { - did: cur.did, - handle: cur.handle, - displayName: truncateUtf8(cur?.displayName, 64) || undefined, - description: truncateUtf8(cur?.description, 256) || undefined, - avatar, - indexedAt: cur?.indexedAt || undefined, - viewer: { - muted: !!cur?.requesterMuted || !!cur?.requesterMutedByList, - mutedByList, - blockedBy: !!cur.requesterBlockedBy, - blocking: cur.requesterBlocking || undefined, - following: cur?.requesterFollowing || undefined, - followedBy: cur?.requesterFollowedBy || undefined, - }, - labels: skipLabels ? undefined : [...actorLabels, ...selfLabels], - } - acc[cur.did] = profile - return acc - }, {} as Record) - } - - async hydrateProfiles( - results: ActorResult[], - viewer: string | null, - opts?: { skipLabels?: boolean; includeSoftDeleted?: boolean }, - ): Promise { - const profiles = await this.profiles(results, viewer, opts) - return mapDefined(results, (result) => profiles[result.did]) - } - - async profile( - result: ActorResult, - viewer: string | null, - opts?: { skipLabels?: boolean; includeSoftDeleted?: boolean }, - ): Promise { - const profiles = await this.profiles([result], viewer, opts) - return profiles[result.did] ?? null - } - - // @NOTE keep in sync with feedService.getActorViews() - async profilesBasic( - results: ActorResult[], - viewer: string, - opts?: { skipLabels?: boolean; includeSoftDeleted?: boolean }, - ): Promise> { - if (results.length === 0) return {} - const profiles = await this.profiles(results, viewer, opts) - return Object.values(profiles).reduce((acc, cur) => { - const profile = { - did: cur.did, - handle: cur.handle, - displayName: truncateUtf8(cur.displayName, 64) || undefined, - avatar: cur.avatar, - viewer: cur.viewer, - labels: cur.labels, - } - acc[cur.did] = profile - return acc - }, {} as Record) - } - - async hydrateProfilesBasic( - results: ActorResult[], - viewer: string, - opts?: { skipLabels?: boolean; includeSoftDeleted?: boolean }, - ): Promise { - const profiles = await this.profilesBasic(results, viewer, opts) - return mapDefined(results, (result) => profiles[result.did]) - } - - async profileBasic( - result: ActorResult, - viewer: string, - opts?: { skipLabels?: boolean; includeSoftDeleted?: boolean }, - ): Promise { - const profiles = await this.profilesBasic([result], viewer, opts) - return profiles[result.did] ?? null - } -} - -type ActorResult = DidHandle - -function truncateUtf8(str: string | null | undefined, length: number) { - if (!str) return str - const encoder = new TextEncoder() - const utf8 = encoder.encode(str) - if (utf8.length > length) { - const decoder = new TextDecoder('utf-8', { fatal: false }) - const truncated = utf8.slice(0, length) - return decoder.decode(truncated).replace(/\uFFFD$/, '') - } - return str -} diff --git a/packages/pds/src/app-view/services/feed/index.ts b/packages/pds/src/app-view/services/feed/index.ts deleted file mode 100644 index a7f153b7a76..00000000000 --- a/packages/pds/src/app-view/services/feed/index.ts +++ /dev/null @@ -1,685 +0,0 @@ -import { sql } from 'kysely' -import { AtUri } from '@atproto/syntax' -import { dedupeStrs } from '@atproto/common' -import { cborToLexRecord } from '@atproto/repo' -import Database from '../../../db' -import { countAll, notSoftDeletedClause, valuesList } from '../../../db/util' -import { ImageUriBuilder } from '../../../image/uri' -import { ids } from '../../../lexicon/lexicons' -import { - Record as PostRecord, - isRecord as isPostRecord, -} from '../../../lexicon/types/app/bsky/feed/post' -import { isMain as isEmbedImages } from '../../../lexicon/types/app/bsky/embed/images' -import { isMain as isEmbedExternal } from '../../../lexicon/types/app/bsky/embed/external' -import { - isMain as isEmbedRecord, - isViewRecord, -} from '../../../lexicon/types/app/bsky/embed/record' -import { isMain as isEmbedRecordWithMedia } from '../../../lexicon/types/app/bsky/embed/recordWithMedia' -import { FeedViewPost } from '../../../lexicon/types/app/bsky/feed/defs' -import { - ActorInfoMap, - PostInfoMap, - FeedItemType, - FeedRow, - FeedGenInfoMap, - PostViews, - PostEmbedViews, - RecordEmbedViewRecordMap, - PostBlocksMap, - RecordEmbedViewRecord, - FeedHydrationOptions, - kSelfLabels, -} from './types' -import { LabelService, Labels, getSelfLabels } from '../label' -import { ActorService } from '../actor' -import { GraphService } from '../graph' -import { FeedViews } from './views' -import { LabelCache } from '../../../label-cache' - -export * from './types' - -export class FeedService { - constructor( - public db: Database, - public imgUriBuilder: ImageUriBuilder, - public labelCache: LabelCache, - ) {} - - static creator(imgUriBuilder: ImageUriBuilder, labelCache: LabelCache) { - return (db: Database) => new FeedService(db, imgUriBuilder, labelCache) - } - - views = new FeedViews(this.db, this.imgUriBuilder) - services = { - label: LabelService.creator(this.labelCache)(this.db), - actor: ActorService.creator(this.imgUriBuilder, this.labelCache)(this.db), - graph: GraphService.creator(this.imgUriBuilder)(this.db), - } - - selectPostQb() { - return this.db.db - .selectFrom('post') - .select([ - sql`${'post'}`.as('type'), - 'post.uri as uri', - 'post.cid as cid', - 'post.uri as postUri', - 'post.creator as originatorDid', - 'post.creator as postAuthorDid', - 'post.replyParent as replyParent', - 'post.replyRoot as replyRoot', - 'post.indexedAt as sortAt', - ]) - } - - selectFeedItemQb() { - return this.db.db - .selectFrom('feed_item') - .innerJoin('post', 'post.uri', 'feed_item.postUri') - .selectAll('feed_item') - .select([ - 'post.replyRoot', - 'post.replyParent', - 'post.creator as postAuthorDid', - ]) - } - - selectFeedGeneratorQb(requester: string | null) { - const { ref } = this.db.db.dynamic - return this.db.db - .selectFrom('feed_generator') - .innerJoin('did_handle', 'did_handle.did', 'feed_generator.creator') - .innerJoin( - 'repo_root as creator_repo', - 'creator_repo.did', - 'feed_generator.creator', - ) - .innerJoin('record', 'record.uri', 'feed_generator.uri') - .selectAll() - .where(notSoftDeletedClause(ref('creator_repo'))) - .where(notSoftDeletedClause(ref('record'))) - .select((qb) => - qb - .selectFrom('like') - .whereRef('like.subject', '=', 'feed_generator.uri') - .select(countAll.as('count')) - .as('likeCount'), - ) - .select((qb) => - qb - .selectFrom('like') - .where('like.creator', '=', requester ?? '') - .whereRef('like.subject', '=', 'feed_generator.uri') - .select('uri') - .as('viewerLike'), - ) - } - - // @NOTE keep in sync with actorService.views.profile() - async getActorInfos( - dids: string[], - requester: string | null, - opts?: { skipLabels?: boolean; includeSoftDeleted?: boolean }, // @NOTE used by hydrateFeed() to batch label hydration - ): Promise { - if (dids.length < 1) return {} - const { ref } = this.db.db.dynamic - const { skipLabels = false, includeSoftDeleted = false } = opts ?? {} - const [actors, labels] = await Promise.all([ - this.db.db - .selectFrom('did_handle') - .where('did_handle.did', 'in', dids) - .innerJoin('repo_root', 'repo_root.did', 'did_handle.did') - .leftJoin('profile', 'profile.creator', 'did_handle.did') - .leftJoin('ipld_block', (join) => - join - .onRef('ipld_block.cid', '=', 'profile.cid') - .onRef('ipld_block.creator', '=', 'profile.creator'), - ) - .selectAll('did_handle') - .if(!includeSoftDeleted, (qb) => - qb.where(notSoftDeletedClause(ref('repo_root'))), - ) - .select([ - 'profile.uri as profileUri', - 'profile.cid as profileCid', - 'profile.displayName as displayName', - 'profile.description as description', - 'profile.avatarCid as avatarCid', - 'profile.indexedAt as indexedAt', - 'ipld_block.content as profileBytes', - this.db.db - .selectFrom('follow') - .where('creator', '=', requester ?? '') - .whereRef('subjectDid', '=', ref('did_handle.did')) - .select('uri') - .as('requesterFollowing'), - this.db.db - .selectFrom('follow') - .whereRef('creator', '=', ref('did_handle.did')) - .where('subjectDid', '=', requester ?? '') - .select('uri') - .as('requesterFollowedBy'), - this.db.db - .selectFrom('actor_block') - .where('creator', '=', requester ?? '') - .whereRef('subjectDid', '=', ref('did_handle.did')) - .select('uri') - .as('requesterBlocking'), - this.db.db - .selectFrom('actor_block') - .whereRef('creator', '=', ref('did_handle.did')) - .where('subjectDid', '=', requester ?? '') - .select('uri') - .as('requesterBlockedBy'), - this.db.db - .selectFrom('mute') - .whereRef('did', '=', ref('did_handle.did')) - .where('mutedByDid', '=', requester ?? '') - .select('did') - .as('requesterMuted'), - this.db.db - .selectFrom('list_item') - .innerJoin('list_mute', 'list_mute.listUri', 'list_item.listUri') - .where('list_mute.mutedByDid', '=', requester ?? '') - .whereRef('list_item.subjectDid', '=', ref('did_handle.did')) - .select('list_item.listUri') - .limit(1) - .as('requesterMutedByList'), - ]) - .execute(), - this.services.label.getLabelsForSubjects(skipLabels ? [] : dids), - ]) - const listUris: string[] = actors - .map((a) => a.requesterMutedByList) - .filter((list) => !!list) - const listViews = await this.services.graph.getListViews( - listUris, - requester, - ) - return actors.reduce((acc, cur) => { - const avatar = cur.avatarCid - ? this.imgUriBuilder.getCommonSignedUri('avatar', cur.avatarCid) - : undefined - const mutedByList = - cur.requesterMutedByList && listViews[cur.requesterMutedByList] - ? this.services.graph.formatListViewBasic( - listViews[cur.requesterMutedByList], - ) - : undefined - const actorLabels = labels[cur.did] ?? [] - const selfLabels = getSelfLabels({ - uri: cur.profileUri, - cid: cur.profileCid, - record: cur.profileBytes && cborToLexRecord(cur.profileBytes), - }) - return { - ...acc, - [cur.did]: { - did: cur.did, - handle: cur.handle, - displayName: truncateUtf8(cur.displayName, 64) || undefined, - avatar, - viewer: { - muted: !!cur?.requesterMuted || !!cur?.requesterMutedByList, - mutedByList, - blockedBy: !!cur?.requesterBlockedBy, - blocking: cur?.requesterBlocking || undefined, - following: cur?.requesterFollowing || undefined, - followedBy: cur?.requesterFollowedBy || undefined, - }, - labels: skipLabels ? undefined : [...actorLabels, ...selfLabels], - [kSelfLabels]: selfLabels, - }, - } - }, {} as ActorInfoMap) - } - - async getPostInfos( - postUris: string[], - requester: string | null, - options?: Pick, - ): Promise { - if (postUris.length < 1) return {} - const db = this.db.db - const { ref } = db.dynamic - let postsQb = db - .selectFrom('post') - .where('post.uri', 'in', postUris) - .leftJoin('post_agg', 'post_agg.uri', 'post.uri') - .innerJoin('ipld_block', (join) => - join - .onRef('ipld_block.cid', '=', 'post.cid') - .onRef('ipld_block.creator', '=', 'post.creator'), - ) - .innerJoin('repo_root', 'repo_root.did', 'post.creator') - .innerJoin('record', 'record.uri', 'post.uri') - - if (!options?.includeSoftDeleted) { - postsQb = postsQb - .where(notSoftDeletedClause(ref('repo_root'))) // Ensures post reply parent/roots get omitted from views when taken down - .where(notSoftDeletedClause(ref('record'))) - } - - const posts = await postsQb - .select([ - 'post.uri as uri', - 'post.cid as cid', - 'post.creator as creator', - 'post.indexedAt as indexedAt', - 'ipld_block.content as recordBytes', - 'post_agg.likeCount as likeCount', - 'post_agg.repostCount as repostCount', - 'post_agg.replyCount as replyCount', - 'record.takedownId as takedownId', - db - .selectFrom('repost') - .where('creator', '=', requester ?? '') - .whereRef('subject', '=', ref('post.uri')) - .select('uri') - .as('requesterRepost'), - db - .selectFrom('like') - .where('creator', '=', requester ?? '') - .whereRef('subject', '=', ref('post.uri')) - .select('uri') - .as('requesterLike'), - ]) - .execute() - return posts.reduce((acc, cur) => { - const { recordBytes, ...post } = cur - return Object.assign(acc, { - [post.uri]: { - ...post, - record: cborToLexRecord(recordBytes), - }, - }) - }, {} as PostInfoMap) - } - - async getFeedGeneratorInfos( - generatorUris: string[], - requester: string | null, - ) { - if (generatorUris.length < 1) return {} - const feedGens = await this.selectFeedGeneratorQb(requester) - .where('feed_generator.uri', 'in', generatorUris) - .execute() - return feedGens.reduce( - (acc, cur) => ({ - ...acc, - [cur.uri]: cur, - }), - {} as FeedGenInfoMap, - ) - } - - async getPostViews( - postUris: string[], - requester: string, - precomputed?: { - actors?: ActorInfoMap - posts?: PostInfoMap - embeds?: PostEmbedViews - blocks?: PostBlocksMap - labels?: Labels - }, - ): Promise { - const uris = dedupeStrs(postUris) - const dids = dedupeStrs(postUris.map((uri) => new AtUri(uri).hostname)) - - const [actors, posts, labels] = await Promise.all([ - precomputed?.actors ?? - this.getActorInfos(dids, requester, { skipLabels: true }), - precomputed?.posts ?? this.getPostInfos(uris, requester), - precomputed?.labels ?? - this.services.label.getLabelsForSubjects([...uris, ...dids]), - ]) - const blocks = precomputed?.blocks ?? (await this.blocksForPosts(posts)) - const embeds = - precomputed?.embeds ?? - (await this.embedsForPosts(posts, blocks, requester)) - - return uris.reduce((acc, cur) => { - const view = this.views.formatPostView(cur, actors, posts, embeds, labels) - if (view) { - acc[cur] = view - } - return acc - }, {} as PostViews) - } - - async hydrateFeed( - items: FeedRow[], - requester: string | null, - options?: FeedHydrationOptions, - ): Promise { - const actorDids = new Set() - const postUris = new Set() - for (const item of items) { - actorDids.add(item.postAuthorDid) - postUris.add(item.postUri) - if (item.postAuthorDid !== item.originatorDid) { - actorDids.add(item.originatorDid) - } - if (item.replyParent) { - postUris.add(item.replyParent) - actorDids.add(new AtUri(item.replyParent).hostname) - } - if (item.replyRoot) { - postUris.add(item.replyRoot) - actorDids.add(new AtUri(item.replyRoot).hostname) - } - } - - const [actors, posts, labels] = await Promise.all([ - this.getActorInfos(Array.from(actorDids), requester, { - skipLabels: true, - includeSoftDeleted: options?.includeSoftDeleted, - }), - this.getPostInfos(Array.from(postUris), requester, options), - this.services.label.getLabelsForSubjects([...postUris, ...actorDids]), - ]) - const blocks = await this.blocksForPosts(posts) - const embeds = await this.embedsForPosts(posts, blocks, requester) - - return this.views.formatFeed( - items, - actors, - posts, - embeds, - labels, - blocks, - options, - ) - } - - // applies blocks for visibility to third-parties (i.e. based on post content) - async blocksForPosts(posts: PostInfoMap): Promise { - const relationships: RelationshipPair[] = [] - const byPost: Record = {} - const didFromUri = (uri) => new AtUri(uri).host - for (const post of Object.values(posts)) { - // skip posts that we can't process or appear to already have been processed - if (!isPostRecord(post.record)) continue - if (byPost[post.uri]) continue - byPost[post.uri] = {} - // 3p block for replies - const parentUri = post.record.reply?.parent.uri - const parentDid = parentUri ? didFromUri(parentUri) : null - // 3p block for record embeds - const embedUris = nestedRecordUris([post.record]) - // gather actor relationships among posts - if (parentDid) { - const pair: RelationshipPair = [post.creator, parentDid] - relationships.push(pair) - byPost[post.uri].reply = pair - } - for (const embedUri of embedUris) { - const pair: RelationshipPair = [post.creator, didFromUri(embedUri)] - relationships.push(pair) - byPost[post.uri].embed = pair - } - } - // compute block state from all actor relationships among posts - const blockSet = await this.getBlockSet(relationships) - if (blockSet.empty()) return {} - const result: PostBlocksMap = {} - Object.entries(byPost).forEach(([uri, block]) => { - if (block.embed && blockSet.has(block.embed)) { - result[uri] ??= {} - result[uri].embed = true - } - if (block.reply && blockSet.has(block.reply)) { - result[uri] ??= {} - result[uri].reply = true - } - }) - return result - } - - private async getBlockSet(relationships: RelationshipPair[]) { - const { ref } = this.db.db.dynamic - const blockSet = new RelationshipSet() - if (!relationships.length) return blockSet - const relationshipSet = new RelationshipSet() - relationships.forEach((pair) => relationshipSet.add(pair)) - // compute actual block set from all actor relationships - const blockRows = await this.db.db - .selectFrom('actor_block') - .select(['creator', 'subjectDid']) // index-only columns - .where( - sql`(${ref('creator')}, ${ref('subjectDid')})`, - 'in', - valuesList( - relationshipSet.listAllPairs().map(([a, b]) => sql`${a}, ${b}`), - ), - ) - .execute() - blockRows.forEach((r) => blockSet.add([r.creator, r.subjectDid])) - return blockSet - } - - async embedsForPosts( - postInfos: PostInfoMap, - blocks: PostBlocksMap, - requester: string | null, - depth = 0, - ) { - const postMap = postRecordsFromInfos(postInfos) - const posts = Object.values(postMap) - if (posts.length < 1) { - return {} - } - const recordEmbedViews = - depth > 1 ? {} : await this.nestedRecordViews(posts, requester, depth) - - const postEmbedViews: PostEmbedViews = {} - for (const [uri, post] of Object.entries(postMap)) { - if (!post.embed) continue - if (isEmbedImages(post.embed)) { - postEmbedViews[uri] = this.views.imagesEmbedView(post.embed) - } else if (isEmbedExternal(post.embed)) { - postEmbedViews[uri] = this.views.externalEmbedView(post.embed) - } else if (isEmbedRecord(post.embed)) { - if (!recordEmbedViews[post.embed.record.uri]) continue - postEmbedViews[uri] = { - $type: 'app.bsky.embed.record#view', - record: applyEmbedBlock( - uri, - blocks, - recordEmbedViews[post.embed.record.uri], - ), - } - } else if (isEmbedRecordWithMedia(post.embed)) { - const embedRecordView = recordEmbedViews[post.embed.record.record.uri] - if (!embedRecordView) continue - const formatted = this.views.getRecordWithMediaEmbedView( - post.embed, - applyEmbedBlock(uri, blocks, embedRecordView), - ) - if (formatted) { - postEmbedViews[uri] = formatted - } - } - } - return postEmbedViews - } - - async nestedRecordViews( - posts: PostRecord[], - requester: string | null, - depth: number, - ): Promise { - const nestedUris = nestedRecordUris(posts) - if (nestedUris.length < 1) return {} - const nestedPostUris: string[] = [] - const nestedFeedGenUris: string[] = [] - const nestedListUris: string[] = [] - const nestedDidsSet = new Set() - for (const uri of nestedUris) { - const parsed = new AtUri(uri) - nestedDidsSet.add(parsed.hostname) - if (parsed.collection === ids.AppBskyFeedPost) { - nestedPostUris.push(uri) - } else if (parsed.collection === ids.AppBskyFeedGenerator) { - nestedFeedGenUris.push(uri) - } else if (parsed.collection === ids.AppBskyGraphList) { - nestedListUris.push(uri) - } - } - const nestedDids = [...nestedDidsSet] - const [postInfos, actorInfos, labelViews, feedGenInfos, listViews] = - await Promise.all([ - this.getPostInfos(nestedPostUris, requester), - this.getActorInfos(nestedDids, requester, { skipLabels: true }), - this.services.label.getLabelsForSubjects([ - ...nestedPostUris, - ...nestedDids, - ]), - this.getFeedGeneratorInfos(nestedFeedGenUris, requester), - this.services.graph.getListViews(nestedListUris, requester), - ]) - const deepBlocks = await this.blocksForPosts(postInfos) - const deepEmbedViews = await this.embedsForPosts( - postInfos, - deepBlocks, - requester, - depth + 1, - ) - const recordEmbedViews: RecordEmbedViewRecordMap = {} - for (const uri of nestedUris) { - const collection = new AtUri(uri).collection - if (collection === ids.AppBskyFeedGenerator && feedGenInfos[uri]) { - recordEmbedViews[uri] = { - $type: 'app.bsky.feed.defs#generatorView', - ...this.views.formatFeedGeneratorView( - feedGenInfos[uri], - actorInfos, - labelViews, - ), - } - } else if (collection === ids.AppBskyGraphList && listViews[uri]) { - recordEmbedViews[uri] = { - $type: 'app.bsky.graph.defs#listView', - ...this.services.graph.formatListView(listViews[uri], actorInfos), - } - } else if (collection === ids.AppBskyFeedPost && postInfos[uri]) { - const formatted = this.views.formatPostView( - uri, - actorInfos, - postInfos, - deepEmbedViews, - labelViews, - ) - recordEmbedViews[uri] = this.views.getRecordEmbedView( - uri, - formatted, - depth > 0, - ) - } else { - recordEmbedViews[uri] = { - $type: 'app.bsky.embed.record#viewNotFound', - uri, - notFound: true, - } - } - } - return recordEmbedViews - } -} - -function truncateUtf8(str: string | null | undefined, length: number) { - if (!str) return str - const encoder = new TextEncoder() - const utf8 = encoder.encode(str) - if (utf8.length > length) { - const decoder = new TextDecoder('utf-8', { fatal: false }) - const truncated = utf8.slice(0, length) - return decoder.decode(truncated).replace(/\uFFFD$/, '') - } - return str -} - -const postRecordsFromInfos = ( - infos: PostInfoMap, -): { [uri: string]: PostRecord } => { - const records: { [uri: string]: PostRecord } = {} - for (const [uri, info] of Object.entries(infos)) { - if (isPostRecord(info.record)) { - records[uri] = info.record - } - } - return records -} - -const nestedRecordUris = (posts: PostRecord[]): string[] => { - const uris: string[] = [] - for (const post of posts) { - if (!post.embed) continue - if (isEmbedRecord(post.embed)) { - uris.push(post.embed.record.uri) - } else if (isEmbedRecordWithMedia(post.embed)) { - uris.push(post.embed.record.record.uri) - } else { - continue - } - } - return uris -} - -type PostRelationships = { reply?: RelationshipPair; embed?: RelationshipPair } - -type RelationshipPair = [didA: string, didB: string] - -class RelationshipSet { - index = new Map>() - add([didA, didB]: RelationshipPair) { - const didAIdx = this.index.get(didA) ?? new Set() - const didBIdx = this.index.get(didB) ?? new Set() - if (!this.index.has(didA)) this.index.set(didA, didAIdx) - if (!this.index.has(didB)) this.index.set(didB, didBIdx) - didAIdx.add(didB) - didBIdx.add(didA) - } - has([didA, didB]: RelationshipPair) { - return !!this.index.get(didA)?.has(didB) - } - listAllPairs() { - const pairs: RelationshipPair[] = [] - for (const [didA, didBIdx] of this.index.entries()) { - for (const didB of didBIdx) { - pairs.push([didA, didB]) - } - } - return pairs - } - empty() { - return this.index.size === 0 - } -} - -function applyEmbedBlock( - uri: string, - blocks: PostBlocksMap, - view: RecordEmbedViewRecord, -): RecordEmbedViewRecord { - if (isViewRecord(view) && blocks[uri]?.embed) { - return { - $type: 'app.bsky.embed.record#viewBlocked', - uri: view.uri, - blocked: true, - author: { - did: view.author.did, - viewer: view.author.viewer - ? { - blockedBy: view.author.viewer?.blockedBy, - blocking: view.author.viewer?.blocking, - } - : undefined, - }, - } - } - return view -} diff --git a/packages/pds/src/app-view/services/feed/types.ts b/packages/pds/src/app-view/services/feed/types.ts deleted file mode 100644 index 998caa6e32c..00000000000 --- a/packages/pds/src/app-view/services/feed/types.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { RepoRecord } from '@atproto/lexicon' -import { View as ImagesEmbedView } from '../../../lexicon/types/app/bsky/embed/images' -import { View as ExternalEmbedView } from '../../../lexicon/types/app/bsky/embed/external' -import { - ViewBlocked, - ViewNotFound, - ViewRecord, - View as RecordEmbedView, -} from '../../../lexicon/types/app/bsky/embed/record' -import { View as RecordWithMediaEmbedView } from '../../../lexicon/types/app/bsky/embed/recordWithMedia' -import { - BlockedPost, - GeneratorView, - NotFoundPost, - PostView, -} from '../../../lexicon/types/app/bsky/feed/defs' -import { Label } from '../../../lexicon/types/com/atproto/label/defs' -import { FeedGenerator } from '../../db/tables/feed-generator' -import { ListView } from '../../../lexicon/types/app/bsky/graph/defs' - -export type PostEmbedViews = { - [uri: string]: PostEmbedView -} - -export type PostEmbedView = - | ImagesEmbedView - | ExternalEmbedView - | RecordEmbedView - | RecordWithMediaEmbedView - -export type PostViews = { [uri: string]: PostView } - -export type PostInfo = { - uri: string - cid: string - creator: string - record: RepoRecord - indexedAt: string - likeCount: number | null - repostCount: number | null - replyCount: number | null - requesterRepost: string | null - requesterLike: string | null - takedownId: number | null -} - -export type PostInfoMap = { [uri: string]: PostInfo } - -export type PostBlocksMap = { - [uri: string]: { reply?: boolean; embed?: boolean } -} - -export const kSelfLabels = Symbol('selfLabels') - -export type ActorInfo = { - did: string - handle: string - displayName?: string - avatar?: string - viewer?: { - muted?: boolean - blockedBy?: boolean - blocking?: string - following?: string - followedBy?: string - } - labels?: Label[] - // allows threading self-labels through if they are going to be applied later, i.e. when using skipLabels option. - [kSelfLabels]?: Label[] -} -export type ActorInfoMap = { [did: string]: ActorInfo } - -export type FeedGenInfo = FeedGenerator & { - likeCount: number - viewerLike: string | null -} - -export type FeedGenInfoMap = { [uri: string]: FeedGenInfo } - -export type FeedItemType = 'post' | 'repost' - -export type FeedRow = { - type: FeedItemType - uri: string - cid: string - postUri: string - postAuthorDid: string - originatorDid: string - replyParent: string | null - replyRoot: string | null - sortAt: string -} -export type FeedHydrationOptions = { - includeSoftDeleted?: boolean - usePostViewUnion?: boolean -} - -export type MaybePostView = PostView | NotFoundPost | BlockedPost - -export type RecordEmbedViewRecord = - | ViewRecord - | ViewNotFound - | ViewBlocked - | GeneratorView - | ListView - -export type RecordEmbedViewRecordMap = { [uri: string]: RecordEmbedViewRecord } diff --git a/packages/pds/src/app-view/services/feed/util.ts b/packages/pds/src/app-view/services/feed/util.ts deleted file mode 100644 index cedc82df0e9..00000000000 --- a/packages/pds/src/app-view/services/feed/util.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { sql } from 'kysely' -import DatabaseSchema from '../../../db/database-schema' - -export const getDescendentsQb = ( - db: DatabaseSchema, - opts: { - uri: string - depth: number // required, protects against cycles - }, -) => { - const { uri, depth } = opts - const query = db.withRecursive('descendent(uri, depth)', (cte) => { - return cte - .selectFrom('post') - .select(['post.uri as uri', sql`1`.as('depth')]) - .where(sql`1`, '<=', depth) - .where('replyParent', '=', uri) - .unionAll( - cte - .selectFrom('post') - .innerJoin('descendent', 'descendent.uri', 'post.replyParent') - .where('descendent.depth', '<', depth) - .select([ - 'post.uri as uri', - sql`descendent.depth + 1`.as('depth'), - ]), - ) - }) - return query -} - -export const getAncestorsAndSelfQb = ( - db: DatabaseSchema, - opts: { - uri: string - parentHeight: number // required, protects against cycles - }, -) => { - const { uri, parentHeight } = opts - const query = db.withRecursive( - 'ancestor(uri, ancestorUri, height)', - (cte) => { - return cte - .selectFrom('post') - .select([ - 'post.uri as uri', - 'post.replyParent as ancestorUri', - sql`0`.as('height'), - ]) - .where('uri', '=', uri) - .unionAll( - cte - .selectFrom('post') - .innerJoin('ancestor', 'ancestor.ancestorUri', 'post.uri') - .where('ancestor.height', '<', parentHeight) - .select([ - 'post.uri as uri', - 'post.replyParent as ancestorUri', - sql`ancestor.height + 1`.as('height'), - ]), - ) - }, - ) - return query -} diff --git a/packages/pds/src/app-view/services/feed/views.ts b/packages/pds/src/app-view/services/feed/views.ts deleted file mode 100644 index cfb4d158e2b..00000000000 --- a/packages/pds/src/app-view/services/feed/views.ts +++ /dev/null @@ -1,344 +0,0 @@ -import Database from '../../../db' -import { - FeedViewPost, - GeneratorView, - PostView, -} from '../../../lexicon/types/app/bsky/feed/defs' -import { - Main as EmbedImages, - isMain as isEmbedImages, - View as EmbedImagesView, -} from '../../../lexicon/types/app/bsky/embed/images' -import { - Main as EmbedExternal, - isMain as isEmbedExternal, - View as EmbedExternalView, -} from '../../../lexicon/types/app/bsky/embed/external' -import { Main as EmbedRecordWithMedia } from '../../../lexicon/types/app/bsky/embed/recordWithMedia' -import { - ViewBlocked, - ViewNotFound, - ViewRecord, -} from '../../../lexicon/types/app/bsky/embed/record' -import { - ActorInfoMap, - PostEmbedViews, - FeedGenInfo, - FeedRow, - MaybePostView, - PostInfoMap, - RecordEmbedViewRecord, - PostBlocksMap, - FeedHydrationOptions, - kSelfLabels, -} from './types' -import { Labels, getSelfLabels } from '../label' -import { ImageUriBuilder } from '../../../image/uri' - -export * from './types' - -export class FeedViews { - constructor(public db: Database, public imgUriBuilder: ImageUriBuilder) {} - - static creator(imgUriBuilder: ImageUriBuilder) { - return (db: Database) => new FeedViews(db, imgUriBuilder) - } - - formatFeedGeneratorView( - info: FeedGenInfo, - profiles: ActorInfoMap, - labels?: Labels, - ): GeneratorView { - const profile = profiles[info.creator] - if (profile && !profile.labels) { - // If the creator labels are not hydrated yet, attempt to pull them - // from labels: e.g. compatible with embedsForPosts() batching label hydration. - const profileLabels = labels?.[info.creator] ?? [] - const profileSelfLabels = profile[kSelfLabels] ?? [] - profile.labels = [...profileLabels, ...profileSelfLabels] - } - return { - uri: info.uri, - cid: info.cid, - did: info.feedDid, - creator: profile, - displayName: info.displayName ?? undefined, - description: info.description ?? undefined, - descriptionFacets: info.descriptionFacets - ? JSON.parse(info.descriptionFacets) - : undefined, - avatar: info.avatarCid - ? this.imgUriBuilder.getCommonSignedUri('avatar', info.avatarCid) - : undefined, - likeCount: info.likeCount, - viewer: { - like: info.viewerLike ?? undefined, - }, - indexedAt: info.indexedAt, - } - } - - formatFeed( - items: FeedRow[], - actors: ActorInfoMap, - posts: PostInfoMap, - embeds: PostEmbedViews, - labels: Labels, - blocks: PostBlocksMap, - opts?: FeedHydrationOptions, - ): FeedViewPost[] { - const feed: FeedViewPost[] = [] - for (const item of items) { - const post = this.formatPostView( - item.postUri, - actors, - posts, - embeds, - labels, - opts, - ) - // skip over not found & blocked posts - if (!post || blocks[post.uri]?.reply) { - continue - } - const feedPost = { post } - if (item.type === 'repost') { - const originator = actors[item.originatorDid] - // skip over reposts where we don't have reposter profile - if (!originator) { - continue - } else { - const originatorLabels = labels[item.originatorDid] ?? [] - const originatorSelfLabels = originator[kSelfLabels] ?? [] - feedPost['reason'] = { - $type: 'app.bsky.feed.defs#reasonRepost', - by: { - ...originator, - labels: [...originatorLabels, ...originatorSelfLabels], - }, - indexedAt: item.sortAt, - } - } - } - if (item.replyParent && item.replyRoot) { - const replyParent = this.formatMaybePostView( - item.replyParent, - actors, - posts, - embeds, - labels, - blocks, - opts, - ) - const replyRoot = this.formatMaybePostView( - item.replyRoot, - actors, - posts, - embeds, - labels, - blocks, - opts, - ) - if (replyRoot && replyParent) { - feedPost['reply'] = { - root: replyRoot, - parent: replyParent, - } - } - } - feed.push(feedPost) - } - return feed - } - - formatPostView( - uri: string, - actors: ActorInfoMap, - posts: PostInfoMap, - embeds: PostEmbedViews, - labels: Labels, - opts?: Pick, - ): PostView | undefined { - const post = posts[uri] - const author = actors[post?.creator] - if (!post || !author) return undefined - // If the author labels are not hydrated yet, attempt to pull them - // from labels: e.g. compatible with hydrateFeed() batching label hydration. - const authorLabels = labels[author.did] ?? [] - const authorSelfLabels = author[kSelfLabels] ?? [] - author.labels ??= [...authorLabels, ...authorSelfLabels] - const postLabels = labels[uri] ?? [] - const postSelfLabels = getSelfLabels({ - uri: post.uri, - cid: post.cid, - record: post.record, - }) - return { - uri: post.uri, - cid: post.cid, - author: author, - takedownId: opts?.includeSoftDeleted - ? post.takedownId ?? null - : undefined, - record: post.record, - embed: embeds[uri], - replyCount: post.replyCount ?? 0, - repostCount: post.repostCount ?? 0, - likeCount: post.likeCount ?? 0, - indexedAt: post.indexedAt, - viewer: { - repost: post.requesterRepost ?? undefined, - like: post.requesterLike ?? undefined, - }, - labels: [...postLabels, ...postSelfLabels], - } - } - - formatMaybePostView( - uri: string, - actors: ActorInfoMap, - posts: PostInfoMap, - embeds: PostEmbedViews, - labels: Labels, - blocks: PostBlocksMap, - opts?: FeedHydrationOptions, - ): MaybePostView | undefined { - const post = this.formatPostView(uri, actors, posts, embeds, labels, opts) - if (!post) { - if (!opts?.usePostViewUnion) return - return this.notFoundPost(uri) - } - if ( - post.author.viewer?.blockedBy || - post.author.viewer?.blocking || - blocks[uri]?.reply - ) { - if (!opts?.usePostViewUnion) return - return this.blockedPost(post) - } - return { - $type: 'app.bsky.feed.defs#postView', - ...post, - } - } - - blockedPost(post: PostView) { - return { - $type: 'app.bsky.feed.defs#blockedPost', - uri: post.uri, - blocked: true as const, - author: { - did: post.author.did, - viewer: post.author.viewer - ? { - blockedBy: post.author.viewer?.blockedBy, - blocking: post.author.viewer?.blocking, - } - : undefined, - }, - } - } - - notFoundPost(uri: string) { - return { - $type: 'app.bsky.feed.defs#notFoundPost', - uri: uri, - notFound: true as const, - } - } - - imagesEmbedView(embed: EmbedImages) { - const imgViews = embed.images.map((img) => ({ - thumb: this.imgUriBuilder.getCommonSignedUri( - 'feed_thumbnail', - img.image.ref, - ), - fullsize: this.imgUriBuilder.getCommonSignedUri( - 'feed_fullsize', - img.image.ref, - ), - alt: img.alt, - aspectRatio: img.aspectRatio, - })) - return { - $type: 'app.bsky.embed.images#view', - images: imgViews, - } - } - - externalEmbedView(embed: EmbedExternal) { - const { uri, title, description, thumb } = embed.external - return { - $type: 'app.bsky.embed.external#view', - external: { - uri, - title, - description, - thumb: thumb - ? this.imgUriBuilder.getCommonSignedUri('feed_thumbnail', thumb.ref) - : undefined, - }, - } - } - - getRecordEmbedView( - uri: string, - post?: PostView, - omitEmbeds = false, - ): (ViewRecord | ViewNotFound | ViewBlocked) & { $type: string } { - if (!post) { - return { - $type: 'app.bsky.embed.record#viewNotFound', - uri, - notFound: true, - } - } - if (post.author.viewer?.blocking || post.author.viewer?.blockedBy) { - return { - $type: 'app.bsky.embed.record#viewBlocked', - uri, - blocked: true, - author: { - did: post.author.did, - viewer: post.author.viewer - ? { - blockedBy: post.author.viewer?.blockedBy, - blocking: post.author.viewer?.blocking, - } - : undefined, - }, - } - } - return { - $type: 'app.bsky.embed.record#viewRecord', - uri: post.uri, - cid: post.cid, - author: post.author, - value: post.record, - labels: post.labels, - indexedAt: post.indexedAt, - embeds: omitEmbeds ? undefined : post.embed ? [post.embed] : [], - } - } - - getRecordWithMediaEmbedView( - embed: EmbedRecordWithMedia, - embedRecordView: RecordEmbedViewRecord, - ) { - let mediaEmbed: EmbedImagesView | EmbedExternalView - if (isEmbedImages(embed.media)) { - mediaEmbed = this.imagesEmbedView(embed.media) - } else if (isEmbedExternal(embed.media)) { - mediaEmbed = this.externalEmbedView(embed.media) - } else { - return - } - return { - $type: 'app.bsky.embed.recordWithMedia#view', - record: { - record: embedRecordView, - }, - media: mediaEmbed, - } - } -} diff --git a/packages/pds/src/app-view/services/graph/index.ts b/packages/pds/src/app-view/services/graph/index.ts deleted file mode 100644 index 6df2d0f10d4..00000000000 --- a/packages/pds/src/app-view/services/graph/index.ts +++ /dev/null @@ -1,174 +0,0 @@ -import Database from '../../../db' -import { DbRef } from '../../../db/util' -import { NotEmptyArray } from '@atproto/common' -import { sql } from 'kysely' -import { ImageUriBuilder } from '../../../image/uri' -import { ProfileView } from '../../../lexicon/types/app/bsky/actor/defs' -import { List } from '../../db/tables/list' - -export class GraphService { - constructor(public db: Database, public imgUriBuilder: ImageUriBuilder) {} - - static creator(imgUriBuilder: ImageUriBuilder) { - return (db: Database) => new GraphService(db, imgUriBuilder) - } - - getListsQb(requester: string | null) { - const { ref } = this.db.db.dynamic - return this.db.db - .selectFrom('list') - .innerJoin('did_handle', 'did_handle.did', 'list.creator') - .selectAll('list') - .selectAll('did_handle') - .select( - this.db.db - .selectFrom('list_mute') - .where('list_mute.mutedByDid', '=', requester ?? '') - .whereRef('list_mute.listUri', '=', ref('list.uri')) - .select('list_mute.listUri') - .as('viewerMuted'), - ) - } - - getListItemsQb() { - return this.db.db - .selectFrom('list_item') - .innerJoin('did_handle as subject', 'subject.did', 'list_item.subjectDid') - .selectAll('subject') - .select(['list_item.cid as cid', 'list_item.createdAt as createdAt']) - } - - blockQb(requester: string | null, refs: NotEmptyArray) { - const subjectRefs = sql.join(refs) - return this.db.db - .selectFrom('actor_block') - .where((outer) => - outer - .where((qb) => - qb - .where('actor_block.creator', '=', requester ?? '') - .whereRef('actor_block.subjectDid', 'in', sql`(${subjectRefs})`), - ) - .orWhere((qb) => - qb - .where('actor_block.subjectDid', '=', requester ?? '') - .whereRef('actor_block.creator', 'in', sql`(${subjectRefs})`), - ), - ) - .select(['creator', 'subjectDid']) - } - - blockRefQb(first: DbRef, second: DbRef) { - return this.db.db - .selectFrom('actor_block') - .where((outer) => - outer - .where((qb) => - qb - .whereRef('actor_block.creator', '=', first) - .whereRef('actor_block.subjectDid', '=', second), - ) - .orWhere((qb) => - qb - .whereRef('actor_block.subjectDid', '=', first) - .whereRef('actor_block.creator', '=', second), - ), - ) - .select(['creator', 'subjectDid']) - } - - async getBlocks( - requester: string, - subjectHandleOrDid: string, - ): Promise<{ blocking: boolean; blockedBy: boolean }> { - let subjectDid - if (subjectHandleOrDid.startsWith('did:')) { - subjectDid = subjectHandleOrDid - } else { - const res = await this.db.db - .selectFrom('did_handle') - .where('handle', '=', subjectHandleOrDid) - .select('did') - .executeTakeFirst() - if (!res) { - return { blocking: false, blockedBy: false } - } - subjectDid = res.did - } - - const accnts = [requester, subjectDid] - const blockRes = await this.db.db - .selectFrom('actor_block') - .where('creator', 'in', accnts) - .where('subjectDid', 'in', accnts) - .selectAll() - .execute() - - const blocking = blockRes.some( - (row) => row.creator === requester && row.subjectDid === subjectDid, - ) - const blockedBy = blockRes.some( - (row) => row.creator === subjectDid && row.subjectDid === requester, - ) - - return { - blocking, - blockedBy, - } - } - - async getListViews(listUris: string[], requester: string | null) { - if (listUris.length < 1) return {} - const lists = await this.getListsQb(requester) - .where('list.uri', 'in', listUris) - .execute() - return lists.reduce( - (acc, cur) => ({ - ...acc, - [cur.uri]: cur, - }), - {}, - ) - } - - formatListView(list: ListInfo, profiles: Record) { - return { - uri: list.uri, - cid: list.cid, - creator: profiles[list.creator], - name: list.name, - purpose: list.purpose, - description: list.description ?? undefined, - descriptionFacets: list.descriptionFacets - ? JSON.parse(list.descriptionFacets) - : undefined, - avatar: list.avatarCid - ? this.imgUriBuilder.getCommonSignedUri('avatar', list.avatarCid) - : undefined, - indexedAt: list.indexedAt, - viewer: { - muted: !!list.viewerMuted, - }, - } - } - - formatListViewBasic(list: ListInfo) { - return { - uri: list.uri, - cid: list.cid, - name: list.name, - purpose: list.purpose, - avatar: list.avatarCid - ? this.imgUriBuilder.getCommonSignedUri('avatar', list.avatarCid) - : undefined, - indexedAt: list.indexedAt, - viewer: { - muted: !!list.viewerMuted, - }, - } - } -} - -type ListInfo = List & { - viewerMuted: string | null -} diff --git a/packages/pds/src/app-view/services/indexing/plugins/post.ts b/packages/pds/src/app-view/services/indexing/plugins/post.ts index 3583f087cf1..efc1f2532c7 100644 --- a/packages/pds/src/app-view/services/indexing/plugins/post.ts +++ b/packages/pds/src/app-view/services/indexing/plugins/post.ts @@ -20,7 +20,6 @@ import RecordProcessor from '../processor' import { UserNotification } from '../../../../db/tables/user-notification' import { countAll, excluded } from '../../../../db/util' import { toSimplifiedISOSafe } from '../util' -import { getAncestorsAndSelfQb } from '../../feed/util' type Post = DatabaseSchemaType['post'] type PostEmbedImage = DatabaseSchemaType['post_embed_image'] @@ -138,14 +137,7 @@ const insertFn = async ( await db.insertInto('post_embed_record').values(recordEmbed).execute() } } - const ancestors = await getAncestorsAndSelfQb(db, { - uri: post.uri, - parentHeight: REPLY_NOTIF_DEPTH, - }) - .selectFrom('ancestor') - .selectAll() - .execute() - return { post: insertedPost, facets, embeds, ancestors } + return { post: insertedPost, facets, embeds } } const findDuplicate = async (): Promise => { diff --git a/packages/pds/src/context.ts b/packages/pds/src/context.ts index 13e765aaf77..04edb9647dd 100644 --- a/packages/pds/src/context.ts +++ b/packages/pds/src/context.ts @@ -17,7 +17,6 @@ import { Sequencer, SequencerLeader } from './sequencer' import { Labeler } from './labeler' import { BackgroundQueue } from './event-stream/background-queue' import DidSqlCache from './did-cache' -import { MountedAlgos } from './feed-gen/types' import { Crawlers } from './crawlers' import { LabelCache } from './label-cache' import { RuntimeFlags } from './runtime-flags' @@ -47,7 +46,6 @@ export class AppContext { backgroundQueue: BackgroundQueue appviewAgent: AtpAgent crawlers: Crawlers - algos: MountedAlgos }, ) {} @@ -171,10 +169,6 @@ export class AppContext { return this.opts.appviewAgent } - get algos(): MountedAlgos { - return this.opts.algos - } - async serviceAuthHeaders(did: string, audience?: string) { const aud = audience ?? this.cfg.bskyAppViewDid if (!aud) { diff --git a/packages/pds/src/feed-gen/best-of-follows.ts b/packages/pds/src/feed-gen/best-of-follows.ts deleted file mode 100644 index a88e24fdb8f..00000000000 --- a/packages/pds/src/feed-gen/best-of-follows.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { InvalidRequestError } from '@atproto/xrpc-server' -import { QueryParams as SkeletonParams } from '../lexicon/types/app/bsky/feed/getFeedSkeleton' -import { AlgoHandler, AlgoResponse } from './types' -import { GenericKeyset, paginate } from '../db/pagination' -import AppContext from '../context' - -const handler: AlgoHandler = async ( - ctx: AppContext, - params: SkeletonParams, - requester: string, -): Promise => { - if (ctx.db.dialect === 'sqlite') { - throw new Error('best-of-follows algo not available in sqlite') - } - - const { limit, cursor } = params - const accountService = ctx.services.account(ctx.db) - const feedService = ctx.services.appView.feed(ctx.db) - const graphService = ctx.services.appView.graph(ctx.db) - - const { ref } = ctx.db.db.dynamic - - // candidates are ranked within a materialized view by like count, depreciated over time. - - let builder = feedService - .selectPostQb() - .innerJoin('algo_whats_hot_view as candidate', 'candidate.uri', 'post.uri') - .where((qb) => - qb - .where('post.creator', '=', requester) - .orWhereExists((inner) => - inner - .selectFrom('follow') - .where('follow.creator', '=', requester) - .whereRef('follow.subjectDid', '=', 'post.creator'), - ), - ) - .where((qb) => - accountService.whereNotMuted(qb, requester, [ref('post.creator')]), - ) - .whereNotExists(graphService.blockQb(requester, [ref('post.creator')])) - .select('candidate.score') - .select('candidate.cid') - - const keyset = new ScoreKeyset(ref('candidate.score'), ref('candidate.cid')) - builder = paginate(builder, { limit, cursor, keyset }) - - const feedItems = await builder.execute() - - return { - feedItems, - cursor: keyset.packFromResult(feedItems), - } -} - -export default handler - -type Result = { score: number; cid: string } -type LabeledResult = { primary: number; secondary: string } -export class ScoreKeyset extends GenericKeyset { - labelResult(result: Result) { - return { - primary: result.score, - secondary: result.cid, - } - } - labeledResultToCursor(labeled: LabeledResult) { - return { - primary: Math.round(labeled.primary).toString(), - secondary: labeled.secondary, - } - } - cursorToLabeledResult(cursor: { primary: string; secondary: string }) { - const score = parseInt(cursor.primary, 10) - if (isNaN(score)) { - throw new InvalidRequestError('Malformed cursor') - } - return { - primary: score, - secondary: cursor.secondary, - } - } -} diff --git a/packages/pds/src/feed-gen/bsky-team.ts b/packages/pds/src/feed-gen/bsky-team.ts deleted file mode 100644 index ba4196e79d9..00000000000 --- a/packages/pds/src/feed-gen/bsky-team.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { NotEmptyArray } from '@atproto/common' -import { QueryParams as SkeletonParams } from '../lexicon/types/app/bsky/feed/getFeedSkeleton' -import AppContext from '../context' -import { paginate } from '../db/pagination' -import { AlgoHandler, AlgoResponse } from './types' -import { FeedKeyset } from '../app-view/api/app/bsky/util/feed' - -const BSKY_TEAM: NotEmptyArray = [ - 'did:plc:z72i7hdynmk6r22z27h6tvur', // @bsky.app - 'did:plc:ewvi7nxzyoun6zhxrhs64oiz', // @atproto.com - 'did:plc:eon2iu7v3x2ukgxkqaf7e5np', // @safety.bsky.app -] - -const handler: AlgoHandler = async ( - ctx: AppContext, - params: SkeletonParams, - requester: string, -): Promise => { - const { limit = 50, cursor } = params - const accountService = ctx.services.account(ctx.db) - const feedService = ctx.services.appView.feed(ctx.db) - const graphService = ctx.services.appView.graph(ctx.db) - - const { ref } = ctx.db.db.dynamic - - const postsQb = feedService - .selectPostQb() - .where('post.creator', 'in', BSKY_TEAM) - .where((qb) => - accountService.whereNotMuted(qb, requester, [ref('post.creator')]), - ) - .whereNotExists(graphService.blockQb(requester, [ref('post.creator')])) - - const keyset = new FeedKeyset(ref('sortAt'), ref('cid')) - - let feedQb = ctx.db.db.selectFrom(postsQb.as('feed_items')).selectAll() - feedQb = paginate(feedQb, { limit, cursor, keyset }) - - const feedItems = await feedQb.execute() - return { - feedItems, - cursor: keyset.packFromResult(feedItems), - } -} - -export default handler diff --git a/packages/pds/src/feed-gen/hot-classic.ts b/packages/pds/src/feed-gen/hot-classic.ts deleted file mode 100644 index 5284632a7e9..00000000000 --- a/packages/pds/src/feed-gen/hot-classic.ts +++ /dev/null @@ -1,59 +0,0 @@ -import AppContext from '../context' -import { NotEmptyArray } from '@atproto/common' -import { QueryParams as SkeletonParams } from '../lexicon/types/app/bsky/feed/getFeedSkeleton' -import { paginate } from '../db/pagination' -import { AlgoHandler, AlgoResponse } from './types' -import { FeedKeyset } from '../app-view/api/app/bsky/util/feed' -import { valuesList } from '../db/util' - -const NO_WHATS_HOT_LABELS: NotEmptyArray = ['!no-promote'] - -const handler: AlgoHandler = async ( - ctx: AppContext, - params: SkeletonParams, - requester: string, -): Promise => { - const { limit = 50, cursor } = params - const accountService = ctx.services.account(ctx.db) - const feedService = ctx.services.appView.feed(ctx.db) - const graphService = ctx.services.appView.graph(ctx.db) - - const { ref } = ctx.db.db.dynamic - - const postsQb = feedService - .selectPostQb() - .leftJoin('post_agg', 'post_agg.uri', 'post.uri') - .leftJoin('post_embed_record', 'post_embed_record.postUri', 'post.uri') - .where('post_agg.likeCount', '>=', 12) - .where('post.replyParent', 'is', null) - .whereNotExists((qb) => - qb - .selectFrom('label') - .selectAll() - .whereRef('val', 'in', valuesList(NO_WHATS_HOT_LABELS)) - .where('neg', '=', 0) - .where((clause) => - clause - .whereRef('label.uri', '=', ref('post.creator')) - .orWhereRef('label.uri', '=', ref('post.uri')) - .orWhereRef('label.uri', '=', ref('post_embed_record.embedUri')), - ), - ) - .where((qb) => - accountService.whereNotMuted(qb, requester, [ref('post.creator')]), - ) - .whereNotExists(graphService.blockQb(requester, [ref('post.creator')])) - - const keyset = new FeedKeyset(ref('sortAt'), ref('cid')) - - let feedQb = ctx.db.db.selectFrom(postsQb.as('feed_items')).selectAll() - feedQb = paginate(feedQb, { limit, cursor, keyset }) - - const feedItems = await feedQb.execute() - return { - feedItems, - cursor: keyset.packFromResult(feedItems), - } -} - -export default handler diff --git a/packages/pds/src/feed-gen/index.ts b/packages/pds/src/feed-gen/index.ts deleted file mode 100644 index 46657e87fae..00000000000 --- a/packages/pds/src/feed-gen/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { AtUri } from '@atproto/syntax' -import withFriends from './with-friends' -import bskyTeam from './bsky-team' -import hotClassic from './hot-classic' -import bestOfFollows from './best-of-follows' -import mutuals from './mutuals' -import { ids } from '../lexicon/lexicons' -import { MountedAlgos } from './types' - -const coll = ids.AppBskyFeedGenerator - -// These are custom algorithms that will be mounted directly onto an AppView -// Feel free to remove, update to your own, or serve the following logic at a record that you control -export const makeAlgos = (did: string): MountedAlgos => ({ - [AtUri.make(did, coll, 'with-friends').toString()]: withFriends, - [AtUri.make(did, coll, 'bsky-team').toString()]: bskyTeam, - [AtUri.make(did, coll, 'hot-classic').toString()]: hotClassic, - [AtUri.make(did, coll, 'best-of-follows').toString()]: bestOfFollows, - [AtUri.make(did, coll, 'mutuals').toString()]: mutuals, -}) diff --git a/packages/pds/src/feed-gen/mutuals.ts b/packages/pds/src/feed-gen/mutuals.ts deleted file mode 100644 index 94aba5bb104..00000000000 --- a/packages/pds/src/feed-gen/mutuals.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { QueryParams as SkeletonParams } from '../lexicon/types/app/bsky/feed/getFeedSkeleton' -import AppContext from '../context' -import { paginate } from '../db/pagination' -import { AlgoHandler, AlgoResponse } from './types' -import { - FeedKeyset, - getFeedDateThreshold, -} from '../app-view/api/app/bsky/util/feed' - -const handler: AlgoHandler = async ( - ctx: AppContext, - params: SkeletonParams, - requester: string, -): Promise => { - const { limit = 50, cursor } = params - const accountService = ctx.services.account(ctx.db) - const feedService = ctx.services.appView.feed(ctx.db) - const graphService = ctx.services.appView.graph(ctx.db) - - const { ref } = ctx.db.db.dynamic - - const mutualsSubquery = ctx.db.db - .selectFrom('follow') - .where('follow.creator', '=', requester) - .whereExists((qb) => - qb - .selectFrom('follow as follow_inner') - .whereRef('follow_inner.creator', '=', 'follow.subjectDid') - .where('follow_inner.subjectDid', '=', requester) - .selectAll(), - ) - .select('follow.subjectDid') - - const keyset = new FeedKeyset(ref('feed_item.sortAt'), ref('feed_item.cid')) - const sortFrom = keyset.unpack(cursor)?.primary - - let feedQb = feedService - .selectFeedItemQb() - .where('feed_item.type', '=', 'post') // ensures originatorDid is post.creator - .where((qb) => - qb - .where('originatorDid', '=', requester) - .orWhere('originatorDid', 'in', mutualsSubquery), - ) - .where((qb) => - accountService.whereNotMuted(qb, requester, [ref('originatorDid')]), - ) - .whereNotExists(graphService.blockQb(requester, [ref('originatorDid')])) - .where('feed_item.sortAt', '>', getFeedDateThreshold(sortFrom)) - - feedQb = paginate(feedQb, { limit, cursor, keyset }) - - const feedItems = await feedQb.execute() - return { - feedItems, - cursor: keyset.packFromResult(feedItems), - } -} - -export default handler diff --git a/packages/pds/src/feed-gen/types.ts b/packages/pds/src/feed-gen/types.ts deleted file mode 100644 index 7682a0e23bc..00000000000 --- a/packages/pds/src/feed-gen/types.ts +++ /dev/null @@ -1,16 +0,0 @@ -import AppContext from '../context' -import { QueryParams as SkeletonParams } from '../lexicon/types/app/bsky/feed/getFeedSkeleton' -import { FeedRow } from '../app-view/services/feed' - -export type AlgoResponse = { - feedItems: FeedRow[] - cursor?: string -} - -export type AlgoHandler = ( - ctx: AppContext, - params: SkeletonParams, - requester: string, -) => Promise - -export type MountedAlgos = Record diff --git a/packages/pds/src/feed-gen/whats-hot.ts b/packages/pds/src/feed-gen/whats-hot.ts deleted file mode 100644 index d48c049e95c..00000000000 --- a/packages/pds/src/feed-gen/whats-hot.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { NotEmptyArray } from '@atproto/common' -import { InvalidRequestError } from '@atproto/xrpc-server' -import { QueryParams as SkeletonParams } from '../lexicon/types/app/bsky/feed/getFeedSkeleton' -import { AlgoHandler, AlgoResponse } from './types' -import { GenericKeyset, paginate } from '../db/pagination' -import AppContext from '../context' -import { valuesList } from '../db/util' -import { sql } from 'kysely' -import { FeedItemType } from '../app-view/services/feed' - -const NO_WHATS_HOT_LABELS: NotEmptyArray = [ - '!no-promote', - 'corpse', - 'self-harm', - 'porn', - 'sexual', - 'nudity', - 'underwear', -] - -const handler: AlgoHandler = async ( - ctx: AppContext, - params: SkeletonParams, - requester: string, -): Promise => { - if (ctx.db.dialect === 'sqlite') { - throw new Error('what-hot algo not available in sqlite') - } - - const { limit, cursor } = params - const accountService = ctx.services.account(ctx.db) - const graphService = ctx.services.appView.graph(ctx.db) - - const { ref } = ctx.db.db.dynamic - - // candidates are ranked within a materialized view by like count, depreciated over time. - - let builder = ctx.db.db - .selectFrom('algo_whats_hot_view as candidate') - .innerJoin('post', 'post.uri', 'candidate.uri') - .leftJoin('post_embed_record', 'post_embed_record.postUri', 'candidate.uri') - .whereNotExists((qb) => - qb - .selectFrom('label') - .selectAll() - .whereRef('val', 'in', valuesList(NO_WHATS_HOT_LABELS)) - .where('neg', '=', 0) - .where((clause) => - clause - .whereRef('label.uri', '=', ref('post.creator')) - .orWhereRef('label.uri', '=', ref('post.uri')) - .orWhereRef('label.uri', '=', ref('post_embed_record.embedUri')), - ), - ) - .where((qb) => - accountService.whereNotMuted(qb, requester, [ref('post.creator')]), - ) - .whereNotExists(graphService.blockQb(requester, [ref('post.creator')])) - .select([ - sql`${'post'}`.as('type'), - 'post.uri as uri', - 'post.cid as cid', - 'post.uri as postUri', - 'post.creator as originatorDid', - 'post.creator as postAuthorDid', - 'post.replyParent as replyParent', - 'post.replyRoot as replyRoot', - 'post.indexedAt as sortAt', - 'candidate.score', - 'candidate.cid', - ]) - - const keyset = new ScoreKeyset(ref('candidate.score'), ref('candidate.cid')) - builder = paginate(builder, { limit, cursor, keyset }) - - const feedItems = await builder.execute() - - return { - feedItems, - cursor: keyset.packFromResult(feedItems), - } -} - -export default handler - -type Result = { score: number; cid: string } -type LabeledResult = { primary: number; secondary: string } -export class ScoreKeyset extends GenericKeyset { - labelResult(result: Result) { - return { - primary: result.score, - secondary: result.cid, - } - } - labeledResultToCursor(labeled: LabeledResult) { - return { - primary: Math.round(labeled.primary).toString(), - secondary: labeled.secondary, - } - } - cursorToLabeledResult(cursor: { primary: string; secondary: string }) { - const score = parseInt(cursor.primary, 10) - if (isNaN(score)) { - throw new InvalidRequestError('Malformed cursor') - } - return { - primary: score, - secondary: cursor.secondary, - } - } -} diff --git a/packages/pds/src/feed-gen/with-friends.ts b/packages/pds/src/feed-gen/with-friends.ts deleted file mode 100644 index 682387a5e61..00000000000 --- a/packages/pds/src/feed-gen/with-friends.ts +++ /dev/null @@ -1,67 +0,0 @@ -import AppContext from '../context' -import { QueryParams as SkeletonParams } from '../lexicon/types/app/bsky/feed/getFeedSkeleton' -import { paginate } from '../db/pagination' -import { AlgoHandler, AlgoResponse } from './types' -import { - FeedKeyset, - getFeedDateThreshold, -} from '../app-view/api/app/bsky/util/feed' - -const handler: AlgoHandler = async ( - ctx: AppContext, - params: SkeletonParams, - requester: string, -): Promise => { - // Temporary change to only return a post notifying users that the feed is down - return { - feedItems: [ - { - type: 'post', - uri: 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3jzinucnmbi2c', - cid: 'bafyreifmtn55tubbv7tefrq277nzfy4zu7ioithky276aho5ehb6w3nu6q', - postUri: - 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3jzinucnmbi2c', - postAuthorDid: 'did:plc:z72i7hdynmk6r22z27h6tvur', - originatorDid: 'did:plc:z72i7hdynmk6r22z27h6tvur', - replyParent: null, - replyRoot: null, - sortAt: '2023-07-01T23:04:27.853Z', - }, - ], - } - // const { cursor, limit = 50 } = params - // const accountService = ctx.services.account(ctx.db) - // const feedService = ctx.services.appView.feed(ctx.db) - // const graphService = ctx.services.appView.graph(ctx.db) - - // const { ref } = ctx.db.db.dynamic - - // const keyset = new FeedKeyset(ref('post.indexedAt'), ref('post.cid')) - // const sortFrom = keyset.unpack(cursor)?.primary - - // let postsQb = feedService - // .selectPostQb() - // // .innerJoin('post_agg', 'post_agg.uri', 'post.uri') - // // .where('post_agg.likeCount', '>=', 6) - // .whereExists((qb) => - // qb - // .selectFrom('follow') - // .where('follow.creator', '=', requester) - // .whereRef('follow.subjectDid', '=', 'post.creator'), - // ) - // .where((qb) => - // accountService.whereNotMuted(qb, requester, [ref('post.creator')]), - // ) - // .whereNotExists(graphService.blockQb(requester, [ref('post.creator')])) - // .where('post.indexedAt', '>', getFeedDateThreshold(sortFrom)) - - // postsQb = paginate(postsQb, { limit, cursor, keyset, tryIndex: true }) - - // const feedItems = await postsQb.execute() - // return { - // feedItems, - // cursor: keyset.packFromResult(feedItems), - // } -} - -export default handler diff --git a/packages/pds/src/index.ts b/packages/pds/src/index.ts index 09a077ce9f7..813b23209cc 100644 --- a/packages/pds/src/index.ts +++ b/packages/pds/src/index.ts @@ -49,13 +49,11 @@ import { import { Labeler, HiveLabeler, KeywordLabeler } from './labeler' import { BackgroundQueue } from './event-stream/background-queue' import DidSqlCache from './did-cache' -import { MountedAlgos } from './feed-gen/types' import { Crawlers } from './crawlers' import { LabelCache } from './label-cache' import { getRedisClient } from './redis' import { RuntimeFlags } from './runtime-flags' -export type { MountedAlgos } from './feed-gen/types' export type { ServerConfigValues } from './config' export { ServerConfig } from './config' export { Database } from './db' @@ -63,7 +61,6 @@ export { ViewMaintainer } from './db/views' export { PeriodicModerationActionReversal } from './db/periodic-moderation-action-reversal' export { DiskBlobStore, MemoryBlobStore } from './storage' export { AppContext } from './context' -export { makeAlgos } from './feed-gen' export class PDS { public ctx: AppContext @@ -84,17 +81,9 @@ export class PDS { imgInvalidator?: ImageInvalidator repoSigningKey: crypto.Keypair plcRotationKey: crypto.Keypair - algos?: MountedAlgos config: ServerConfig }): PDS { - const { - db, - blobstore, - repoSigningKey, - plcRotationKey, - algos = {}, - config, - } = opts + const { db, blobstore, repoSigningKey, plcRotationKey, config } = opts let maybeImgInvalidator = opts.imgInvalidator const auth = new ServerAuth({ jwtSecret: config.jwtSecret, @@ -248,7 +237,6 @@ export class PDS { backgroundQueue, appviewAgent, crawlers, - algos, }) const xrpcOpts: XrpcServerOptions = { diff --git a/packages/pds/src/services/index.ts b/packages/pds/src/services/index.ts index b49693cc8cf..757808df198 100644 --- a/packages/pds/src/services/index.ts +++ b/packages/pds/src/services/index.ts @@ -10,9 +10,6 @@ import { AuthService } from './auth' import { RecordService } from './record' import { RepoService } from './repo' import { ModerationService } from './moderation' -import { ActorService } from '../app-view/services/actor' -import { GraphService } from '../app-view/services/graph' -import { FeedService } from '../app-view/services/feed' import { IndexingService } from '../app-view/services/indexing' import { Labeler } from '../labeler' import { LabelService } from '../app-view/services/label' @@ -74,9 +71,6 @@ export function createServices(resources: { imgInvalidator, ), appView: { - actor: ActorService.creator(imgUriBuilder, labelCache), - graph: GraphService.creator(imgUriBuilder), - feed: FeedService.creator(imgUriBuilder, labelCache), indexing: IndexingService.creator(backgroundQueue), label: LabelService.creator(labelCache), }, @@ -91,10 +85,7 @@ export type Services = { local: FromDb moderation: FromDb appView: { - feed: FromDb indexing: FromDb - actor: FromDb - graph: FromDb label: FromDb } } From c5291a5e07967b14696b56144d11c5fd2c000de4 Mon Sep 17 00:00:00 2001 From: dholms Date: Wed, 20 Sep 2023 19:47:32 -0500 Subject: [PATCH 06/31] clean up tests --- packages/pds/tests/_util.ts | 254 +- .../get-moderation-action.test.ts.snap | 0 .../get-moderation-actions.test.ts.snap | 0 .../get-moderation-report.test.ts.snap | 0 .../get-moderation-reports.test.ts.snap | 0 .../__snapshots__/get-record.test.ts.snap | 0 .../admin/__snapshots__/get-repo.test.ts.snap | 0 .../admin/get-moderation-action.test.ts | 8 +- .../admin/get-moderation-actions.test.ts | 8 +- .../admin/get-moderation-report.test.ts | 8 +- .../admin/get-moderation-reports.test.ts | 8 +- .../{views => }/admin/get-record.test.ts | 8 +- .../tests/{views => }/admin/get-repo.test.ts | 8 +- .../tests/{views => }/admin/invites.test.ts | 4 +- .../{views => }/admin/repo-search.test.ts | 8 +- packages/pds/tests/algos/hot-classic.test.ts | 93 - packages/pds/tests/algos/whats-hot.test.ts | 123 - packages/pds/tests/algos/with-friends.test.ts | 148 - packages/pds/tests/feed-generation.test.ts | 518 -- .../pds/tests/labeler/apply-labels.test.ts | 37 +- .../pds/tests/migrations/blob-creator.test.ts | 141 - .../migrations/indexed-at-on-record.test.ts | 77 - .../migrations/repo-sync-data-pt2.test.ts | 87 - .../tests/migrations/repo-sync-data.test.ts | 153 - .../migrations/user-partitioned-cids.test.ts | 102 - .../migrations/user-table-did-pkey.test.ts | 133 - .../__snapshots__/author-feed.test.ts.snap | 2495 ------ .../views/__snapshots__/blocks.test.ts.snap | 383 - .../views/__snapshots__/follows.test.ts.snap | 703 -- .../views/__snapshots__/likes.test.ts.snap | 120 - .../__snapshots__/mute-lists.test.ts.snap | 599 -- .../views/__snapshots__/mutes.test.ts.snap | 75 - .../__snapshots__/notifications.test.ts.snap | 1922 ----- .../views/__snapshots__/posts.test.ts.snap | 526 -- .../views/__snapshots__/profile.test.ts.snap | 286 - .../views/__snapshots__/reposts.test.ts.snap | 108 - .../views/__snapshots__/thread.test.ts.snap | 1883 ----- .../views/__snapshots__/timeline.test.ts.snap | 7343 ----------------- packages/pds/tests/views/actor-likes.test.ts | 117 - packages/pds/tests/views/actor-search.test.ts | 464 -- packages/pds/tests/views/author-feed.test.ts | 363 - packages/pds/tests/views/blocks.test.ts | 531 -- packages/pds/tests/views/follows.test.ts | 266 - packages/pds/tests/views/likes.test.ts | 97 - packages/pds/tests/views/mute-lists.test.ts | 355 - packages/pds/tests/views/mutes.test.ts | 107 - .../pds/tests/views/notifications.test.ts | 310 - packages/pds/tests/views/popular.test.ts | 110 - packages/pds/tests/views/posts.test.ts | 65 - packages/pds/tests/views/profile.test.ts | 285 - packages/pds/tests/views/reposts.test.ts | 77 - packages/pds/tests/views/suggestions.test.ts | 75 - packages/pds/tests/views/thread.test.ts | 456 - packages/pds/tests/views/timeline.test.ts | 310 - 54 files changed, 170 insertions(+), 22187 deletions(-) rename packages/pds/tests/{views => }/admin/__snapshots__/get-moderation-action.test.ts.snap (100%) rename packages/pds/tests/{views => }/admin/__snapshots__/get-moderation-actions.test.ts.snap (100%) rename packages/pds/tests/{views => }/admin/__snapshots__/get-moderation-report.test.ts.snap (100%) rename packages/pds/tests/{views => }/admin/__snapshots__/get-moderation-reports.test.ts.snap (100%) rename packages/pds/tests/{views => }/admin/__snapshots__/get-record.test.ts.snap (100%) rename packages/pds/tests/{views => }/admin/__snapshots__/get-repo.test.ts.snap (100%) rename packages/pds/tests/{views => }/admin/get-moderation-action.test.ts (94%) rename packages/pds/tests/{views => }/admin/get-moderation-actions.test.ts (96%) rename packages/pds/tests/{views => }/admin/get-moderation-report.test.ts (94%) rename packages/pds/tests/{views => }/admin/get-moderation-reports.test.ts (98%) rename packages/pds/tests/{views => }/admin/get-record.test.ts (95%) rename packages/pds/tests/{views => }/admin/get-repo.test.ts (95%) rename packages/pds/tests/{views => }/admin/invites.test.ts (99%) rename packages/pds/tests/{views => }/admin/repo-search.test.ts (98%) delete mode 100644 packages/pds/tests/algos/hot-classic.test.ts delete mode 100644 packages/pds/tests/algos/whats-hot.test.ts delete mode 100644 packages/pds/tests/algos/with-friends.test.ts delete mode 100644 packages/pds/tests/feed-generation.test.ts delete mode 100644 packages/pds/tests/migrations/blob-creator.test.ts delete mode 100644 packages/pds/tests/migrations/indexed-at-on-record.test.ts delete mode 100644 packages/pds/tests/migrations/repo-sync-data-pt2.test.ts delete mode 100644 packages/pds/tests/migrations/repo-sync-data.test.ts delete mode 100644 packages/pds/tests/migrations/user-partitioned-cids.test.ts delete mode 100644 packages/pds/tests/migrations/user-table-did-pkey.test.ts delete mode 100644 packages/pds/tests/views/__snapshots__/author-feed.test.ts.snap delete mode 100644 packages/pds/tests/views/__snapshots__/blocks.test.ts.snap delete mode 100644 packages/pds/tests/views/__snapshots__/follows.test.ts.snap delete mode 100644 packages/pds/tests/views/__snapshots__/likes.test.ts.snap delete mode 100644 packages/pds/tests/views/__snapshots__/mute-lists.test.ts.snap delete mode 100644 packages/pds/tests/views/__snapshots__/mutes.test.ts.snap delete mode 100644 packages/pds/tests/views/__snapshots__/notifications.test.ts.snap delete mode 100644 packages/pds/tests/views/__snapshots__/posts.test.ts.snap delete mode 100644 packages/pds/tests/views/__snapshots__/profile.test.ts.snap delete mode 100644 packages/pds/tests/views/__snapshots__/reposts.test.ts.snap delete mode 100644 packages/pds/tests/views/__snapshots__/thread.test.ts.snap delete mode 100644 packages/pds/tests/views/__snapshots__/timeline.test.ts.snap delete mode 100644 packages/pds/tests/views/actor-likes.test.ts delete mode 100644 packages/pds/tests/views/actor-search.test.ts delete mode 100644 packages/pds/tests/views/author-feed.test.ts delete mode 100644 packages/pds/tests/views/blocks.test.ts delete mode 100644 packages/pds/tests/views/follows.test.ts delete mode 100644 packages/pds/tests/views/likes.test.ts delete mode 100644 packages/pds/tests/views/mute-lists.test.ts delete mode 100644 packages/pds/tests/views/mutes.test.ts delete mode 100644 packages/pds/tests/views/notifications.test.ts delete mode 100644 packages/pds/tests/views/popular.test.ts delete mode 100644 packages/pds/tests/views/posts.test.ts delete mode 100644 packages/pds/tests/views/profile.test.ts delete mode 100644 packages/pds/tests/views/reposts.test.ts delete mode 100644 packages/pds/tests/views/suggestions.test.ts delete mode 100644 packages/pds/tests/views/thread.test.ts delete mode 100644 packages/pds/tests/views/timeline.test.ts diff --git a/packages/pds/tests/_util.ts b/packages/pds/tests/_util.ts index 0b94f09148b..bdfb72ce02d 100644 --- a/packages/pds/tests/_util.ts +++ b/packages/pds/tests/_util.ts @@ -15,7 +15,6 @@ import DiskBlobStore from '../src/storage/disk-blobstore' import AppContext from '../src/context' import { DAY, HOUR } from '@atproto/common' import { lexToJson } from '@atproto/lexicon' -import { MountedAlgos } from '../src/feed-gen/types' const ADMIN_PASSWORD = 'admin-pass' const MODERATOR_PASSWORD = 'moderator-pass' @@ -31,149 +30,146 @@ export type TestServerInfo = { export type TestServerOpts = { migration?: string - algos?: MountedAlgos } -export const runTestServer = async ( - params: Partial = {}, - opts: TestServerOpts = {}, -): Promise => { - const repoSigningKey = await crypto.Secp256k1Keypair.create() - const plcRotationKey = await crypto.Secp256k1Keypair.create() +// export const runTestServer = async ( +// params: Partial = {}, +// opts: TestServerOpts = {}, +// ): Promise => { +// const repoSigningKey = await crypto.Secp256k1Keypair.create() +// const plcRotationKey = await crypto.Secp256k1Keypair.create() - const dbPostgresUrl = params.dbPostgresUrl || process.env.DB_POSTGRES_URL - const dbPostgresSchema = - params.dbPostgresSchema || process.env.DB_POSTGRES_SCHEMA - // run plc server +// const dbPostgresUrl = params.dbPostgresUrl || process.env.DB_POSTGRES_URL +// const dbPostgresSchema = +// params.dbPostgresSchema || process.env.DB_POSTGRES_SCHEMA +// // run plc server - let plcDb - if (dbPostgresUrl !== undefined) { - plcDb = PlcDatabase.postgres({ - url: dbPostgresUrl, - schema: `plc_test_${dbPostgresSchema}`, - }) - await plcDb.migrateToLatestOrThrow() - } else { - plcDb = PlcDatabase.mock() - } +// let plcDb +// if (dbPostgresUrl !== undefined) { +// plcDb = PlcDatabase.postgres({ +// url: dbPostgresUrl, +// schema: `plc_test_${dbPostgresSchema}`, +// }) +// await plcDb.migrateToLatestOrThrow() +// } else { +// plcDb = PlcDatabase.mock() +// } - const plcServer = PlcServer.create({ db: plcDb }) - const plcListener = await plcServer.start() - const plcPort = (plcListener.address() as AddressInfo).port - const plcUrl = `http://localhost:${plcPort}` +// const plcServer = PlcServer.create({ db: plcDb }) +// const plcListener = await plcServer.start() +// const plcPort = (plcListener.address() as AddressInfo).port +// const plcUrl = `http://localhost:${plcPort}` - const recoveryKey = (await crypto.Secp256k1Keypair.create()).did() +// const recoveryKey = (await crypto.Secp256k1Keypair.create()).did() - const plcClient = new plc.Client(plcUrl) - const serverDid = await plcClient.createDid({ - signingKey: repoSigningKey.did(), - rotationKeys: [recoveryKey, plcRotationKey.did()], - handle: 'localhost', - pds: 'https://pds.public.url', - signer: plcRotationKey, - }) +// const plcClient = new plc.Client(plcUrl) +// const serverDid = await plcClient.createDid({ +// signingKey: repoSigningKey.did(), +// rotationKeys: [recoveryKey, plcRotationKey.did()], +// handle: 'localhost', +// pds: 'https://pds.public.url', +// signer: plcRotationKey, +// }) - const blobstoreLoc = path.join(os.tmpdir(), randomStr(5, 'base32')) +// const blobstoreLoc = path.join(os.tmpdir(), randomStr(5, 'base32')) - const cfg = new ServerConfig({ - debugMode: true, - version: '0.0.0', - scheme: 'http', - hostname: 'localhost', - serverDid, - recoveryKey, - adminPassword: ADMIN_PASSWORD, - moderatorPassword: MODERATOR_PASSWORD, - triagePassword: TRIAGE_PASSWORD, - inviteRequired: false, - userInviteInterval: null, - userInviteEpoch: Date.now(), - didPlcUrl: plcUrl, - didCacheMaxTTL: DAY, - didCacheStaleTTL: HOUR, - jwtSecret: 'jwt-secret', - availableUserDomains: ['.test'], - rateLimitsEnabled: false, - appUrlPasswordReset: 'app://forgot-password', - emailNoReplyAddress: 'noreply@blueskyweb.xyz', - publicUrl: 'https://pds.public.url', - imgUriSalt: '9dd04221f5755bce5f55f47464c27e1e', - imgUriKey: - 'f23ecd142835025f42c3db2cf25dd813956c178392760256211f9d315f8ab4d8', - dbPostgresUrl: process.env.DB_POSTGRES_URL, - blobstoreLocation: `${blobstoreLoc}/blobs`, - blobstoreTmp: `${blobstoreLoc}/tmp`, - labelerDid: 'did:example:labeler', - labelerKeywords: { label_me: 'test-label', label_me_2: 'test-label-2' }, - feedGenDid: 'did:example:feedGen', - maxSubscriptionBuffer: 200, - repoBackfillLimitMs: HOUR, - sequencerLeaderLockId: uniqueLockId(), - dbTxLockNonce: await randomStr(32, 'base32'), - bskyAppViewProxy: false, - ...params, - }) +// const cfg = new ServerConfig({ +// debugMode: true, +// version: '0.0.0', +// scheme: 'http', +// hostname: 'localhost', +// serverDid, +// recoveryKey, +// adminPassword: ADMIN_PASSWORD, +// moderatorPassword: MODERATOR_PASSWORD, +// triagePassword: TRIAGE_PASSWORD, +// inviteRequired: false, +// userInviteInterval: null, +// userInviteEpoch: Date.now(), +// didPlcUrl: plcUrl, +// didCacheMaxTTL: DAY, +// didCacheStaleTTL: HOUR, +// jwtSecret: 'jwt-secret', +// availableUserDomains: ['.test'], +// rateLimitsEnabled: false, +// appUrlPasswordReset: 'app://forgot-password', +// emailNoReplyAddress: 'noreply@blueskyweb.xyz', +// publicUrl: 'https://pds.public.url', +// imgUriSalt: '9dd04221f5755bce5f55f47464c27e1e', +// imgUriKey: +// 'f23ecd142835025f42c3db2cf25dd813956c178392760256211f9d315f8ab4d8', +// dbPostgresUrl: process.env.DB_POSTGRES_URL, +// blobstoreLocation: `${blobstoreLoc}/blobs`, +// blobstoreTmp: `${blobstoreLoc}/tmp`, +// labelerDid: 'did:example:labeler', +// labelerKeywords: { label_me: 'test-label', label_me_2: 'test-label-2' }, +// feedGenDid: 'did:example:feedGen', +// maxSubscriptionBuffer: 200, +// repoBackfillLimitMs: HOUR, +// sequencerLeaderLockId: uniqueLockId(), +// dbTxLockNonce: await randomStr(32, 'base32'), +// ...params, +// }) - const db = - cfg.dbPostgresUrl !== undefined - ? Database.postgres({ - url: cfg.dbPostgresUrl, - schema: cfg.dbPostgresSchema, - txLockNonce: cfg.dbTxLockNonce, - }) - : Database.memory() +// const db = +// cfg.dbPostgresUrl !== undefined +// ? Database.postgres({ +// url: cfg.dbPostgresUrl, +// schema: cfg.dbPostgresSchema, +// txLockNonce: cfg.dbTxLockNonce, +// }) +// : Database.memory() - // Separate migration db on postgres in case migration changes some - // connection state that we need in the tests, e.g. "alter database ... set ..." - const migrationDb = - cfg.dbPostgresUrl !== undefined - ? Database.postgres({ - url: cfg.dbPostgresUrl, - schema: cfg.dbPostgresSchema, - txLockNonce: cfg.dbTxLockNonce, - }) - : db - if (opts.migration) { - await migrationDb.migrateToOrThrow(opts.migration) - } else { - await migrationDb.migrateToLatestOrThrow() - } - if (migrationDb !== db) { - await migrationDb.close() - } +// // Separate migration db on postgres in case migration changes some +// // connection state that we need in the tests, e.g. "alter database ... set ..." +// const migrationDb = +// cfg.dbPostgresUrl !== undefined +// ? Database.postgres({ +// url: cfg.dbPostgresUrl, +// schema: cfg.dbPostgresSchema, +// txLockNonce: cfg.dbTxLockNonce, +// }) +// : db +// if (opts.migration) { +// await migrationDb.migrateToOrThrow(opts.migration) +// } else { +// await migrationDb.migrateToLatestOrThrow() +// } +// if (migrationDb !== db) { +// await migrationDb.close() +// } - const blobstore = - cfg.blobstoreLocation !== undefined - ? await DiskBlobStore.create(cfg.blobstoreLocation, cfg.blobstoreTmp) - : new MemoryBlobStore() +// const blobstore = +// cfg.blobstoreLocation !== undefined +// ? await DiskBlobStore.create(cfg.blobstoreLocation, cfg.blobstoreTmp) +// : new MemoryBlobStore() - const pds = PDS.create({ - db, - blobstore, - repoSigningKey, - plcRotationKey, - config: cfg, - algos: opts.algos, - }) - const pdsServer = await pds.start() - const pdsPort = (pdsServer.address() as AddressInfo).port +// const pds = PDS.create({ +// db, +// blobstore, +// repoSigningKey, +// plcRotationKey, +// config: cfg, +// }) +// const pdsServer = await pds.start() +// const pdsPort = (pdsServer.address() as AddressInfo).port - // we refresh label cache by hand in `processAll` instead of on a timer - pds.ctx.labelCache.stop() +// // we refresh label cache by hand in `processAll` instead of on a timer +// pds.ctx.labelCache.stop() - return { - url: `http://localhost:${pdsPort}`, - ctx: pds.ctx, - close: async () => { - await pds.destroy() - await plcServer.destroy() - }, - processAll: async () => { - await pds.ctx.backgroundQueue.processAll() - await pds.ctx.labelCache.fullRefresh() - }, - } -} +// return { +// url: `http://localhost:${pdsPort}`, +// ctx: pds.ctx, +// close: async () => { +// await pds.destroy() +// await plcServer.destroy() +// }, +// processAll: async () => { +// await pds.ctx.backgroundQueue.processAll() +// await pds.ctx.labelCache.fullRefresh() +// }, +// } +// } export const adminAuth = () => { return basicAuth('admin', ADMIN_PASSWORD) diff --git a/packages/pds/tests/views/admin/__snapshots__/get-moderation-action.test.ts.snap b/packages/pds/tests/admin/__snapshots__/get-moderation-action.test.ts.snap similarity index 100% rename from packages/pds/tests/views/admin/__snapshots__/get-moderation-action.test.ts.snap rename to packages/pds/tests/admin/__snapshots__/get-moderation-action.test.ts.snap diff --git a/packages/pds/tests/views/admin/__snapshots__/get-moderation-actions.test.ts.snap b/packages/pds/tests/admin/__snapshots__/get-moderation-actions.test.ts.snap similarity index 100% rename from packages/pds/tests/views/admin/__snapshots__/get-moderation-actions.test.ts.snap rename to packages/pds/tests/admin/__snapshots__/get-moderation-actions.test.ts.snap diff --git a/packages/pds/tests/views/admin/__snapshots__/get-moderation-report.test.ts.snap b/packages/pds/tests/admin/__snapshots__/get-moderation-report.test.ts.snap similarity index 100% rename from packages/pds/tests/views/admin/__snapshots__/get-moderation-report.test.ts.snap rename to packages/pds/tests/admin/__snapshots__/get-moderation-report.test.ts.snap diff --git a/packages/pds/tests/views/admin/__snapshots__/get-moderation-reports.test.ts.snap b/packages/pds/tests/admin/__snapshots__/get-moderation-reports.test.ts.snap similarity index 100% rename from packages/pds/tests/views/admin/__snapshots__/get-moderation-reports.test.ts.snap rename to packages/pds/tests/admin/__snapshots__/get-moderation-reports.test.ts.snap diff --git a/packages/pds/tests/views/admin/__snapshots__/get-record.test.ts.snap b/packages/pds/tests/admin/__snapshots__/get-record.test.ts.snap similarity index 100% rename from packages/pds/tests/views/admin/__snapshots__/get-record.test.ts.snap rename to packages/pds/tests/admin/__snapshots__/get-record.test.ts.snap diff --git a/packages/pds/tests/views/admin/__snapshots__/get-repo.test.ts.snap b/packages/pds/tests/admin/__snapshots__/get-repo.test.ts.snap similarity index 100% rename from packages/pds/tests/views/admin/__snapshots__/get-repo.test.ts.snap rename to packages/pds/tests/admin/__snapshots__/get-repo.test.ts.snap diff --git a/packages/pds/tests/views/admin/get-moderation-action.test.ts b/packages/pds/tests/admin/get-moderation-action.test.ts similarity index 94% rename from packages/pds/tests/views/admin/get-moderation-action.test.ts rename to packages/pds/tests/admin/get-moderation-action.test.ts index 66562a73ec2..54b86984416 100644 --- a/packages/pds/tests/views/admin/get-moderation-action.test.ts +++ b/packages/pds/tests/admin/get-moderation-action.test.ts @@ -6,10 +6,10 @@ import { import { REASONOTHER, REASONSPAM, -} from '../../../src/lexicon/types/com/atproto/moderation/defs' -import { runTestServer, forSnapshot, CloseFn, adminAuth } from '../../_util' -import { SeedClient } from '../../seeds/client' -import basicSeed from '../../seeds/basic' +} from '../../src/lexicon/types/com/atproto/moderation/defs' +import { runTestServer, forSnapshot, CloseFn, adminAuth } from '../_util' +import { SeedClient } from '../seeds/client' +import basicSeed from '../seeds/basic' describe('pds admin get moderation action view', () => { let agent: AtpAgent diff --git a/packages/pds/tests/views/admin/get-moderation-actions.test.ts b/packages/pds/tests/admin/get-moderation-actions.test.ts similarity index 96% rename from packages/pds/tests/views/admin/get-moderation-actions.test.ts rename to packages/pds/tests/admin/get-moderation-actions.test.ts index 31ee1686c3c..1ad5e066c60 100644 --- a/packages/pds/tests/views/admin/get-moderation-actions.test.ts +++ b/packages/pds/tests/admin/get-moderation-actions.test.ts @@ -7,16 +7,16 @@ import { import { REASONOTHER, REASONSPAM, -} from '../../../src/lexicon/types/com/atproto/moderation/defs' +} from '../../src/lexicon/types/com/atproto/moderation/defs' import { runTestServer, forSnapshot, CloseFn, adminAuth, paginateAll, -} from '../../_util' -import { SeedClient } from '../../seeds/client' -import basicSeed from '../../seeds/basic' +} from '../_util' +import { SeedClient } from '../seeds/client' +import basicSeed from '../seeds/basic' describe('pds admin get moderation actions view', () => { let agent: AtpAgent diff --git a/packages/pds/tests/views/admin/get-moderation-report.test.ts b/packages/pds/tests/admin/get-moderation-report.test.ts similarity index 94% rename from packages/pds/tests/views/admin/get-moderation-report.test.ts rename to packages/pds/tests/admin/get-moderation-report.test.ts index c1363a0c49f..7d433539b37 100644 --- a/packages/pds/tests/views/admin/get-moderation-report.test.ts +++ b/packages/pds/tests/admin/get-moderation-report.test.ts @@ -6,10 +6,10 @@ import { import { REASONOTHER, REASONSPAM, -} from '../../../src/lexicon/types/com/atproto/moderation/defs' -import { runTestServer, forSnapshot, CloseFn, adminAuth } from '../../_util' -import { SeedClient } from '../../seeds/client' -import basicSeed from '../../seeds/basic' +} from '../../src/lexicon/types/com/atproto/moderation/defs' +import { runTestServer, forSnapshot, CloseFn, adminAuth } from '../_util' +import { SeedClient } from '../seeds/client' +import basicSeed from '../seeds/basic' describe('pds admin get moderation action view', () => { let agent: AtpAgent diff --git a/packages/pds/tests/views/admin/get-moderation-reports.test.ts b/packages/pds/tests/admin/get-moderation-reports.test.ts similarity index 98% rename from packages/pds/tests/views/admin/get-moderation-reports.test.ts rename to packages/pds/tests/admin/get-moderation-reports.test.ts index 14be4ce821a..20f1c97f781 100644 --- a/packages/pds/tests/views/admin/get-moderation-reports.test.ts +++ b/packages/pds/tests/admin/get-moderation-reports.test.ts @@ -7,16 +7,16 @@ import { import { REASONOTHER, REASONSPAM, -} from '../../../src/lexicon/types/com/atproto/moderation/defs' +} from '../../src/lexicon/types/com/atproto/moderation/defs' import { runTestServer, forSnapshot, CloseFn, adminAuth, paginateAll, -} from '../../_util' -import { SeedClient } from '../../seeds/client' -import basicSeed from '../../seeds/basic' +} from '../_util' +import { SeedClient } from '../seeds/client' +import basicSeed from '../seeds/basic' describe('pds admin get moderation reports view', () => { let agent: AtpAgent diff --git a/packages/pds/tests/views/admin/get-record.test.ts b/packages/pds/tests/admin/get-record.test.ts similarity index 95% rename from packages/pds/tests/views/admin/get-record.test.ts rename to packages/pds/tests/admin/get-record.test.ts index 797a4b726f2..d70707b2b70 100644 --- a/packages/pds/tests/views/admin/get-record.test.ts +++ b/packages/pds/tests/admin/get-record.test.ts @@ -7,16 +7,16 @@ import { import { REASONOTHER, REASONSPAM, -} from '../../../src/lexicon/types/com/atproto/moderation/defs' +} from '../../src/lexicon/types/com/atproto/moderation/defs' import { runTestServer, forSnapshot, CloseFn, adminAuth, TestServerInfo, -} from '../../_util' -import { SeedClient } from '../../seeds/client' -import basicSeed from '../../seeds/basic' +} from '../_util' +import { SeedClient } from '../seeds/client' +import basicSeed from '../seeds/basic' describe('pds admin get record view', () => { let server: TestServerInfo diff --git a/packages/pds/tests/views/admin/get-repo.test.ts b/packages/pds/tests/admin/get-repo.test.ts similarity index 95% rename from packages/pds/tests/views/admin/get-repo.test.ts rename to packages/pds/tests/admin/get-repo.test.ts index c0f8d500c90..3cb997f6ff2 100644 --- a/packages/pds/tests/views/admin/get-repo.test.ts +++ b/packages/pds/tests/admin/get-repo.test.ts @@ -6,7 +6,7 @@ import { import { REASONOTHER, REASONSPAM, -} from '../../../src/lexicon/types/com/atproto/moderation/defs' +} from '../../src/lexicon/types/com/atproto/moderation/defs' import { runTestServer, forSnapshot, @@ -15,9 +15,9 @@ import { TestServerInfo, moderatorAuth, triageAuth, -} from '../../_util' -import { SeedClient } from '../../seeds/client' -import basicSeed from '../../seeds/basic' +} from '../_util' +import { SeedClient } from '../seeds/client' +import basicSeed from '../seeds/basic' describe('pds admin get repo view', () => { let server: TestServerInfo diff --git a/packages/pds/tests/views/admin/invites.test.ts b/packages/pds/tests/admin/invites.test.ts similarity index 99% rename from packages/pds/tests/views/admin/invites.test.ts rename to packages/pds/tests/admin/invites.test.ts index d315b7dbdd7..6cdfdbaba93 100644 --- a/packages/pds/tests/views/admin/invites.test.ts +++ b/packages/pds/tests/admin/invites.test.ts @@ -4,9 +4,9 @@ import { adminAuth, moderatorAuth, TestServerInfo, -} from '../../_util' +} from '../_util' import { randomStr } from '@atproto/crypto' -import { SeedClient } from '../../seeds/client' +import { SeedClient } from '../seeds/client' describe('pds admin invite views', () => { let agent: AtpAgent diff --git a/packages/pds/tests/views/admin/repo-search.test.ts b/packages/pds/tests/admin/repo-search.test.ts similarity index 98% rename from packages/pds/tests/views/admin/repo-search.test.ts rename to packages/pds/tests/admin/repo-search.test.ts index e468a58a663..3f25c893901 100644 --- a/packages/pds/tests/views/admin/repo-search.test.ts +++ b/packages/pds/tests/admin/repo-search.test.ts @@ -6,10 +6,10 @@ import { CloseFn, paginateAll, adminAuth, -} from '../../_util' -import { SeedClient } from '../../seeds/client' -import usersBulkSeed from '../../seeds/users-bulk' -import { Database } from '../../../src' +} from '../_util' +import { SeedClient } from '../seeds/client' +import usersBulkSeed from '../seeds/users-bulk' +import { Database } from '../../src' describe('pds admin repo search view', () => { let agent: AtpAgent diff --git a/packages/pds/tests/algos/hot-classic.test.ts b/packages/pds/tests/algos/hot-classic.test.ts deleted file mode 100644 index 82a65f5c852..00000000000 --- a/packages/pds/tests/algos/hot-classic.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import AtpAgent, { AtUri } from '@atproto/api' -import { runTestServer, TestServerInfo } from '../_util' -import { SeedClient } from '../seeds/client' -import basicSeed from '../seeds/basic' -import { makeAlgos } from '../../src' - -describe.skip('algo hot-classic', () => { - let server: TestServerInfo - let agent: AtpAgent - let sc: SeedClient - - // account dids, for convenience - let alice: string - let bob: string - - const feedPublisherDid = 'did:example:feed-publisher' - const feedUri = AtUri.make( - feedPublisherDid, - 'app.bsky.feed.generator', - 'hot-classic', - ).toString() - - beforeAll(async () => { - server = await runTestServer( - { - dbPostgresSchema: 'algo_hot_classic', - }, - { - algos: makeAlgos(feedPublisherDid), - }, - ) - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await basicSeed(sc) - - alice = sc.dids.alice - bob = sc.dids.bob - await server.processAll() - }) - - afterAll(async () => { - await server.close() - }) - - it('returns well liked posts', async () => { - const img = await sc.uploadFile( - alice, - 'tests/image/fixtures/key-landscape-small.jpg', - 'image/jpeg', - ) - const one = await sc.post(alice, 'first post', undefined, [img]) - const two = await sc.post(bob, 'bobby boi') - const three = await sc.reply(bob, one.ref, one.ref, 'reply') - - for (let i = 0; i < 12; i++) { - const name = `user${i}` - await sc.createAccount(name, { - handle: `user${i}.test`, - email: `user${i}@test.com`, - password: 'password', - }) - await sc.like(sc.dids[name], one.ref) - await sc.like(sc.dids[name], two.ref) - await sc.like(sc.dids[name], three.ref) - } - await server.processAll() - - const res = await agent.api.app.bsky.feed.getFeed( - { feed: feedUri }, - { headers: sc.getHeaders(alice) }, - ) - const feedUris = res.data.feed.map((i) => i.post.uri).sort() - const expected = [one.ref.uriStr, two.ref.uriStr].sort() - expect(feedUris).toEqual(expected) - }) - - it('paginates', async () => { - const res = await agent.api.app.bsky.feed.getFeed( - { feed: feedUri }, - { headers: sc.getHeaders(alice) }, - ) - const first = await agent.api.app.bsky.feed.getFeed( - { feed: feedUri, limit: 1 }, - { headers: sc.getHeaders(alice) }, - ) - const second = await agent.api.app.bsky.feed.getFeed( - { feed: feedUri, cursor: first.data.cursor }, - { headers: sc.getHeaders(alice) }, - ) - - expect([...first.data.feed, ...second.data.feed]).toEqual(res.data.feed) - }) -}) diff --git a/packages/pds/tests/algos/whats-hot.test.ts b/packages/pds/tests/algos/whats-hot.test.ts deleted file mode 100644 index 9ad33621af4..00000000000 --- a/packages/pds/tests/algos/whats-hot.test.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { HOUR } from '@atproto/common' -import AtpAgent, { AtUri } from '@atproto/api' -import { runTestServer, TestServerInfo } from '../_util' -import { SeedClient } from '../seeds/client' -import basicSeed from '../seeds/basic' -import { makeAlgos } from '../../src' - -describe.skip('algo whats-hot', () => { - let server: TestServerInfo - let agent: AtpAgent - let sc: SeedClient - - // account dids, for convenience - let alice: string - let bob: string - let carol: string - - const feedPublisherDid = 'did:example:feed-publisher' - const feedUri = AtUri.make( - feedPublisherDid, - 'app.bsky.feed.generator', - 'whats-hot', - ).toString() - - beforeAll(async () => { - server = await runTestServer( - { - dbPostgresSchema: 'algo_whats_hot', - }, - { - algos: makeAlgos(feedPublisherDid), - }, - ) - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await basicSeed(sc) - - alice = sc.dids.alice - bob = sc.dids.bob - carol = sc.dids.carol - await server.processAll() - }) - - afterAll(async () => { - await server.close() - }) - - it('returns well liked posts', async () => { - if (server.ctx.db.dialect === 'sqlite') return - - const img = await sc.uploadFile( - alice, - 'tests/image/fixtures/key-landscape-small.jpg', - 'image/jpeg', - ) - const one = await sc.post(carol, 'carol is in the chat') - const two = await sc.post(carol, "it's me, carol") - const three = await sc.post(alice, 'first post', undefined, [img]) - const four = await sc.post(bob, 'bobby boi') - const five = await sc.post(bob, 'another one') - - for (let i = 0; i < 20; i++) { - const name = `user${i}` - await sc.createAccount(name, { - handle: `user${i}.test`, - email: `user${i}@test.com`, - password: 'password', - }) - await sc.like(sc.dids[name], three.ref) // will be down-regulated by time - if (i > 3) { - await sc.like(sc.dids[name], one.ref) - } - if (i > 5) { - await sc.like(sc.dids[name], two.ref) - } - if (i > 7) { - await sc.like(sc.dids[name], four.ref) - await sc.like(sc.dids[name], five.ref) - } - } - await server.processAll() - - // move the 3rd post 5 hours into the past to check gravity - await server.ctx.db.db - .updateTable('post') - .where('uri', '=', three.ref.uriStr) - .set({ indexedAt: new Date(Date.now() - 5 * HOUR).toISOString() }) - .execute() - - await server.ctx.db.refreshMaterializedView('algo_whats_hot_view') - - const res = await agent.api.app.bsky.feed.getFeed( - { feed: feedUri }, - { headers: sc.getHeaders(alice) }, - ) - expect(res.data.feed[0].post.uri).toBe(one.ref.uriStr) - expect(res.data.feed[1].post.uri).toBe(two.ref.uriStr) - const indexOfThird = res.data.feed.findIndex( - (item) => item.post.uri === three.ref.uriStr, - ) - // doesn't quite matter where this cam in but it should be down-regulated pretty severely from gravity - expect(indexOfThird).toBeGreaterThan(3) - }) - - it('paginates', async () => { - if (server.ctx.db.dialect === 'sqlite') return - - const res = await agent.api.app.bsky.feed.getFeed( - { feed: feedUri }, - { headers: sc.getHeaders(alice) }, - ) - const first = await agent.api.app.bsky.feed.getFeed( - { feed: feedUri, limit: 3 }, - { headers: sc.getHeaders(alice) }, - ) - const second = await agent.api.app.bsky.feed.getFeed( - { feed: feedUri, cursor: first.data.cursor }, - { headers: sc.getHeaders(alice) }, - ) - - expect([...first.data.feed, ...second.data.feed]).toEqual(res.data.feed) - }) -}) diff --git a/packages/pds/tests/algos/with-friends.test.ts b/packages/pds/tests/algos/with-friends.test.ts deleted file mode 100644 index 7e6e79357d9..00000000000 --- a/packages/pds/tests/algos/with-friends.test.ts +++ /dev/null @@ -1,148 +0,0 @@ -import AtpAgent, { AtUri } from '@atproto/api' -import { runTestServer, TestServerInfo } from '../_util' -import { RecordRef, SeedClient } from '../seeds/client' -import userSeed from '../seeds/users' -import { makeAlgos } from '../../src' - -describe.skip('algo with friends', () => { - let server: TestServerInfo - let agent: AtpAgent - let sc: SeedClient - - // account dids, for convenience - let alice: string - let bob: string - let carol: string - let dan: string - - const feedPublisherDid = 'did:example:feed-publisher' - const feedUri = AtUri.make( - feedPublisherDid, - 'app.bsky.feed.generator', - 'with-friends', - ).toString() - - beforeAll(async () => { - server = await runTestServer( - { - dbPostgresSchema: 'algo_with_friends', - }, - { - algos: makeAlgos(feedPublisherDid), - }, - ) - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await userSeed(sc) - - alice = sc.dids.alice - bob = sc.dids.bob - carol = sc.dids.carol - dan = sc.dids.dan - - await server.processAll() - }) - - afterAll(async () => { - await server.close() - }) - - let expectedFeed: string[] - - it('setup', async () => { - for (let i = 0; i < 10; i++) { - const name = `user${i}` - await sc.createAccount(name, { - handle: `user${i}.test`, - email: `user${i}@test.com`, - password: 'password', - }) - } - - const hitLikeThreshold = async (ref: RecordRef) => { - for (let i = 0; i < 10; i++) { - const name = `user${i}` - await sc.like(sc.dids[name], ref) - } - } - - // bob and dan are mutuals of alice, all userN are out-of-network. - await sc.follow(alice, bob) - await sc.follow(alice, carol) - await sc.follow(alice, dan) - await sc.follow(bob, alice) - await sc.follow(dan, alice) - const one = await sc.post(bob, 'one') - const two = await sc.post(bob, 'two') - const three = await sc.post(carol, 'three') - const four = await sc.post(carol, 'four') - const five = await sc.post(dan, 'five') - const six = await sc.post(dan, 'six') - const seven = await sc.post(sc.dids.user0, 'seven') - const eight = await sc.post(sc.dids.user0, 'eight') - const nine = await sc.post(sc.dids.user1, 'nine') - const ten = await sc.post(sc.dids.user1, 'ten') - - // 1, 2, 3, 4, 6, 8, 10 hit like threshold - await hitLikeThreshold(one.ref) - await hitLikeThreshold(two.ref) - await hitLikeThreshold(three.ref) - await hitLikeThreshold(four.ref) - await hitLikeThreshold(six.ref) - await hitLikeThreshold(eight.ref) - await hitLikeThreshold(ten.ref) - - // 1, 4, 7, 8, 10 liked by mutual - await sc.like(bob, one.ref) - await sc.like(dan, four.ref) - await sc.like(bob, seven.ref) - await sc.like(dan, eight.ref) - await sc.like(bob, nine.ref) - await sc.like(dan, ten.ref) - - // all liked by non-mutual - await sc.like(carol, one.ref) - await sc.like(carol, two.ref) - await sc.like(carol, three.ref) - await sc.like(carol, four.ref) - await sc.like(carol, five.ref) - await sc.like(carol, six.ref) - await sc.like(carol, seven.ref) - await sc.like(carol, eight.ref) - await sc.like(carol, nine.ref) - await sc.like(carol, ten.ref) - - expectedFeed = [ - ten.ref.uriStr, - eight.ref.uriStr, - four.ref.uriStr, - one.ref.uriStr, - ] - }) - - it('returns popular in & out of network posts', async () => { - const res = await agent.api.app.bsky.feed.getFeed( - { feed: feedUri }, - { headers: sc.getHeaders(alice) }, - ) - const feedUris = res.data.feed.map((i) => i.post.uri) - expect(feedUris).toEqual(expectedFeed) - }) - - it('paginates', async () => { - const res = await agent.api.app.bsky.feed.getFeed( - { feed: feedUri }, - { headers: sc.getHeaders(alice) }, - ) - const first = await agent.api.app.bsky.feed.getFeed( - { feed: feedUri, limit: 2 }, - { headers: sc.getHeaders(alice) }, - ) - const second = await agent.api.app.bsky.feed.getFeed( - { feed: feedUri, cursor: first.data.cursor }, - { headers: sc.getHeaders(alice) }, - ) - - expect([...first.data.feed, ...second.data.feed]).toEqual(res.data.feed) - }) -}) diff --git a/packages/pds/tests/feed-generation.test.ts b/packages/pds/tests/feed-generation.test.ts deleted file mode 100644 index 41b21901ba4..00000000000 --- a/packages/pds/tests/feed-generation.test.ts +++ /dev/null @@ -1,518 +0,0 @@ -import { AtUri, AtpAgent } from '@atproto/api' -import { Handler as SkeletonHandler } from '@atproto/pds/src/lexicon/types/app/bsky/feed/getFeedSkeleton' -import { UnknownFeedError } from '@atproto/api/src/client/types/app/bsky/feed/getFeed' -import { SeedClient } from './seeds/client' -import basicSeed from './seeds/basic' -import { TestNetworkNoAppView } from '@atproto/dev-env' -import { TestFeedGen } from '@atproto/dev-env/src/feed-gen' -import { TID } from '@atproto/common' -import { adminAuth, forSnapshot, paginateAll } from './_util' -import { - FeedViewPost, - GeneratorView, -} from '@atproto/api/src/client/types/app/bsky/feed/defs' -import { SkeletonFeedPost } from '../src/lexicon/types/app/bsky/feed/defs' -import { RecordRef } from './seeds/client' -import { ids } from '../src/lexicon/lexicons' -import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/defs' - -describe('feed generation', () => { - let network: TestNetworkNoAppView - let agent: AtpAgent - let sc: SeedClient - let gen: TestFeedGen - - let alice: string - let feedUriAll: string - let feedUriAllRef: RecordRef - let feedUriEven: string - let feedUriOdd: string // Unsupported by feed gen - let feedUriBadPagination: string - let feedUriPrime: string // Taken-down - let feedUriPrimeRef: RecordRef - - beforeAll(async () => { - network = await TestNetworkNoAppView.create({ - dbPostgresSchema: 'feed_generation', - }) - agent = network.pds.getClient() - sc = new SeedClient(agent) - await basicSeed(sc) - await network.processAll() - alice = sc.dids.alice - const allUri = AtUri.make(alice, 'app.bsky.feed.generator', 'all') - const feedUriBadPagination = AtUri.make( - alice, - 'app.bsky.feed.generator', - 'bad-pagination', - ) - const evenUri = AtUri.make(alice, 'app.bsky.feed.generator', 'even') - const primeUri = AtUri.make(alice, 'app.bsky.feed.generator', 'prime') - gen = await network.createFeedGen({ - [allUri.toString()]: feedGenHandler('all'), - [evenUri.toString()]: feedGenHandler('even'), - [feedUriBadPagination.toString()]: feedGenHandler('bad-pagination'), - [primeUri.toString()]: feedGenHandler('prime'), - }) - }) - - afterAll(async () => { - await network.close() - }) - - it('describes the feed generator', async () => { - const res = await agent.api.app.bsky.feed.describeFeedGenerator() - expect(res.data.did).toBe(network.pds.ctx.cfg.feedGenDid) - }) - - it('feed gen records can be created.', async () => { - const all = await agent.api.app.bsky.feed.generator.create( - { repo: alice, rkey: 'all' }, - { - did: gen.did, - displayName: 'All', - description: 'Provides all feed candidates', - createdAt: new Date().toISOString(), - }, - sc.getHeaders(alice), - ) - const even = await agent.api.app.bsky.feed.generator.create( - { repo: alice, rkey: 'even' }, - { - did: gen.did, - displayName: 'Even', - description: 'Provides even-indexed feed candidates', - createdAt: new Date().toISOString(), - }, - sc.getHeaders(alice), - ) - // Unsupported by feed gen - const odd = await agent.api.app.bsky.feed.generator.create( - { repo: alice, rkey: 'odd' }, - { - did: gen.did, - displayName: 'Temp', // updated in next test - description: 'Temp', // updated in next test - createdAt: new Date().toISOString(), - }, - sc.getHeaders(alice), - ) - const badPagination = await agent.api.app.bsky.feed.generator.create( - { repo: alice, rkey: 'bad-pagination' }, - { - did: gen.did, - displayName: 'Bad Pagination', - description: - 'Provides all feed candidates, blindly ignoring pagination limit', - createdAt: new Date().toISOString(), - }, - sc.getHeaders(alice), - ) - // Taken-down - const prime = await agent.api.app.bsky.feed.generator.create( - { repo: alice, rkey: 'prime' }, - { - did: gen.did, - displayName: 'Prime', - description: 'Provides prime-indexed feed candidates', - createdAt: new Date().toISOString(), - }, - sc.getHeaders(alice), - ) - await agent.api.com.atproto.admin.takeModerationAction( - { - action: TAKEDOWN, - subject: { - $type: 'com.atproto.repo.strongRef', - uri: prime.uri, - cid: prime.cid, - }, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - feedUriAll = all.uri - feedUriAllRef = new RecordRef(all.uri, all.cid) - feedUriEven = even.uri - feedUriOdd = odd.uri - feedUriBadPagination = badPagination.uri - feedUriPrime = prime.uri - feedUriPrimeRef = new RecordRef(prime.uri, prime.cid) - }) - - it('feed gen records can be updated', async () => { - await agent.api.com.atproto.repo.putRecord( - { - repo: alice, - collection: ids.AppBskyFeedGenerator, - rkey: 'odd', - record: { - did: gen.did, - displayName: 'Odd', - description: 'Provides odd-indexed feed candidates', - createdAt: new Date().toISOString(), - }, - }, - { headers: sc.getHeaders(alice), encoding: 'application/json' }, - ) - }) - - it('getActorFeeds fetches feed generators by actor.', async () => { - // add some likes - await sc.like(sc.dids.bob, feedUriAllRef) - await sc.like(sc.dids.carol, feedUriAllRef) - - const results = (results) => results.flatMap((res) => res.feeds) - const paginator = async (cursor?: string) => { - const res = await agent.api.app.bsky.feed.getActorFeeds( - { actor: alice, cursor, limit: 2 }, - { headers: sc.getHeaders(sc.dids.bob) }, - ) - return res.data - } - - const paginatedAll: GeneratorView[] = results(await paginateAll(paginator)) - - expect(paginatedAll.length).toEqual(4) - expect(paginatedAll[0].uri).toEqual(feedUriOdd) - expect(paginatedAll[1].uri).toEqual(feedUriBadPagination) - expect(paginatedAll[2].uri).toEqual(feedUriEven) - expect(paginatedAll[3].uri).toEqual(feedUriAll) - expect(paginatedAll.map((fg) => fg.uri)).not.toContain(feedUriPrime) // taken-down - expect(forSnapshot(paginatedAll)).toMatchSnapshot() - }) - - it('embeds feed generator records in posts', async () => { - const res = await agent.api.app.bsky.feed.post.create( - { repo: sc.dids.bob }, - { - text: 'cool feed!', - embed: { - $type: 'app.bsky.embed.record', - record: feedUriAllRef.raw, - }, - createdAt: new Date().toISOString(), - }, - sc.getHeaders(sc.dids.bob), - ) - const view = await agent.api.app.bsky.feed.getPosts( - { uris: [res.uri] }, - { headers: sc.getHeaders(sc.dids.bob) }, - ) - expect(view.data.posts.length).toBe(1) - expect(forSnapshot(view.data.posts[0])).toMatchSnapshot() - }) - - it('does not embed taken-down feed generator records in posts', async () => { - const res = await agent.api.app.bsky.feed.post.create( - { repo: sc.dids.bob }, - { - text: 'weird feed', - embed: { - $type: 'app.bsky.embed.record', - record: feedUriPrimeRef.raw, - }, - createdAt: new Date().toISOString(), - }, - sc.getHeaders(sc.dids.bob), - ) - const view = await agent.api.app.bsky.feed.getPosts( - { uris: [res.uri] }, - { headers: sc.getHeaders(sc.dids.bob) }, - ) - expect(view.data.posts.length).toBe(1) - expect(forSnapshot(view.data.posts[0])).toMatchSnapshot() - }) - - describe('getFeedGenerator', () => { - it('describes a feed gen & returns online status', async () => { - const resEven = await agent.api.app.bsky.feed.getFeedGenerator( - { feed: feedUriAll }, - { headers: sc.getHeaders(sc.dids.bob) }, - ) - expect(forSnapshot(resEven.data)).toMatchSnapshot() - expect(resEven.data.isOnline).toBe(true) - expect(resEven.data.isValid).toBe(true) - }) - - it('does not describe taken-down feed', async () => { - const tryGetFeed = agent.api.app.bsky.feed.getFeedGenerator( - { feed: feedUriPrime }, - { headers: sc.getHeaders(sc.dids.bob) }, - ) - await expect(tryGetFeed).rejects.toThrow('could not find feed') - }) - - // @TODO temporarily skipping while external feedgens catch-up on describeFeedGenerator - it.skip('handles an unsupported algo', async () => { - const resOdd = await agent.api.app.bsky.feed.getFeedGenerator( - { feed: feedUriOdd }, - { headers: sc.getHeaders(sc.dids.bob) }, - ) - expect(resOdd.data.isOnline).toBe(true) - expect(resOdd.data.isValid).toBe(false) - }) - - // @TODO temporarily skipping while external feedgens catch-up on describeFeedGenerator - it.skip('handles an offline feed', async () => { - // make an invalid feed gen in bob's repo - const allUriBob = AtUri.make( - sc.dids.bob, - 'app.bsky.feed.generator', - 'all', - ) - const bobFg = await network.createFeedGen({ - [allUriBob.toString()]: feedGenHandler('all'), - }) - - await agent.api.app.bsky.feed.generator.create( - { repo: sc.dids.bob, rkey: 'all' }, - { - did: bobFg.did, - displayName: 'All by bob', - description: 'Provides all feed candidates - by bob', - createdAt: new Date().toISOString(), - }, - sc.getHeaders(sc.dids.bob), - ) - - // now take it offline - await bobFg.close() - - const res = await agent.api.app.bsky.feed.getFeedGenerator( - { - feed: allUriBob.toString(), - }, - { headers: sc.getHeaders(sc.dids.alice) }, - ) - expect(res.data.isOnline).toBe(false) - expect(res.data.isValid).toBe(false) - }) - }) - - describe('getFeedGenerators', () => { - it('describes multiple feed gens', async () => { - const resEven = await agent.api.app.bsky.feed.getFeedGenerators( - { feeds: [feedUriEven, feedUriAll, feedUriPrime] }, - { headers: sc.getHeaders(sc.dids.bob) }, - ) - expect(forSnapshot(resEven.data)).toMatchSnapshot() - expect(resEven.data.feeds.map((fg) => fg.uri)).not.toContain(feedUriPrime) // taken-down - }) - }) - - describe('getPopularFeedGenerators', () => { - it('gets popular feed generators', async () => { - const res = await agent.api.app.bsky.unspecced.getPopularFeedGenerators( - {}, - { headers: sc.getHeaders(sc.dids.bob) }, - ) - expect(res.data.feeds.map((f) => f.likeCount)).toEqual([2, 0, 0, 0]) - expect(res.data.feeds.map((f) => f.uri)).not.toContain(feedUriPrime) // taken-down - }) - - it('paginates', async () => { - const resFull = - await agent.api.app.bsky.unspecced.getPopularFeedGenerators( - {}, - { headers: sc.getHeaders(sc.dids.bob) }, - ) - - const resOne = - await agent.api.app.bsky.unspecced.getPopularFeedGenerators( - { limit: 2 }, - { headers: sc.getHeaders(sc.dids.bob) }, - ) - const resTwo = - await agent.api.app.bsky.unspecced.getPopularFeedGenerators( - { cursor: resOne.data.cursor }, - { headers: sc.getHeaders(sc.dids.bob) }, - ) - expect([...resOne.data.feeds, ...resTwo.data.feeds]).toEqual( - resFull.data.feeds, - ) - }) - - it('searches', async () => { - const res = await agent.api.app.bsky.unspecced.getPopularFeedGenerators( - { query: 'pagination' }, - { headers: sc.getHeaders(sc.dids.bob) }, - ) - - expect(res.data.feeds[0].displayName).toBe('Bad Pagination') - }) - }) - - describe('getFeed', () => { - it('resolves basic feed contents.', async () => { - const feed = await agent.api.app.bsky.feed.getFeed( - { feed: feedUriEven }, - { headers: sc.getHeaders(alice) }, - ) - expect(feed.data.feed.map((item) => item.post.uri)).toEqual([ - sc.posts[sc.dids.alice][0].ref.uriStr, - sc.posts[sc.dids.carol][0].ref.uriStr, - sc.replies[sc.dids.carol][0].ref.uriStr, - ]) - expect(forSnapshot(feed.data.feed)).toMatchSnapshot() - }) - - it('paginates, handling replies and reposts.', async () => { - const results = (results) => results.flatMap((res) => res.feed) - const paginator = async (cursor?: string) => { - const res = await agent.api.app.bsky.feed.getFeed( - { feed: feedUriAll, cursor, limit: 2 }, - { headers: sc.getHeaders(alice) }, - ) - return res.data - } - - const paginatedAll: FeedViewPost[] = results(await paginateAll(paginator)) - - // Unknown post uri is omitted - expect(paginatedAll.map((item) => item.post.uri)).toEqual([ - sc.posts[sc.dids.alice][0].ref.uriStr, - sc.posts[sc.dids.bob][0].ref.uriStr, - sc.posts[sc.dids.carol][0].ref.uriStr, - sc.replies[sc.dids.carol][0].ref.uriStr, - sc.posts[sc.dids.dan][1].ref.uriStr, - ]) - expect(forSnapshot(paginatedAll)).toMatchSnapshot() - }) - - it('paginates, handling feed not respecting limit.', async () => { - const res = await agent.api.app.bsky.feed.getFeed( - { feed: feedUriBadPagination, limit: 3 }, - { headers: sc.getHeaders(alice) }, - ) - // refused to respect pagination limit, so it got cut short by appview but the cursor remains. - expect(res.data.feed.length).toBeLessThanOrEqual(3) - expect(parseInt(res.data.cursor || '', 10)).toBeGreaterThanOrEqual(3) - expect(res.data.feed.map((item) => item.post.uri)).toEqual([ - sc.posts[sc.dids.alice][0].ref.uriStr, - sc.posts[sc.dids.bob][0].ref.uriStr, - sc.posts[sc.dids.carol][0].ref.uriStr, - ]) - }) - - it('fails on unknown feed.', async () => { - const tryGetFeed = agent.api.app.bsky.feed.getFeed( - { feed: feedUriOdd }, - { headers: sc.getHeaders(alice) }, - ) - await expect(tryGetFeed).rejects.toThrow(UnknownFeedError) - }) - - it('resolves contents of taken-down feed.', async () => { - const tryGetFeed = agent.api.app.bsky.feed.getFeed( - { feed: feedUriPrime }, - { headers: sc.getHeaders(alice) }, - ) - await expect(tryGetFeed).resolves.toBeDefined() - }) - - it('receives proper auth details.', async () => { - const feed = await agent.api.app.bsky.feed.getFeed( - { feed: feedUriEven }, - { headers: sc.getHeaders(alice) }, - ) - expect(feed.data['$auth']?.['aud']).toEqual(gen.did) - expect(feed.data['$auth']?.['iss']).toEqual(alice) - }) - - it('receives proper auth details.', async () => { - const feed = await agent.api.app.bsky.feed.getFeed( - { feed: feedUriEven }, - { headers: sc.getHeaders(alice) }, - ) - expect(feed.data['$auth']?.['aud']).toEqual(gen.did) - expect(feed.data['$auth']?.['iss']).toEqual(alice) - }) - - it('provides timing info in server-timing header.', async () => { - const result = await agent.api.app.bsky.feed.getFeed( - { feed: feedUriEven }, - { headers: sc.getHeaders(alice) }, - ) - expect(result.headers['server-timing']).toMatch( - /^skele;dur=\d+, hydr;dur=\d+$/, - ) - }) - - it('returns an upstream failure error when the feed is down.', async () => { - await gen.close() // @NOTE must be last test - const tryGetFeed = agent.api.app.bsky.feed.getFeed( - { feed: feedUriEven }, - { headers: sc.getHeaders(alice) }, - ) - await expect(tryGetFeed).rejects.toThrow('feed unavailable') - }) - }) - - const feedGenHandler = - (feedName: 'even' | 'all' | 'prime' | 'bad-pagination'): SkeletonHandler => - async ({ req, params }) => { - const { limit, cursor } = params - const candidates: SkeletonFeedPost[] = [ - { post: sc.posts[sc.dids.alice][0].ref.uriStr }, - { post: sc.posts[sc.dids.bob][0].ref.uriStr }, - { post: sc.posts[sc.dids.carol][0].ref.uriStr }, - { post: `at://did:plc:unknown/app.bsky.feed.post/${TID.nextStr()}` }, // Doesn't exist - { post: sc.replies[sc.dids.carol][0].ref.uriStr }, // Reply - // Repost (accurate) - { - post: sc.posts[sc.dids.dan][1].ref.uriStr, - reason: { - $type: 'app.bsky.feed.defs#skeletonReasonRepost', - repost: sc.reposts[sc.dids.carol][0].uriStr, - }, - }, - // Repost (inaccurate) - { - post: sc.posts[alice][1].ref.uriStr, - reason: { - $type: 'app.bsky.feed.defs#skeletonReasonRepost', - repost: sc.reposts[sc.dids.carol][0].uriStr, - }, - }, - ] - const offset = cursor ? parseInt(cursor, 10) : 0 - const fullFeed = candidates.filter((_, i) => { - if (feedName === 'even') { - return i % 2 === 0 - } - if (feedName === 'prime') { - return [2, 3, 5, 7, 11, 13].includes(i) - } - return true - }) - const feedResults = - feedName === 'bad-pagination' - ? fullFeed.slice(offset) // does not respect limit - : fullFeed.slice(offset, offset + limit) - const lastResult = feedResults.at(-1) - return { - encoding: 'application/json', - body: { - feed: feedResults, - cursor: lastResult - ? (fullFeed.indexOf(lastResult) + 1).toString() - : undefined, - $auth: jwtBody(req.headers.authorization), // for testing purposes - }, - } - } -}) - -const jwtBody = (authHeader?: string): Record | undefined => { - if (!authHeader?.startsWith('Bearer')) return undefined - const jwt = authHeader.replace('Bearer ', '') - const [, bodyb64] = jwt.split('.') - const body = JSON.parse(Buffer.from(bodyb64, 'base64').toString()) - if (!body || typeof body !== 'object') return undefined - return body -} diff --git a/packages/pds/tests/labeler/apply-labels.test.ts b/packages/pds/tests/labeler/apply-labels.test.ts index 756fd2c13fd..80fedc459d5 100644 --- a/packages/pds/tests/labeler/apply-labels.test.ts +++ b/packages/pds/tests/labeler/apply-labels.test.ts @@ -1,32 +1,25 @@ import AtpAgent from '@atproto/api' -import { - adminAuth, - CloseFn, - moderatorAuth, - runTestServer, - TestServerInfo, -} from '../_util' +import { TestNetworkNoAppView } from '@atproto/dev-env' +import { adminAuth, moderatorAuth } from '../_util' import { SeedClient } from '../seeds/client' import basicSeed from '../seeds/basic' describe('unspecced.applyLabels', () => { - let server: TestServerInfo - let close: CloseFn + let network: TestNetworkNoAppView let agent: AtpAgent let sc: SeedClient beforeAll(async () => { - server = await runTestServer({ + network = await TestNetworkNoAppView.create({ dbPostgresSchema: 'apply_labels', }) - close = server.close - agent = new AtpAgent({ service: server.url }) + agent = network.pds.getClient() sc = new SeedClient(agent) await basicSeed(sc) }) afterAll(async () => { - await close() + await network.close() }) it('requires admin auth.', async () => { @@ -34,7 +27,7 @@ describe('unspecced.applyLabels', () => { { labels: [ { - src: server.ctx.cfg.labelerDid, + src: network.pds.ctx.cfg.labelerDid, uri: sc.dids.carol, val: 'cats', neg: false, @@ -56,7 +49,7 @@ describe('unspecced.applyLabels', () => { { labels: [ { - src: server.ctx.cfg.labelerDid, + src: network.pds.ctx.cfg.labelerDid, uri: post.uriStr, cid: post.cidStr, val: 'birds', @@ -64,7 +57,7 @@ describe('unspecced.applyLabels', () => { cts: new Date().toISOString(), }, { - src: server.ctx.cfg.labelerDid, + src: network.pds.ctx.cfg.labelerDid, uri: post.uriStr, cid: post.cidStr, val: 'bats', @@ -86,7 +79,7 @@ describe('unspecced.applyLabels', () => { { labels: [ { - src: server.ctx.cfg.labelerDid, + src: network.pds.ctx.cfg.labelerDid, uri: post.uriStr, cid: post.cidStr, val: 'birds', @@ -94,7 +87,7 @@ describe('unspecced.applyLabels', () => { cts: new Date().toISOString(), }, { - src: server.ctx.cfg.labelerDid, + src: network.pds.ctx.cfg.labelerDid, uri: post.uriStr, cid: post.cidStr, val: 'bats', @@ -116,14 +109,14 @@ describe('unspecced.applyLabels', () => { { labels: [ { - src: server.ctx.cfg.labelerDid, + src: network.pds.ctx.cfg.labelerDid, uri: sc.dids.carol, val: 'birds', neg: false, cts: new Date().toISOString(), }, { - src: server.ctx.cfg.labelerDid, + src: network.pds.ctx.cfg.labelerDid, uri: sc.dids.carol, val: 'bats', neg: false, @@ -144,14 +137,14 @@ describe('unspecced.applyLabels', () => { { labels: [ { - src: server.ctx.cfg.labelerDid, + src: network.pds.ctx.cfg.labelerDid, uri: sc.dids.carol, val: 'birds', neg: true, cts: new Date().toISOString(), }, { - src: server.ctx.cfg.labelerDid, + src: network.pds.ctx.cfg.labelerDid, uri: sc.dids.carol, val: 'bats', neg: true, diff --git a/packages/pds/tests/migrations/blob-creator.test.ts b/packages/pds/tests/migrations/blob-creator.test.ts deleted file mode 100644 index bc9cc72faf4..00000000000 --- a/packages/pds/tests/migrations/blob-creator.test.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { Database } from '../../src' -import { randomStr } from '@atproto/crypto' -import { cidForCbor, TID } from '@atproto/common' -import { Kysely } from 'kysely' -import { AtUri } from '@atproto/syntax' - -describe.skip('blob creator migration', () => { - let db: Database - let rawDb: Kysely - - beforeAll(async () => { - if (process.env.DB_POSTGRES_URL) { - db = Database.postgres({ - url: process.env.DB_POSTGRES_URL, - schema: 'migration_blob_creator', - }) - } else { - db = Database.memory() - } - await db.migrateToOrThrow('_20230310T205728933Z') - - rawDb = db.db - }) - - afterAll(async () => { - await db.close() - }) - - const dids = ['did:example:alice', 'did:example:bob', 'did:example:carol'] - const getCidStr = async () => { - const cid = await cidForCbor({ test: randomStr(20, 'base32') }) - return cid.toString() - } - - const repoBlob = async (did: string, cid: string) => { - const uri = AtUri.make(did, 'com.atproto.collection', TID.nextStr()) - return { - cid, - recordUri: uri.toString(), - commit: await getCidStr(), - did, - takedownId: null, - } - } - - let blobsSnap - let repoBlobsSnap - - it('creates a some blobs', async () => { - const blobs: any[] = [] - const repoBlobs: any[] = [] - for (let i = 0; i < 1000; i++) { - const cid = await getCidStr() - blobs.push({ - cid, - mimeType: 'image/jpeg', - size: Math.floor(Math.random() * 1000000), - tempKey: null, - width: Math.floor(Math.random() * 1000), - height: Math.floor(Math.random() * 1000), - createdAt: new Date().toISOString(), - }) - if (i % 2 === 0) { - repoBlobs.push(await repoBlob(dids[0], cid)) - } else { - repoBlobs.push(await repoBlob(dids[1], cid)) - } - - if (i % 5 === 0) { - repoBlobs.push(await repoBlob(dids[2], cid)) - } - } - await rawDb.insertInto('blob').values(blobs).execute() - await rawDb.insertInto('repo_blob').values(repoBlobs).execute() - - blobsSnap = await rawDb - .selectFrom('blob') - .selectAll() - .orderBy('cid') - .execute() - repoBlobsSnap = await rawDb - .selectFrom('repo_blob') - .selectAll() - .orderBy('cid') - .orderBy('did') - .execute() - }) - - it('migrates up', async () => { - const migration = await db.migrator.migrateTo('_20230313T232322844Z') - expect(migration.error).toBeUndefined() - }) - - it('correctly migrated data', async () => { - const blobs = await rawDb - .selectFrom('blob') - .selectAll() - .orderBy('cid') - .orderBy('creator') - .execute() - const repoBlobs = await rawDb - .selectFrom('repo_blob') - .selectAll() - .orderBy('cid') - .orderBy('did') - .execute() - - expect(blobs.length).toBe(repoBlobs.length) - expect(repoBlobs.length).toBe(repoBlobsSnap.length) - - for (const blob of blobs) { - const snapped = blobsSnap.find((b) => b.cid === blob.cid) - const { creator, ...rest } = blob - expect(snapped).toEqual(rest) - const found = repoBlobsSnap.find( - (b) => b.cid === blob.cid && b.did === creator, - ) - expect(found).toBeDefined() - } - }) - - it('migrates down', async () => { - const migration = await db.migrator.migrateTo('_20230310T205728933Z') - expect(migration.error).toBeUndefined() - - const updatedBlobs = await rawDb - .selectFrom('blob') - .selectAll() - .orderBy('cid') - .execute() - const updatedRepoBlobs = await rawDb - .selectFrom('repo_blob') - .selectAll() - .orderBy('cid') - .orderBy('did') - .execute() - - expect(updatedBlobs).toEqual(blobsSnap) - expect(updatedRepoBlobs).toEqual(repoBlobsSnap) - }) -}) diff --git a/packages/pds/tests/migrations/indexed-at-on-record.test.ts b/packages/pds/tests/migrations/indexed-at-on-record.test.ts deleted file mode 100644 index 664ae3e16cd..00000000000 --- a/packages/pds/tests/migrations/indexed-at-on-record.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Database } from '../../src' -import { randomStr } from '@atproto/crypto' -import { dataToCborBlock, TID } from '@atproto/common' -import { AtUri } from '@atproto/syntax' -import { Kysely } from 'kysely' - -describe.skip('indexedAt on record migration', () => { - let db: Database - let rawDb: Kysely - - beforeAll(async () => { - if (process.env.DB_POSTGRES_URL) { - db = Database.postgres({ - url: process.env.DB_POSTGRES_URL, - schema: 'migration_indexed_at_on_record', - }) - } else { - db = Database.memory() - } - - await db.migrateToOrThrow('_20221230T215012029Z') - rawDb = db.db - }) - - afterAll(async () => { - await db.close() - }) - - const randomDate = () => { - const start = new Date(2022, 0, 1) - const end = new Date() - return new Date( - start.getTime() + Math.random() * (end.getTime() - start.getTime()), - ).toISOString() - } - - const times: { [cid: string]: string } = {} - - it('fills the db with some records & blocks', async () => { - const blocks: any[] = [] - const records: any[] = [] - for (let i = 0; i < 100; i++) { - const date = randomDate() - const record = { test: randomStr(8, 'base32') } - const block = await dataToCborBlock(record) - blocks.push({ - cid: block.cid.toString(), - content: block.bytes, - size: block.bytes.length, - indexedAt: date, - }) - const uri = AtUri.make('did:example:alice', 'fake.posts', TID.nextStr()) - records.push({ - uri: uri.toString(), - cid: block.cid.toString(), - did: uri.hostname, - collection: uri.collection, - rkey: uri.rkey, - }) - times[block.cid.toString()] = date - } - - await rawDb.insertInto('ipld_block').values(blocks).execute() - await rawDb.insertInto('record').values(records).execute() - }) - - it('migrates up', async () => { - await db.migrateToOrThrow('_20230127T215753149Z') - }) - - it('associated the date to the correct record', async () => { - const res = await rawDb.selectFrom('record').selectAll().execute() - res.forEach((row) => { - expect(row.indexedAt).toEqual(times[row.cid]) - }) - }) -}) diff --git a/packages/pds/tests/migrations/repo-sync-data-pt2.test.ts b/packages/pds/tests/migrations/repo-sync-data-pt2.test.ts deleted file mode 100644 index 91971fb7043..00000000000 --- a/packages/pds/tests/migrations/repo-sync-data-pt2.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -import AtpAgent from '@atproto/api' -import { Database } from '../../src' -import { Kysely } from 'kysely' -import { CloseFn, runTestServer } from '../_util' -import { SeedClient } from '../seeds/client' -import basicSeed from '../seeds/basic' - -describe.skip('repo sync data migration', () => { - let db: Database - let rawDb: Kysely - let close: CloseFn - - beforeAll(async () => { - const server = await runTestServer({ - dbPostgresSchema: 'migration_repo_sync_data_pt_two', - }) - db = server.ctx.db - rawDb = db.db - close = server.close - const agent = new AtpAgent({ service: server.url }) - const sc = new SeedClient(agent) - await basicSeed(sc) - }) - - afterAll(async () => { - await close() - }) - - it('migrates down to pt2', async () => { - await db.migrateToOrThrow('_20230201T200606704Z') - }) - - const getSnapshot = async () => { - const [history, blocks] = await Promise.all([ - rawDb - .selectFrom('repo_commit_history') - .selectAll() - .orderBy('creator') - .orderBy('commit') - .execute(), - rawDb - .selectFrom('repo_commit_block') - .selectAll() - .orderBy('creator') - .orderBy('commit') - .orderBy('block') - .execute(), - ]) - return { history, blocks } - } - - let snapshot: { history: any[]; blocks: any[] } - - it('snapshots current state of commits', async () => { - snapshot = await getSnapshot() - }) - - it('migrates down to pt1', async () => { - await db.migrateToOrThrow('_20230127T224743452Z') - }) - - it('deletes some random commits', async () => { - const toDelete: string[] = [] - for (const commit of snapshot.history) { - if (Math.random() < 0.2) { - toDelete.push(commit.commit) - } - } - await rawDb - .deleteFrom('repo_commit_block') - .where('commit', 'in', toDelete) - .execute() - await rawDb - .deleteFrom('repo_commit_history') - .where('commit', 'in', toDelete) - .execute() - }) - - it('migrates up', async () => { - await db.migrateToOrThrow('_20230201T200606704Z') - }) - - it('backfilled missing commits', async () => { - const newSnapshot = await getSnapshot() - expect(newSnapshot).toEqual(snapshot) - }) -}) diff --git a/packages/pds/tests/migrations/repo-sync-data.test.ts b/packages/pds/tests/migrations/repo-sync-data.test.ts deleted file mode 100644 index 2fd17c199eb..00000000000 --- a/packages/pds/tests/migrations/repo-sync-data.test.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { Database } from '../../src' -import { MemoryBlockstore, Repo, WriteOpAction } from '@atproto/repo' -import { P256Keypair, Keypair, randomStr } from '@atproto/crypto' -import { TID } from '@atproto/common' -import { Kysely } from 'kysely' -import { CID } from 'multiformats/cid' - -describe.skip('repo sync data migration', () => { - let db: Database - let rawDb: Kysely - let memoryStore: MemoryBlockstore - let keypair: Keypair - let repo: Repo - - const aliceDid = 'did:example:alice' - const bobDid = 'did:example:bob' - - beforeAll(async () => { - if (process.env.DB_POSTGRES_URL) { - db = Database.postgres({ - url: process.env.DB_POSTGRES_URL, - schema: 'migration_repo_sync_data', - }) - } else { - db = Database.memory() - } - await db.migrateToOrThrow('_20230127T215753149Z') - rawDb = db.db - memoryStore = new MemoryBlockstore() - keypair = await P256Keypair.create() - repo = await Repo.create(memoryStore, keypair.did(), keypair) - }) - - afterAll(async () => { - await db.close() - }) - - it('fills the db with some repo data', async () => { - for (let i = 0; i < 100; i++) { - repo = await repo.applyWrites( - { - action: WriteOpAction.Create, - collection: randomStr(8, 'base32'), - rkey: TID.nextStr(), - record: { name: randomStr(32, 'base32') }, - }, - keypair, - ) - } - const blocks: any[] = [] - const creators: any[] = [] - for (const entry of memoryStore.blocks.entries()) { - blocks.push({ - cid: entry.cid.toString(), - size: entry.bytes.length, - content: entry.bytes, - }) - creators.push({ - cid: entry.cid.toString(), - did: aliceDid, - }) - const registerTwice = Math.random() > 0.1 - if (registerTwice) { - creators.push({ - cid: entry.cid.toString(), - did: bobDid, - }) - } - } - const rawDb: Kysely = db.db - await Promise.all([ - rawDb.insertInto('ipld_block').values(blocks).execute(), - rawDb.insertInto('ipld_block_creator').values(creators).execute(), - rawDb - .insertInto('repo_root') - .values([ - { - did: aliceDid, - root: repo.cid.toString(), - indexedAt: new Date().toISOString(), - }, - { - did: bobDid, - root: repo.cid.toString(), - indexedAt: new Date().toISOString(), - }, - ]) - .execute(), - ]) - }) - - it('migrates up', async () => { - const migration = await db.migrator.migrateTo('_20230201T200606704Z') - expect(migration.error).toBeUndefined() - }) - - it('fills in missing block creators', async () => { - const aliceBlocks = await rawDb - .selectFrom('ipld_block_creator') - .selectAll() - .where('did', '=', aliceDid) - .execute() - const bobBlocks = await rawDb - .selectFrom('ipld_block_creator') - .selectAll() - .where('did', '=', bobDid) - .execute() - - const aliceCids = aliceBlocks.map((row) => row.cid).sort() - const bobCids = bobBlocks.map((row) => row.cid).sort() - - expect(aliceCids).toEqual(bobCids) - }) - - it('correctly constructs repo history', async () => { - const checkRepoContents = async (did: string) => { - const history = await rawDb - .selectFrom('repo_commit_history') - .where('creator', '=', did) - .selectAll() - .execute() - const blocks = await rawDb - .selectFrom('repo_commit_block') - .where('creator', '=', did) - .selectAll() - .execute() - const commits = await memoryStore.getCommits(repo.cid, null) - if (!commits) { - throw new Error('Could not get commit log from memoryStore') - } - for (let i = 0; i < commits.length; i++) { - const commit = commits[i] - const prev = commits[i - 1]?.commit?.toString() || null - const filteredHistory = history.filter( - (row) => row.commit === commit.commit.toString(), - ) - expect(filteredHistory.length).toBe(1) - expect(filteredHistory[0].prev).toEqual(prev) - - const filteredBlocks = blocks.filter( - (row) => row.commit === commit.commit.toString(), - ) - expect(filteredBlocks.length).toEqual(commit.blocks.size) - filteredBlocks.forEach((block) => { - expect(commit.blocks.has(CID.parse(block.block))).toBeTruthy() - }) - } - } - - await checkRepoContents(aliceDid) - await checkRepoContents(bobDid) - }) -}) diff --git a/packages/pds/tests/migrations/user-partitioned-cids.test.ts b/packages/pds/tests/migrations/user-partitioned-cids.test.ts deleted file mode 100644 index e6eff220445..00000000000 --- a/packages/pds/tests/migrations/user-partitioned-cids.test.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { Database } from '../../src' -import { randomStr } from '@atproto/crypto' -import { dataToCborBlock } from '@atproto/common' -import { Kysely } from 'kysely' -import { Block } from 'multiformats/block' -import * as uint8arrays from 'uint8arrays' - -describe.skip('user partitioned cids migration', () => { - let db: Database - let rawDb: Kysely - - beforeAll(async () => { - if (process.env.DB_POSTGRES_URL) { - db = Database.postgres({ - url: process.env.DB_POSTGRES_URL, - schema: 'migration_user_partitioned_cids', - }) - } else { - db = Database.memory() - } - await db.migrateToOrThrow('_20230201T200606704Z') - - rawDb = db.db - }) - - afterAll(async () => { - await db.close() - }) - - const dids = ['did:example:one', 'did:example:two', 'did:example:three'] - const blocks: Block[] = [] - - it('fills the db with some cids', async () => { - for (let i = 0; i < 4; i++) { - const block = await dataToCborBlock({ - test: randomStr(32, 'base32'), - }) - - blocks.push(block) - } - - const blocksToInsert = blocks.map((b) => ({ - cid: b.cid.toString(), - size: b.bytes.length, - content: b.bytes, - })) - await rawDb.insertInto('ipld_block').values(blocksToInsert).execute() - - // block 0 is owned by only user 0 - // block 1 is owned by only user 1 - // block 2 is owned by users 0 & 1 - // block 3 is owned by all three users - const creatorsToInsert = [ - { cid: blocks[0].cid.toString(), did: dids[0] }, - { cid: blocks[1].cid.toString(), did: dids[1] }, - { cid: blocks[2].cid.toString(), did: dids[0] }, - { cid: blocks[2].cid.toString(), did: dids[1] }, - { cid: blocks[3].cid.toString(), did: dids[0] }, - { cid: blocks[3].cid.toString(), did: dids[1] }, - { cid: blocks[3].cid.toString(), did: dids[2] }, - ] - await rawDb - .insertInto('ipld_block_creator') - .values(creatorsToInsert) - .execute() - }) - - it('migrates up', async () => { - const migration = await db.migrator.migrateTo('_20230202T170426672Z') - expect(migration.error).toBeUndefined() - }) - - it('correctly partitions user ipld blocks', async () => { - const fromDb = await rawDb.selectFrom('ipld_block').selectAll().execute() - - const first = fromDb.filter((b) => b.cid === blocks[0].cid.toString()) - expect(first.length).toBe(1) - expect(first[0].creator).toEqual(dids[0]) - expect(uint8arrays.equals(first[0].content, blocks[0].bytes)).toBeTruthy() - - const second = fromDb.filter((b) => b.cid === blocks[1].cid.toString()) - expect(second.length).toBe(1) - expect(second[0].creator).toEqual(dids[1]) - expect(uint8arrays.equals(second[0].content, blocks[1].bytes)).toBeTruthy() - - const third = fromDb.filter((b) => b.cid === blocks[2].cid.toString()) - expect(third.length).toBe(2) - const thirdCreators = third.map((row) => row.creator) - expect(thirdCreators.sort()).toEqual(dids.slice(0, 2).sort()) - third.forEach((row) => { - expect(uint8arrays.equals(row.content, blocks[2].bytes)).toBeTruthy() - }) - - const fourth = fromDb.filter((b) => b.cid === blocks[3].cid.toString()) - expect(fourth.length).toBe(3) - const fourthCreators = fourth.map((row) => row.creator) - expect(fourthCreators.sort()).toEqual(dids.sort()) - fourth.forEach((row) => { - expect(uint8arrays.equals(row.content, blocks[3].bytes)).toBeTruthy() - }) - }) -}) diff --git a/packages/pds/tests/migrations/user-table-did-pkey.test.ts b/packages/pds/tests/migrations/user-table-did-pkey.test.ts deleted file mode 100644 index 881907e71b8..00000000000 --- a/packages/pds/tests/migrations/user-table-did-pkey.test.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { Database } from '../../src' -import { randomStr } from '@atproto/crypto' -import { Kysely } from 'kysely' - -describe.skip('user table did pkey migration', () => { - let db: Database - let rawDb: Kysely - - beforeAll(async () => { - if (process.env.DB_POSTGRES_URL) { - db = Database.postgres({ - url: process.env.DB_POSTGRES_URL, - schema: 'migration_user_table_did_pkey', - }) - } else { - db = Database.memory() - } - await db.migrateToOrThrow('_20230202T172831900Z') - - rawDb = db.db - }) - - afterAll(async () => { - await db.close() - }) - - const randDateBefore = (beforeUnix: number): Date => { - return new Date(beforeUnix - Math.floor(1000000 * Math.random())) - } - - let userSnap - let didHandleSnap - - it('creates a bunch of users', async () => { - const didHandles: any[] = [] - const users: any[] = [] - for (let i = 0; i < 1000; i++) { - const did = `did:plc:${randomStr(24, 'base32')}` - const handle = `${randomStr(8, 'base32')}.bsky.social` - const email = `${randomStr(8, 'base32')}@test.com` - const password = randomStr(32, 'base32') - const lastSeenNotifs = randDateBefore(Date.now()) - const createdAt = randDateBefore(lastSeenNotifs.getTime()) - const passwordResetToken = - Math.random() > 0.9 ? randomStr(4, 'base32') : null - const passwordResetGrantedAt = - Math.random() > 0.5 ? randDateBefore(lastSeenNotifs.getTime()) : null - didHandles.push({ did, handle }) - users.push({ - handle, - email, - password, - lastSeenNotifs: lastSeenNotifs.toISOString(), - createdAt: createdAt.toISOString(), - passwordResetToken, - passwordResetGrantedAt: passwordResetGrantedAt?.toISOString() || null, - }) - } - await rawDb.insertInto('did_handle').values(didHandles).execute() - await rawDb.insertInto('user').values(users).execute() - - didHandleSnap = await rawDb - .selectFrom('did_handle') - .selectAll() - .orderBy('did') - .execute() - userSnap = await rawDb - .selectFrom('user') - .selectAll() - .orderBy('email') - .execute() - - // console.log( - // await rawDb.selectFrom('user').select('lastSeenNotifs').execute(), - // ) - }) - - it('migrates up', async () => { - const migration = await db.migrator.migrateTo('_20230208T222001557Z') - expect(migration.error).toBeUndefined() - }) - - it('correctly migrated data', async () => { - const updatedDidHandle = await rawDb - .selectFrom('did_handle') - .selectAll() - .orderBy('did') - .execute() - const updatedUser = await rawDb - .selectFrom('user_account') - .selectAll() - .orderBy('email') - .execute() - const updatedUserState = await rawDb - .selectFrom('user_state') - .selectAll() - .orderBy('did') - .execute() - - expect(updatedDidHandle).toEqual(didHandleSnap) - - expect(updatedUser.length).toBe(userSnap.length) - for (let i = 0; i < updatedUser.length; i++) { - const { handle, password, lastSeenNotifs, ...rest } = userSnap[i] - const expectedDid = didHandleSnap.find((row) => row.handle === handle).did - const expected = { did: expectedDid, passwordScrypt: password, ...rest } - expect(updatedUser[i]).toEqual(expected) - const lastSeen = updatedUserState.find( - (row) => row.did === expectedDid, - )?.lastSeenNotifs - expect(lastSeen).toEqual(lastSeenNotifs) - } - }) - - it('migrates down', async () => { - const migration = await db.migrator.migrateTo('_20230202T172831900Z') - expect(migration.error).toBeUndefined() - - const updatedDidHandle = await rawDb - .selectFrom('did_handle') - .selectAll() - .orderBy('did') - .execute() - const updatedUser = await rawDb - .selectFrom('user') - .selectAll() - .orderBy('email') - .execute() - - expect(updatedDidHandle).toEqual(didHandleSnap) - expect(updatedUser).toEqual(userSnap) - }) -}) diff --git a/packages/pds/tests/views/__snapshots__/author-feed.test.ts.snap b/packages/pds/tests/views/__snapshots__/author-feed.test.ts.snap deleted file mode 100644 index ca56abeb646..00000000000 --- a/packages/pds/tests/views/__snapshots__/author-feed.test.ts.snap +++ /dev/null @@ -1,2495 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`pds author feed views fetches full author feeds for self (sorted, minimal viewer state). 1`] = ` -Array [ - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(3)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(5)", - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(3)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(6)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(2)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(2)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(9)", - "muted": false, - }, - }, - "cid": "cids(7)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(3)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(12)", - "following": "record(11)", - "muted": false, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(10)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(9)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(10)", - "uri": "record(13)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(8)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(8)", - "uri": "record(10)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(0)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(6)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(7)", - "val": "test-label", - }, - ], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(7)", - "uri": "record(8)", - }, - }, - "text": "yoohoo label_me", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(7)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(11)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(11)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(14)", - "val": "self-label", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "labels": Object { - "$type": "com.atproto.label.defs#selfLabels", - "values": Array [ - Object { - "val": "self-label", - }, - ], - }, - "text": "hey there", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(14)", - "viewer": Object {}, - }, - }, -] -`; - -exports[`pds author feed views fetches full author feeds for self (sorted, minimal viewer state). 2`] = ` -Array [ - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(0)", - "val": "test-label", - }, - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(0)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(2)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(2)", - }, - "root": Object { - "cid": "cids(3)", - "uri": "record(2)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(0)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(5)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(5)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(4)", - "following": "record(3)", - "muted": false, - }, - }, - "cid": "cids(3)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object { - "like": "record(6)", - }, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(5)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(5)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(4)", - "following": "record(3)", - "muted": false, - }, - }, - "cid": "cids(3)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object { - "like": "record(6)", - }, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(5)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000+00:00", - "text": "bobby boy here", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(7)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(6)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(8)", - "viewer": Object {}, - }, - }, -] -`; - -exports[`pds author feed views fetches full author feeds for self (sorted, minimal viewer state). 3`] = ` -Array [ - Object { - "post": Object { - "author": Object { - "did": "user(0)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(0)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(1)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(3)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(4)", - "val": "self-label-a", - }, - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(4)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(3)", - "muted": false, - }, - }, - "cid": "cids(4)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(2)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(1)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(2)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(3)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(4)", - "uri": "record(2)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(1)", - "uri": "record(1)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(1)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object { - "repost": "record(5)", - }, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(6)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(7)", - "uri": "record(7)", - }, - "root": Object { - "cid": "cids(7)", - "uri": "record(7)", - }, - }, - "text": "of course", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(6)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(8)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(10)", - "val": "self-label-a", - }, - Object { - "cid": "cids(8)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(10)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(9)", - "following": "record(8)", - "muted": false, - }, - }, - "cid": "cids(7)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(7)", - "viewer": Object { - "like": "record(11)", - }, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(8)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(10)", - "val": "self-label-a", - }, - Object { - "cid": "cids(8)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(10)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(9)", - "following": "record(8)", - "muted": false, - }, - }, - "cid": "cids(7)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(7)", - "viewer": Object { - "like": "record(11)", - }, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(1)", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(3)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(4)", - "val": "self-label-a", - }, - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(4)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(3)", - "muted": false, - }, - }, - "cid": "cids(4)", - "embeds": Array [], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(2)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(2)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(3)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(4)", - "uri": "record(2)", - }, - }, - }, - "text": "hi im carol", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(1)", - "viewer": Object {}, - }, - }, -] -`; - -exports[`pds author feed views fetches full author feeds for self (sorted, minimal viewer state). 4`] = ` -Array [ - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(4)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(3)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object { - "repost": "record(5)", - }, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(8)", - "muted": false, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(4)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(4)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(3)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(3)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(4)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(3)", - "viewer": Object { - "like": "record(7)", - "repost": "record(6)", - }, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(3)", - "viewer": Object { - "like": "record(7)", - "repost": "record(6)", - }, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(6)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(3)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(7)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(8)", - "muted": false, - }, - }, - "cid": "cids(9)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(12)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(11)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(8)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(9)", - "uri": "record(12)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(7)", - "uri": "record(11)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(0)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(10)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(10)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "text": "dan here!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(13)", - "viewer": Object {}, - }, - }, -] -`; - -exports[`pds author feed views omits reposts from muted users. 1`] = ` -Array [ - Object { - "post": Object { - "author": Object { - "did": "user(0)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(0)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": true, - }, - }, - "cid": "cids(0)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "following": "record(3)", - "muted": false, - }, - }, - "cid": "cids(1)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(3)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(5)", - "val": "self-label-a", - }, - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(5)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(4)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(4)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(2)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(2)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(3)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(4)", - "uri": "record(4)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(1)", - "uri": "record(2)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(1)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(0)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(0)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": true, - }, - }, - "cid": "cids(6)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "text": "dan here!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(6)", - "viewer": Object {}, - }, - }, -] -`; - -exports[`pds author feed views reflects fetching user's state in the feed. 1`] = ` -Array [ - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(5)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(4)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(8)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(8)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(7)", - "muted": false, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(5)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(5)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(4)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(4)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(5)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(4)", - "viewer": Object { - "like": "record(6)", - }, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(6)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(2)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(2)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(7)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(3)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(11)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(9)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(10)", - "uri": "record(12)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(10)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(8)", - "uri": "record(11)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(0)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(6)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(9)", - "val": "test-label", - }, - ], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(7)", - "uri": "record(10)", - }, - }, - "text": "yoohoo label_me", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(9)", - "viewer": Object { - "like": "record(13)", - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(4)", - "viewer": Object { - "like": "record(6)", - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(11)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(11)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(14)", - "val": "self-label", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "labels": Object { - "$type": "com.atproto.label.defs#selfLabels", - "values": Array [ - Object { - "val": "self-label", - }, - ], - }, - "text": "hey there", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(14)", - "viewer": Object {}, - }, - }, -] -`; diff --git a/packages/pds/tests/views/__snapshots__/blocks.test.ts.snap b/packages/pds/tests/views/__snapshots__/blocks.test.ts.snap deleted file mode 100644 index 9b168ae33a5..00000000000 --- a/packages/pds/tests/views/__snapshots__/blocks.test.ts.snap +++ /dev/null @@ -1,383 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`pds views with blocking blocks record embeds 1`] = ` -Object { - "thread": Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewBlocked", - "author": Object { - "did": "user(2)", - "viewer": Object { - "blockedBy": false, - "blocking": "record(5)", - }, - }, - "blocked": true, - "uri": "record(4)", - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(3)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(3)", - "uri": "record(4)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(0)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(0)", - "val": "test-label", - }, - ], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(2)", - "uri": "record(3)", - }, - }, - "text": "yoohoo label_me", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(0)", - "viewer": Object {}, - }, - }, -} -`; - -exports[`pds views with blocking blocks thread parent 1`] = ` -Object { - "thread": Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "parent": Object { - "$type": "app.bsky.feed.defs#blockedPost", - "author": Object { - "did": "user(1)", - "viewer": Object { - "blockedBy": true, - }, - }, - "blocked": true, - "uri": "record(4)", - }, - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(4)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(4)", - }, - }, - "text": "alice replies to dan", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(0)", - "viewer": Object {}, - }, - "replies": Array [], - }, -} -`; - -exports[`pds views with blocking blocks thread reply 1`] = ` -Object { - "thread": Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object { - "like": "record(4)", - "repost": "record(3)", - }, - }, - "replies": Array [ - Object { - "$type": "app.bsky.feed.defs#blockedPost", - "author": Object { - "did": "user(1)", - "viewer": Object { - "blockedBy": false, - "blocking": "record(6)", - }, - }, - "blocked": true, - "uri": "record(5)", - }, - Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(8)", - "muted": false, - }, - }, - "cid": "cids(2)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(2)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(7)", - "val": "test-label", - }, - Object { - "cid": "cids(2)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(7)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(4)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - "root": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(7)", - "viewer": Object {}, - }, - }, - ], - }, -} -`; diff --git a/packages/pds/tests/views/__snapshots__/follows.test.ts.snap b/packages/pds/tests/views/__snapshots__/follows.test.ts.snap deleted file mode 100644 index fbcd95eb3a4..00000000000 --- a/packages/pds/tests/views/__snapshots__/follows.test.ts.snap +++ /dev/null @@ -1,703 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`pds follow views blocks followers by actor takedown 1`] = ` -Object { - "cursor": "0000000000000::bafycid", - "followers": Array [ - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-eve", - "did": "user(1)", - "displayName": "display-eve", - "handle": "eve.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "following": "record(0)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-bob", - "did": "user(2)", - "displayName": "display-bob", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(3)", - "following": "record(2)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-carol", - "did": "user(3)", - "displayName": "display-carol", - "handle": "carol.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(5)", - "following": "record(4)", - "muted": false, - }, - }, - ], - "subject": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-alice", - "did": "user(0)", - "displayName": "display-alice", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, -} -`; - -exports[`pds follow views blocks follows by actor takedown 1`] = ` -Object { - "cursor": "0000000000000::bafycid", - "follows": Array [ - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-eve", - "did": "user(1)", - "displayName": "display-eve", - "handle": "eve.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "following": "record(0)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-carol", - "did": "user(2)", - "displayName": "display-carol", - "handle": "carol.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(3)", - "following": "record(2)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-bob", - "did": "user(3)", - "displayName": "display-bob", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(5)", - "following": "record(4)", - "muted": false, - }, - }, - ], - "subject": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-alice", - "did": "user(0)", - "displayName": "display-alice", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, -} -`; - -exports[`pds follow views fetches followers 1`] = ` -Object { - "cursor": "0000000000000::bafycid", - "followers": Array [ - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-eve", - "did": "user(1)", - "displayName": "display-eve", - "handle": "eve.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "following": "record(0)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-dan", - "did": "user(2)", - "displayName": "display-dan", - "handle": "dan.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(3)", - "following": "record(2)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-bob", - "did": "user(3)", - "displayName": "display-bob", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(5)", - "following": "record(4)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-carol", - "did": "user(4)", - "displayName": "display-carol", - "handle": "carol.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(7)", - "following": "record(6)", - "muted": false, - }, - }, - ], - "subject": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-alice", - "did": "user(0)", - "displayName": "display-alice", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, -} -`; - -exports[`pds follow views fetches followers 2`] = ` -Object { - "cursor": "0000000000000::bafycid", - "followers": Array [ - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-dan", - "did": "user(1)", - "displayName": "display-dan", - "handle": "dan.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(3)", - "following": "record(2)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-alice", - "did": "user(2)", - "displayName": "display-alice", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - ], - "subject": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-bob", - "did": "user(0)", - "displayName": "display-bob", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "following": "record(0)", - "muted": false, - }, - }, -} -`; - -exports[`pds follow views fetches followers 3`] = ` -Object { - "cursor": "0000000000000::bafycid", - "followers": Array [ - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-eve", - "did": "user(1)", - "displayName": "display-eve", - "handle": "eve.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(3)", - "following": "record(2)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-bob", - "did": "user(2)", - "displayName": "display-bob", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(5)", - "following": "record(4)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-alice", - "did": "user(3)", - "displayName": "display-alice", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - ], - "subject": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-carol", - "did": "user(0)", - "displayName": "display-carol", - "handle": "carol.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "following": "record(0)", - "muted": false, - }, - }, -} -`; - -exports[`pds follow views fetches followers 4`] = ` -Object { - "cursor": "0000000000000::bafycid", - "followers": Array [ - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-alice", - "did": "user(1)", - "displayName": "display-alice", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - ], - "subject": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-dan", - "did": "user(0)", - "displayName": "display-dan", - "handle": "dan.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "following": "record(0)", - "muted": false, - }, - }, -} -`; - -exports[`pds follow views fetches followers 5`] = ` -Object { - "cursor": "0000000000000::bafycid", - "followers": Array [ - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-dan", - "did": "user(1)", - "displayName": "display-dan", - "handle": "dan.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(3)", - "following": "record(2)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-alice", - "did": "user(2)", - "displayName": "display-alice", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - ], - "subject": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-eve", - "did": "user(0)", - "displayName": "display-eve", - "handle": "eve.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "following": "record(0)", - "muted": false, - }, - }, -} -`; - -exports[`pds follow views fetches follows 1`] = ` -Object { - "cursor": "0000000000000::bafycid", - "follows": Array [ - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-eve", - "did": "user(1)", - "displayName": "display-eve", - "handle": "eve.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "following": "record(0)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-dan", - "did": "user(2)", - "displayName": "display-dan", - "handle": "dan.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(3)", - "following": "record(2)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-carol", - "did": "user(3)", - "displayName": "display-carol", - "handle": "carol.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(5)", - "following": "record(4)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-bob", - "did": "user(4)", - "displayName": "display-bob", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(7)", - "following": "record(6)", - "muted": false, - }, - }, - ], - "subject": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-alice", - "did": "user(0)", - "displayName": "display-alice", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, -} -`; - -exports[`pds follow views fetches follows 2`] = ` -Object { - "cursor": "0000000000000::bafycid", - "follows": Array [ - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-carol", - "did": "user(1)", - "displayName": "display-carol", - "handle": "carol.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(3)", - "following": "record(2)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-alice", - "did": "user(2)", - "displayName": "display-alice", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - ], - "subject": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-bob", - "did": "user(0)", - "displayName": "display-bob", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "following": "record(0)", - "muted": false, - }, - }, -} -`; - -exports[`pds follow views fetches follows 3`] = ` -Object { - "cursor": "0000000000000::bafycid", - "follows": Array [ - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-alice", - "did": "user(1)", - "displayName": "display-alice", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - ], - "subject": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-carol", - "did": "user(0)", - "displayName": "display-carol", - "handle": "carol.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "following": "record(0)", - "muted": false, - }, - }, -} -`; - -exports[`pds follow views fetches follows 4`] = ` -Object { - "cursor": "0000000000000::bafycid", - "follows": Array [ - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-eve", - "did": "user(1)", - "displayName": "display-eve", - "handle": "eve.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(3)", - "following": "record(2)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-bob", - "did": "user(2)", - "displayName": "display-bob", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(5)", - "following": "record(4)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-alice", - "did": "user(3)", - "displayName": "display-alice", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - ], - "subject": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-dan", - "did": "user(0)", - "displayName": "display-dan", - "handle": "dan.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "following": "record(0)", - "muted": false, - }, - }, -} -`; - -exports[`pds follow views fetches follows 5`] = ` -Object { - "cursor": "0000000000000::bafycid", - "follows": Array [ - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-carol", - "did": "user(1)", - "displayName": "display-carol", - "handle": "carol.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(3)", - "following": "record(2)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-alice", - "did": "user(2)", - "displayName": "display-alice", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - ], - "subject": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "descript-eve", - "did": "user(0)", - "displayName": "display-eve", - "handle": "eve.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "following": "record(0)", - "muted": false, - }, - }, -} -`; diff --git a/packages/pds/tests/views/__snapshots__/likes.test.ts.snap b/packages/pds/tests/views/__snapshots__/likes.test.ts.snap deleted file mode 100644 index 3a15355409f..00000000000 --- a/packages/pds/tests/views/__snapshots__/likes.test.ts.snap +++ /dev/null @@ -1,120 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`pds like views fetches post likes 1`] = ` -Object { - "cursor": "0000000000000::bafycid", - "likes": Array [ - Object { - "actor": Object { - "did": "user(0)", - "handle": "eve.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "createdAt": "1970-01-01T00:00:00.000Z", - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - Object { - "actor": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(1)", - "muted": false, - }, - }, - "createdAt": "1970-01-01T00:00:00.000Z", - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - Object { - "actor": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(3)", - "following": "record(2)", - "muted": false, - }, - }, - "createdAt": "1970-01-01T00:00:00.000Z", - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - Object { - "actor": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(3)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(6)", - "val": "self-label-a", - }, - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(6)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(5)", - "following": "record(4)", - "muted": false, - }, - }, - "createdAt": "1970-01-01T00:00:00.000Z", - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - ], - "uri": "record(0)", -} -`; - -exports[`pds like views fetches reply likes 1`] = ` -Object { - "cursor": "0000000000000::bafycid", - "likes": Array [ - Object { - "actor": Object { - "did": "user(0)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "createdAt": "1970-01-01T00:00:00.000Z", - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - ], - "uri": "record(0)", -} -`; diff --git a/packages/pds/tests/views/__snapshots__/mute-lists.test.ts.snap b/packages/pds/tests/views/__snapshots__/mute-lists.test.ts.snap deleted file mode 100644 index 6434e1e51b1..00000000000 --- a/packages/pds/tests/views/__snapshots__/mute-lists.test.ts.snap +++ /dev/null @@ -1,599 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`pds views with mutes from mute lists embeds lists in posts 1`] = ` -Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.graph.defs#listView", - "cid": "cids(3)", - "creator": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "description": "new descript", - "indexedAt": "1970-01-01T00:00:00.000Z", - "name": "updated alice mutes", - "purpose": "app.bsky.graph.defs#modlist", - "uri": "record(2)", - "viewer": Object { - "muted": false, - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "list embed!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(0)", - "viewer": Object {}, -} -`; - -exports[`pds views with mutes from mute lists flags mutes in threads 1`] = ` -Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object { - "like": "record(4)", - "repost": "record(3)", - }, - }, - "replies": Array [ - Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": true, - "mutedByList": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "cid": "cids(3)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "name": "alice mutes", - "purpose": "app.bsky.graph.defs#modlist", - "uri": "record(6)", - "viewer": Object { - "muted": true, - }, - }, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - "root": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - }, - "text": "of course", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(5)", - "viewer": Object {}, - }, - }, - Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(11)", - "val": "self-label-a", - }, - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(11)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(10)", - "muted": true, - "mutedByList": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "cid": "cids(3)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "name": "alice mutes", - "purpose": "app.bsky.graph.defs#modlist", - "uri": "record(6)", - "viewer": Object { - "muted": true, - }, - }, - }, - }, - "cid": "cids(4)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(9)", - "val": "test-label", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(9)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(6)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - "root": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(9)", - "viewer": Object {}, - }, - }, - ], -} -`; - -exports[`pds views with mutes from mute lists returns a users own list mutes 1`] = ` -Object { - "cursor": "0000000000000::bafycid", - "lists": Array [ - Object { - "cid": "cids(0)", - "creator": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "its me!", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": false, - }, - }, - "description": "blah blah", - "indexedAt": "1970-01-01T00:00:00.000Z", - "name": "new list", - "purpose": "app.bsky.graph.defs#modlist", - "uri": "record(0)", - "viewer": Object { - "muted": true, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "cid": "cids(2)", - "creator": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "its me!", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": false, - }, - }, - "description": "big list of mutes", - "indexedAt": "1970-01-01T00:00:00.000Z", - "name": "alice mutes", - "purpose": "app.bsky.graph.defs#modlist", - "uri": "record(3)", - "viewer": Object { - "muted": true, - }, - }, - ], -} -`; - -exports[`pds views with mutes from mute lists returns lists associated with a user 1`] = ` -Object { - "cursor": "0000000000000::bafycid", - "lists": Array [ - Object { - "cid": "cids(0)", - "creator": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "its me!", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": false, - }, - }, - "description": "blah blah", - "indexedAt": "1970-01-01T00:00:00.000Z", - "name": "new list", - "purpose": "app.bsky.graph.defs#modlist", - "uri": "record(0)", - "viewer": Object { - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "cid": "cids(2)", - "creator": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "its me!", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": false, - }, - }, - "description": "big list of mutes", - "indexedAt": "1970-01-01T00:00:00.000Z", - "name": "alice mutes", - "purpose": "app.bsky.graph.defs#modlist", - "uri": "record(3)", - "viewer": Object { - "muted": true, - }, - }, - ], -} -`; - -exports[`pds views with mutes from mute lists returns the contents of a list 1`] = ` -Object { - "cursor": "0000000000000::bafycid", - "items": Array [ - Object { - "subject": Object { - "did": "user(0)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": true, - "mutedByList": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "name": "alice mutes", - "purpose": "app.bsky.graph.defs#modlist", - "uri": "record(0)", - "viewer": Object { - "muted": true, - }, - }, - }, - }, - }, - Object { - "subject": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(2)", - "muted": true, - "mutedByList": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "name": "alice mutes", - "purpose": "app.bsky.graph.defs#modlist", - "uri": "record(0)", - "viewer": Object { - "muted": true, - }, - }, - }, - }, - }, - ], - "list": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "cid": "cids(0)", - "creator": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "its me!", - "did": "user(2)", - "displayName": "ali", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(2)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(5)", - "val": "self-label-a", - }, - Object { - "cid": "cids(2)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(5)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(4)", - "muted": false, - }, - }, - "description": "big list of mutes", - "indexedAt": "1970-01-01T00:00:00.000Z", - "name": "alice mutes", - "purpose": "app.bsky.graph.defs#modlist", - "uri": "record(0)", - "viewer": Object { - "muted": true, - }, - }, -} -`; diff --git a/packages/pds/tests/views/__snapshots__/mutes.test.ts.snap b/packages/pds/tests/views/__snapshots__/mutes.test.ts.snap deleted file mode 100644 index 8929a3c347b..00000000000 --- a/packages/pds/tests/views/__snapshots__/mutes.test.ts.snap +++ /dev/null @@ -1,75 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`mute views fetches mutes for the logged-in user. 1`] = ` -Array [ - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "Dr. Lowell DuBuque", - "handle": "elta48.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": true, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "Sally Funk", - "handle": "magnus53.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": true, - }, - }, - Object { - "did": "user(2)", - "handle": "nicolas-krajcik10.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": true, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(3)", - "displayName": "Patrick Sawayn", - "handle": "jeffrey-sawayn87.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": true, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(4)", - "displayName": "Kim Streich", - "handle": "adrienne49.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": true, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(5)", - "displayName": "Carlton Abernathy IV", - "handle": "aliya-hodkiewicz.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": true, - }, - }, -] -`; diff --git a/packages/pds/tests/views/__snapshots__/notifications.test.ts.snap b/packages/pds/tests/views/__snapshots__/notifications.test.ts.snap deleted file mode 100644 index 117bcdfbb79..00000000000 --- a/packages/pds/tests/views/__snapshots__/notifications.test.ts.snap +++ /dev/null @@ -1,1922 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`pds notification views fetches notifications omitting mentions and replies for taken-down posts 1`] = ` -Array [ - Object { - "author": Object { - "did": "user(0)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "reply", - "reasonSubject": "record(3)", - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(3)", - }, - "root": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "text": "indeed", - }, - "uri": "record(0)", - }, - Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(6)", - "muted": false, - }, - }, - "cid": "cids(3)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "repost", - "reasonSubject": "record(3)", - "record": Object { - "$type": "app.bsky.feed.repost", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(2)", - "uri": "record(3)", - }, - }, - "uri": "record(5)", - }, - Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(6)", - "muted": false, - }, - }, - "cid": "cids(4)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "repost", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.repost", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "uri": "record(7)", - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(6)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(11)", - "val": "self-label-a", - }, - Object { - "cid": "cids(6)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(11)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(10)", - "following": "record(9)", - "muted": false, - }, - }, - "cid": "cids(5)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [ - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(8)", - "val": "test-label", - }, - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(8)", - "val": "test-label-2", - }, - ], - "reason": "reply", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(7)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - "root": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "uri": "record(8)", - }, - Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(6)", - "muted": false, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "like", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.like", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "uri": "record(12)", - }, - Object { - "author": Object { - "did": "user(0)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(9)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "like", - "reasonSubject": "record(14)", - "record": Object { - "$type": "app.bsky.feed.like", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(10)", - "uri": "record(14)", - }, - }, - "uri": "record(13)", - }, - Object { - "author": Object { - "did": "user(0)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(11)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "like", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.like", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "uri": "record(15)", - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(6)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(11)", - "val": "self-label-a", - }, - Object { - "cid": "cids(6)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(11)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(10)", - "following": "record(9)", - "muted": false, - }, - }, - "cid": "cids(12)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "like", - "reasonSubject": "record(14)", - "record": Object { - "$type": "app.bsky.feed.like", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(10)", - "uri": "record(14)", - }, - }, - "uri": "record(16)", - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(6)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(11)", - "val": "self-label-a", - }, - Object { - "cid": "cids(6)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(11)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(10)", - "following": "record(9)", - "muted": false, - }, - }, - "cid": "cids(13)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "like", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.like", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "uri": "record(17)", - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(6)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(11)", - "val": "self-label-a", - }, - Object { - "cid": "cids(6)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(11)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(10)", - "following": "record(9)", - "muted": false, - }, - }, - "cid": "cids(14)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "follow", - "record": Object { - "$type": "app.bsky.graph.follow", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": "user(3)", - }, - "uri": "record(10)", - }, - Object { - "author": Object { - "did": "user(0)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(15)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "follow", - "record": Object { - "$type": "app.bsky.graph.follow", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": "user(3)", - }, - "uri": "record(2)", - }, -] -`; - -exports[`pds notification views fetches notifications omitting records by a muted user 1`] = ` -Array [ - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(0)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [ - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(0)", - "val": "test-label", - }, - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(0)", - "val": "test-label-2", - }, - ], - "reason": "reply", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(2)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(4)", - }, - "root": Object { - "cid": "cids(3)", - "uri": "record(4)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "uri": "record(0)", - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(0)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(4)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "like", - "reasonSubject": "record(6)", - "record": Object { - "$type": "app.bsky.feed.like", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(5)", - "uri": "record(6)", - }, - }, - "uri": "record(5)", - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(0)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(6)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "like", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.like", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(3)", - "uri": "record(4)", - }, - }, - "uri": "record(7)", - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(0)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(7)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "follow", - "record": Object { - "$type": "app.bsky.graph.follow", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": "user(1)", - }, - "uri": "record(2)", - }, -] -`; - -exports[`pds notification views fetches notifications with a last-seen 1`] = ` -Array [ - Object { - "author": Object { - "did": "user(0)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "reply", - "reasonSubject": "record(3)", - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(3)", - }, - "root": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "text": "indeed", - }, - "uri": "record(0)", - }, - Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(6)", - "muted": false, - }, - }, - "cid": "cids(3)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "repost", - "reasonSubject": "record(3)", - "record": Object { - "$type": "app.bsky.feed.repost", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(2)", - "uri": "record(3)", - }, - }, - "uri": "record(5)", - }, - Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(6)", - "muted": false, - }, - }, - "cid": "cids(4)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "repost", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.repost", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "uri": "record(7)", - }, - Object { - "author": Object { - "did": "user(0)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(5)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": true, - "labels": Array [], - "reason": "reply", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - "root": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "text": "of course", - }, - "uri": "record(8)", - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(12)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(12)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "cid": "cids(6)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": true, - "labels": Array [ - Object { - "cid": "cids(6)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(9)", - "val": "test-label", - }, - Object { - "cid": "cids(6)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(9)", - "val": "test-label-2", - }, - ], - "reason": "reply", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(8)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - "root": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "uri": "record(9)", - }, - Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(6)", - "muted": false, - }, - }, - "cid": "cids(9)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": true, - "labels": Array [], - "reason": "like", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.like", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "uri": "record(13)", - }, - Object { - "author": Object { - "did": "user(0)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(10)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": true, - "labels": Array [], - "reason": "like", - "reasonSubject": "record(15)", - "record": Object { - "$type": "app.bsky.feed.like", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(11)", - "uri": "record(15)", - }, - }, - "uri": "record(14)", - }, - Object { - "author": Object { - "did": "user(0)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(12)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": true, - "labels": Array [], - "reason": "like", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.like", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "uri": "record(16)", - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(12)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(12)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "cid": "cids(13)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": true, - "labels": Array [], - "reason": "like", - "reasonSubject": "record(15)", - "record": Object { - "$type": "app.bsky.feed.like", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(11)", - "uri": "record(15)", - }, - }, - "uri": "record(17)", - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(12)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(12)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "cid": "cids(14)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": true, - "labels": Array [], - "reason": "like", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.like", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "uri": "record(18)", - }, - Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(6)", - "muted": false, - }, - }, - "cid": "cids(15)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": true, - "labels": Array [], - "reason": "mention", - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(16)", - "uri": "record(20)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(3)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - "uri": "record(19)", - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(12)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(12)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "cid": "cids(17)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": true, - "labels": Array [], - "reason": "follow", - "record": Object { - "$type": "app.bsky.graph.follow", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": "user(3)", - }, - "uri": "record(11)", - }, - Object { - "author": Object { - "did": "user(0)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(18)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": true, - "labels": Array [], - "reason": "follow", - "record": Object { - "$type": "app.bsky.graph.follow", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": "user(3)", - }, - "uri": "record(2)", - }, -] -`; - -exports[`pds notification views fetches notifications without a last-seen 1`] = ` -Array [ - Object { - "author": Object { - "did": "user(0)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "reply", - "reasonSubject": "record(3)", - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(3)", - }, - "root": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "text": "indeed", - }, - "uri": "record(0)", - }, - Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(6)", - "muted": false, - }, - }, - "cid": "cids(3)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "repost", - "reasonSubject": "record(3)", - "record": Object { - "$type": "app.bsky.feed.repost", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(2)", - "uri": "record(3)", - }, - }, - "uri": "record(5)", - }, - Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(6)", - "muted": false, - }, - }, - "cid": "cids(4)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "repost", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.repost", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "uri": "record(7)", - }, - Object { - "author": Object { - "did": "user(0)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(5)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "reply", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - "root": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "text": "of course", - }, - "uri": "record(8)", - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(12)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(12)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "cid": "cids(6)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [ - Object { - "cid": "cids(6)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(9)", - "val": "test-label", - }, - Object { - "cid": "cids(6)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(9)", - "val": "test-label-2", - }, - ], - "reason": "reply", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(8)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - "root": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "uri": "record(9)", - }, - Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(6)", - "muted": false, - }, - }, - "cid": "cids(9)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "like", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.like", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "uri": "record(13)", - }, - Object { - "author": Object { - "did": "user(0)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(10)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "like", - "reasonSubject": "record(15)", - "record": Object { - "$type": "app.bsky.feed.like", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(11)", - "uri": "record(15)", - }, - }, - "uri": "record(14)", - }, - Object { - "author": Object { - "did": "user(0)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(12)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "like", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.like", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "uri": "record(16)", - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(12)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(12)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "cid": "cids(13)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "like", - "reasonSubject": "record(15)", - "record": Object { - "$type": "app.bsky.feed.like", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(11)", - "uri": "record(15)", - }, - }, - "uri": "record(17)", - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(12)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(12)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "cid": "cids(14)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "like", - "reasonSubject": "record(4)", - "record": Object { - "$type": "app.bsky.feed.like", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(1)", - "uri": "record(4)", - }, - }, - "uri": "record(18)", - }, - Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(6)", - "muted": false, - }, - }, - "cid": "cids(15)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "mention", - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(16)", - "uri": "record(20)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(3)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - "uri": "record(19)", - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(12)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(12)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "cid": "cids(17)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "follow", - "record": Object { - "$type": "app.bsky.graph.follow", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": "user(3)", - }, - "uri": "record(11)", - }, - Object { - "author": Object { - "did": "user(0)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(18)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "follow", - "record": Object { - "$type": "app.bsky.graph.follow", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": "user(3)", - }, - "uri": "record(2)", - }, -] -`; - -exports[`pds notification views generates notifications for quotes 1`] = ` -Object { - "cursor": "0000000000000::bafycid", - "notifications": Array [ - Object { - "author": Object { - "did": "user(0)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "repost", - "reasonSubject": "record(1)", - "record": Object { - "$type": "app.bsky.feed.repost", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": Object { - "cid": "cids(1)", - "uri": "record(1)", - }, - }, - "uri": "record(0)", - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "its me!", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(4)", - "val": "self-label-a", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(4)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(3)", - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [ - Object { - "cid": "cids(2)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(2)", - "val": "test-label", - }, - ], - "reason": "quote", - "reasonSubject": "record(1)", - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(1)", - "uri": "record(1)", - }, - }, - "text": "yoohoo label_me", - }, - "uri": "record(2)", - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "its me!", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(4)", - "val": "self-label-a", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(4)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(3)", - "muted": false, - }, - }, - "cid": "cids(4)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "isRead": false, - "labels": Array [], - "reason": "follow", - "record": Object { - "$type": "app.bsky.graph.follow", - "createdAt": "1970-01-01T00:00:00.000Z", - "subject": "user(2)", - }, - "uri": "record(3)", - }, - ], -} -`; diff --git a/packages/pds/tests/views/__snapshots__/posts.test.ts.snap b/packages/pds/tests/views/__snapshots__/posts.test.ts.snap deleted file mode 100644 index 75ff566186e..00000000000 --- a/packages/pds/tests/views/__snapshots__/posts.test.ts.snap +++ /dev/null @@ -1,526 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`pds posts views fetches posts 1`] = ` -Array [ - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(0)", - "val": "self-label", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "labels": Object { - "$type": "com.atproto.label.defs#selfLabels", - "values": Array [ - Object { - "val": "self-label", - }, - ], - }, - "text": "hey there", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(0)", - "viewer": Object {}, - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(5)", - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(3)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(3)", - "viewer": Object {}, - }, - Object { - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(9)", - "following": "record(8)", - "muted": false, - }, - }, - "cid": "cids(5)", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(5)", - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(3)", - "embeds": Array [], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(3)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(6)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(7)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(3)", - "uri": "record(3)", - }, - }, - }, - "text": "hi im carol", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(7)", - "viewer": Object { - "like": "record(10)", - }, - }, - Object { - "author": Object { - "did": "user(3)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(3)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(12)", - "muted": false, - }, - }, - "cid": "cids(8)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(9)", - "following": "record(8)", - "muted": false, - }, - }, - "cid": "cids(5)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(5)", - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(3)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(3)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(7)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(6)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(7)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(3)", - "uri": "record(3)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(5)", - "uri": "record(7)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(0)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(11)", - "viewer": Object {}, - }, - Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(9)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(10)", - "uri": "record(14)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(13)", - "viewer": Object {}, - }, -] -`; diff --git a/packages/pds/tests/views/__snapshots__/profile.test.ts.snap b/packages/pds/tests/views/__snapshots__/profile.test.ts.snap deleted file mode 100644 index dbfd30bdbce..00000000000 --- a/packages/pds/tests/views/__snapshots__/profile.test.ts.snap +++ /dev/null @@ -1,286 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`pds profile views creates new profile 1`] = ` -Object { - "did": "user(0)", - "displayName": "danny boy", - "followersCount": 1, - "followsCount": 1, - "handle": "dan.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(0)", - "val": "repo-action-label", - }, - ], - "postsCount": 2, - "viewer": Object { - "blockedBy": false, - "muted": false, - }, -} -`; - -exports[`pds profile views fetches multiple profiles 1`] = ` -Array [ - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "its me!", - "did": "user(0)", - "displayName": "ali", - "followersCount": 2, - "followsCount": 3, - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-a", - }, - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-b", - }, - ], - "postsCount": 4, - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "following": "record(0)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(1)", - "displayName": "bobby", - "followersCount": 2, - "followsCount": 2, - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "postsCount": 3, - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - Object { - "did": "user(2)", - "followersCount": 2, - "followsCount": 1, - "handle": "carol.test", - "labels": Array [], - "postsCount": 2, - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - Object { - "did": "user(3)", - "followersCount": 1, - "followsCount": 1, - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(3)", - "val": "repo-action-label", - }, - ], - "postsCount": 2, - "viewer": Object { - "blockedBy": false, - "followedBy": "record(5)", - "muted": false, - }, - }, -] -`; - -exports[`pds profile views fetches other's profile, with a follow 1`] = ` -Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "its me!", - "did": "user(0)", - "displayName": "ali", - "followersCount": 2, - "followsCount": 3, - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-a", - }, - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-b", - }, - ], - "postsCount": 4, - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "following": "record(0)", - "muted": false, - }, -} -`; - -exports[`pds profile views fetches other's profile, without a follow 1`] = ` -Object { - "did": "user(0)", - "followersCount": 1, - "followsCount": 1, - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(0)", - "val": "repo-action-label", - }, - ], - "postsCount": 2, - "viewer": Object { - "blockedBy": false, - "followedBy": "record(0)", - "muted": false, - }, -} -`; - -exports[`pds profile views fetches own profile 1`] = ` -Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "its me!", - "did": "user(0)", - "displayName": "ali", - "followersCount": 2, - "followsCount": 3, - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(0)", - "val": "self-label-a", - }, - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(0)", - "val": "self-label-b", - }, - ], - "postsCount": 4, - "viewer": Object { - "blockedBy": false, - "muted": false, - }, -} -`; - -exports[`pds profile views handles avatars & banners 1`] = ` -Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "banner": "https://pds.public.url/image/ejOTK5gy9tlarOXM6MhrPOs0C18LFpfd9qQ9lBrcIBE/rs:fill:3000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "description": "new descript", - "did": "user(0)", - "displayName": "ali", - "followersCount": 2, - "followsCount": 3, - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "postsCount": 4, - "viewer": Object { - "blockedBy": false, - "muted": false, - }, -} -`; - -exports[`pds profile views handles unsetting profile fields 1`] = ` -Object { - "did": "user(0)", - "followersCount": 2, - "followsCount": 3, - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "postsCount": 4, - "viewer": Object { - "blockedBy": false, - "muted": false, - }, -} -`; - -exports[`pds profile views updates profile 1`] = ` -Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "new descript", - "did": "user(0)", - "displayName": "ali", - "followersCount": 2, - "followsCount": 3, - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "postsCount": 4, - "viewer": Object { - "blockedBy": false, - "muted": false, - }, -} -`; diff --git a/packages/pds/tests/views/__snapshots__/reposts.test.ts.snap b/packages/pds/tests/views/__snapshots__/reposts.test.ts.snap deleted file mode 100644 index 69927a4b5ae..00000000000 --- a/packages/pds/tests/views/__snapshots__/reposts.test.ts.snap +++ /dev/null @@ -1,108 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`pds repost views fetches reposted-by for a post 1`] = ` -Array [ - Object { - "did": "user(0)", - "handle": "eve.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(0)", - "muted": false, - }, - }, - Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "hi im bob label_me", - "did": "user(3)", - "displayName": "bobby", - "handle": "bob.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(5)", - "val": "self-label-a", - }, - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(5)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(4)", - "following": "record(3)", - "muted": false, - }, - }, -] -`; - -exports[`pds repost views fetches reposted-by for a reply 1`] = ` -Array [ - Object { - "did": "user(0)", - "handle": "eve.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(0)", - "muted": false, - }, - }, -] -`; diff --git a/packages/pds/tests/views/__snapshots__/thread.test.ts.snap b/packages/pds/tests/views/__snapshots__/thread.test.ts.snap deleted file mode 100644 index df37cbb712d..00000000000 --- a/packages/pds/tests/views/__snapshots__/thread.test.ts.snap +++ /dev/null @@ -1,1883 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`pds thread views blocks ancestors by actor takedown 1`] = ` -Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "parent": Object { - "$type": "app.bsky.feed.defs#notFoundPost", - "notFound": true, - "uri": "record(5)", - }, - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(5)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(4)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 2, - "uri": "record(0)", - "viewer": Object { - "repost": "record(6)", - }, - }, - "replies": Array [], -} -`; - -exports[`pds thread views blocks ancestors by record takedown 1`] = ` -Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "parent": Object { - "$type": "app.bsky.feed.defs#notFoundPost", - "notFound": true, - "uri": "record(5)", - }, - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(5)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(4)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 2, - "uri": "record(0)", - "viewer": Object { - "repost": "record(6)", - }, - }, - "replies": Array [], -} -`; - -exports[`pds thread views blocks replies by actor takedown 1`] = ` -Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object { - "like": "record(4)", - }, - }, - "replies": Array [ - Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-a", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(2)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(5)", - "val": "test-label", - }, - Object { - "cid": "cids(2)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(5)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(4)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - "root": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(5)", - "viewer": Object {}, - }, - "replies": Array [ - Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(5)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(5)", - }, - "root": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 2, - "uri": "record(7)", - "viewer": Object { - "repost": "record(8)", - }, - }, - "replies": Array [], - }, - ], - }, - ], -} -`; - -exports[`pds thread views blocks replies by record takedown 1`] = ` -Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object { - "like": "record(4)", - }, - }, - "replies": Array [ - Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-a", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(2)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(5)", - "val": "test-label", - }, - Object { - "cid": "cids(2)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(5)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(4)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - "root": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(5)", - "viewer": Object {}, - }, - "replies": Array [], - }, - ], -} -`; - -exports[`pds thread views fetches ancestors 1`] = ` -Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "parent": Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "parent": Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(4)", - "viewer": Object { - "like": "record(8)", - }, - }, - "replies": Array [], - }, - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(5)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(5)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(4)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(4)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(5)", - "viewer": Object {}, - }, - "replies": Array [], - }, - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(5)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(4)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 2, - "uri": "record(0)", - "viewer": Object { - "repost": "record(6)", - }, - }, - "replies": Array [], -} -`; - -exports[`pds thread views fetches deep post thread 1`] = ` -Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object { - "like": "record(4)", - }, - }, - "replies": Array [ - Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "following": "record(6)", - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - "root": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - }, - "text": "of course", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(5)", - "viewer": Object {}, - }, - "replies": Array [], - }, - Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(8)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(8)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(7)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(7)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - "root": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(7)", - "viewer": Object {}, - }, - "replies": Array [ - Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(6)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(7)", - }, - "root": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 2, - "uri": "record(9)", - "viewer": Object { - "repost": "record(10)", - }, - }, - "replies": Array [], - }, - ], - }, - ], -} -`; - -exports[`pds thread views fetches shallow ancestors 1`] = ` -Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "parent": Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(5)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(5)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(4)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(4)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(5)", - "viewer": Object {}, - }, - "replies": Array [], - }, - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(5)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(4)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 2, - "uri": "record(0)", - "viewer": Object { - "repost": "record(6)", - }, - }, - "replies": Array [], -} -`; - -exports[`pds thread views fetches shallow post thread 1`] = ` -Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object { - "like": "record(4)", - }, - }, - "replies": Array [ - Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "following": "record(6)", - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - "root": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - }, - "text": "of course", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(5)", - "viewer": Object {}, - }, - }, - Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(8)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(8)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(7)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(7)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - "root": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(7)", - "viewer": Object {}, - }, - }, - ], -} -`; - -exports[`pds thread views handles deleted posts correctly 1`] = ` -Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "text": "Deletion thread", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(0)", - "viewer": Object {}, - }, - "replies": Array [ - Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(5)", - "val": "self-label-a", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(5)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - "root": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - }, - "text": "Reply", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(4)", - "viewer": Object {}, - }, - "replies": Array [ - Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(4)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(4)", - }, - "root": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - }, - "text": "Reply reply", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(6)", - "viewer": Object {}, - }, - "replies": Array [], - }, - ], - }, - ], -} -`; - -exports[`pds thread views handles deleted posts correctly 2`] = ` -Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "text": "Deletion thread", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(0)", - "viewer": Object {}, - }, - "replies": Array [], -} -`; - -exports[`pds thread views handles deleted posts correctly 3`] = ` -Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "parent": Object { - "$type": "app.bsky.feed.defs#notFoundPost", - "notFound": true, - "uri": "record(5)", - }, - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(5)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(4)", - }, - }, - "text": "Reply reply", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(0)", - "viewer": Object {}, - }, - "replies": Array [], -} -`; - -exports[`pds thread views includes the muted status of post authors. 1`] = ` -Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": true, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object { - "like": "record(4)", - }, - }, - "replies": Array [ - Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "following": "record(6)", - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - "root": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - }, - "text": "of course", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(5)", - "viewer": Object {}, - }, - "replies": Array [], - }, - Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(8)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(8)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(7)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(7)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - "root": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(7)", - "viewer": Object {}, - }, - "replies": Array [ - Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": true, - }, - }, - "cid": "cids(6)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(7)", - }, - "root": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 2, - "uri": "record(9)", - "viewer": Object { - "repost": "record(10)", - }, - }, - "replies": Array [], - }, - ], - }, - ], -} -`; diff --git a/packages/pds/tests/views/__snapshots__/timeline.test.ts.snap b/packages/pds/tests/views/__snapshots__/timeline.test.ts.snap deleted file mode 100644 index 73f5e4f6748..00000000000 --- a/packages/pds/tests/views/__snapshots__/timeline.test.ts.snap +++ /dev/null @@ -1,7343 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`timeline views blocks posts, reposts, replies by actor takedown 1`] = ` -Array [ - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(3)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object {}, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(3)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(4)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(5)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewNotFound", - "notFound": true, - "uri": "record(7)", - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(6)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(6)", - "uri": "record(7)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(0)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(5)", - "val": "test-label", - }, - ], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(5)", - "uri": "record(6)", - }, - }, - "text": "yoohoo label_me", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(5)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(5)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewNotFound", - "notFound": true, - "uri": "record(7)", - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(6)", - "uri": "record(7)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(0)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(6)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(7)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "text": "dan here!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(8)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(8)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(9)", - "val": "self-label", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "labels": Object { - "$type": "com.atproto.label.defs#selfLabels", - "values": Array [ - Object { - "val": "self-label", - }, - ], - }, - "text": "hey there", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(9)", - "viewer": Object {}, - }, - }, -] -`; - -exports[`timeline views blocks posts, reposts, replies by record takedown. 1`] = ` -Array [ - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(3)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object {}, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(3)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(7)", - "following": "record(6)", - "muted": false, - }, - }, - "cid": "cids(4)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "of course", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(5)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(5)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewNotFound", - "notFound": true, - "uri": "record(9)", - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(8)", - "val": "test-label", - }, - ], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(6)", - "uri": "record(9)", - }, - }, - "text": "yoohoo label_me", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(8)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(3)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(8)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(13)", - "val": "self-label-a", - }, - Object { - "cid": "cids(8)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(13)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(12)", - "following": "record(11)", - "muted": false, - }, - }, - "cid": "cids(7)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000+00:00", - "text": "bobby boy here", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(10)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(9)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "text": "dan here!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(14)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(7)", - "following": "record(6)", - "muted": false, - }, - }, - "cid": "cids(10)", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(3)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(8)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(13)", - "val": "self-label-a", - }, - Object { - "cid": "cids(8)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(13)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(12)", - "following": "record(11)", - "muted": false, - }, - }, - "cid": "cids(13)", - "embeds": Array [], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(13)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(16)", - "val": "kind", - }, - ], - "uri": "record(16)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(10)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(15)", - "val": "kind", - }, - ], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(11)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(12)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(13)", - "uri": "record(16)", - }, - }, - }, - "text": "hi im carol", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(15)", - "viewer": Object { - "like": "record(17)", - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(3)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(8)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(13)", - "val": "self-label-a", - }, - Object { - "cid": "cids(8)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(13)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(12)", - "following": "record(11)", - "muted": false, - }, - }, - "cid": "cids(13)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(13)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(16)", - "val": "kind", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(16)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(14)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(14)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(18)", - "val": "self-label", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "labels": Object { - "$type": "com.atproto.label.defs#selfLabels", - "values": Array [ - Object { - "val": "self-label", - }, - ], - }, - "text": "hey there", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(18)", - "viewer": Object {}, - }, - }, -] -`; - -exports[`timeline views fetches authenticated user's home feed w/ reverse-chronological algorithm 1`] = ` -Array [ - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(3)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object {}, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": false, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(3)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(6)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(3)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "cid": "cids(7)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": false, - }, - }, - "cid": "cids(9)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(9)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(12)", - "val": "kind", - }, - ], - "uri": "record(12)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(9)", - "val": "kind", - }, - ], - "uri": "record(9)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(8)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(9)", - "uri": "record(12)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(7)", - "uri": "record(9)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(0)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(8)", - "viewer": Object {}, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(3)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(3)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": false, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(3)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(3)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "cid": "cids(10)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "of course", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(13)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": false, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(3)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(11)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(6)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(3)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "cid": "cids(7)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(9)", - "val": "kind", - }, - ], - "uri": "record(9)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(8)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(9)", - "uri": "record(12)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(8)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(7)", - "uri": "record(9)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(0)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(11)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(14)", - "val": "test-label", - }, - ], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(6)", - "uri": "record(8)", - }, - }, - "text": "yoohoo label_me", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(14)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": false, - }, - }, - "cid": "cids(12)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000+00:00", - "text": "bobby boy here", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(15)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(6)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(3)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "cid": "cids(7)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": false, - }, - }, - "cid": "cids(9)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(9)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(12)", - "val": "kind", - }, - ], - "uri": "record(12)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(9)", - "val": "kind", - }, - ], - "uri": "record(9)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(8)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(9)", - "uri": "record(12)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(7)", - "uri": "record(9)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(0)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(8)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(13)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "text": "dan here!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(16)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(3)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "cid": "cids(7)", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": false, - }, - }, - "cid": "cids(9)", - "embeds": Array [], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(9)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(12)", - "val": "kind", - }, - ], - "uri": "record(12)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(9)", - "val": "kind", - }, - ], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(8)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(9)", - "uri": "record(12)", - }, - }, - }, - "text": "hi im carol", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(9)", - "viewer": Object { - "like": "record(17)", - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": false, - }, - }, - "cid": "cids(9)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(9)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(12)", - "val": "kind", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(12)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(14)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(14)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(18)", - "val": "self-label", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "labels": Object { - "$type": "com.atproto.label.defs#selfLabels", - "values": Array [ - Object { - "val": "self-label", - }, - ], - }, - "text": "hey there", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(18)", - "viewer": Object {}, - }, - }, -] -`; - -exports[`timeline views fetches authenticated user's home feed w/ reverse-chronological algorithm 2`] = ` -Array [ - Object { - "post": Object { - "author": Object { - "did": "user(0)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(0)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "following": "record(3)", - "muted": false, - }, - }, - "cid": "cids(1)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(3)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(5)", - "val": "self-label-a", - }, - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(5)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(4)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(4)", - "val": "kind", - }, - ], - "uri": "record(4)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(2)", - "val": "kind", - }, - ], - "uri": "record(2)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(2)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(3)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(4)", - "uri": "record(4)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(1)", - "uri": "record(2)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(1)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object {}, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "following": "record(3)", - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(6)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(9)", - "uri": "record(11)", - }, - "root": Object { - "cid": "cids(8)", - "uri": "record(10)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(6)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(3)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(5)", - "val": "self-label-a", - }, - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(5)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(9)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(9)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(11)", - "val": "test-label", - }, - Object { - "cid": "cids(9)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(11)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(2)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(8)", - "uri": "record(10)", - }, - "root": Object { - "cid": "cids(8)", - "uri": "record(10)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(11)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(10)", - "viewer": Object { - "like": "record(12)", - }, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "following": "record(3)", - "muted": false, - }, - }, - "cid": "cids(10)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(8)", - "uri": "record(10)", - }, - "root": Object { - "cid": "cids(8)", - "uri": "record(10)", - }, - }, - "text": "of course", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(13)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(10)", - "viewer": Object { - "like": "record(12)", - }, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(10)", - "viewer": Object { - "like": "record(12)", - }, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(3)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(5)", - "val": "self-label-a", - }, - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(5)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(9)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(9)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(11)", - "val": "test-label", - }, - Object { - "cid": "cids(9)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(11)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(2)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(8)", - "uri": "record(10)", - }, - "root": Object { - "cid": "cids(8)", - "uri": "record(10)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(11)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(10)", - "viewer": Object { - "like": "record(12)", - }, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(10)", - "viewer": Object { - "like": "record(12)", - }, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(11)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(0)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(0)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "following": "record(3)", - "muted": false, - }, - }, - "cid": "cids(1)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(2)", - "val": "kind", - }, - ], - "uri": "record(2)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(2)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(3)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(4)", - "uri": "record(4)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(0)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(1)", - "uri": "record(2)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(1)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(11)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(14)", - "val": "test-label", - }, - ], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - }, - "text": "yoohoo label_me", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(14)", - "viewer": Object { - "like": "record(15)", - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(3)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(5)", - "val": "self-label-a", - }, - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(5)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(12)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000+00:00", - "text": "bobby boy here", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(16)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(10)", - "viewer": Object { - "like": "record(12)", - }, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "following": "record(3)", - "muted": false, - }, - }, - "cid": "cids(1)", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(3)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(5)", - "val": "self-label-a", - }, - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(5)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(4)", - "embeds": Array [], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(4)", - "val": "kind", - }, - ], - "uri": "record(4)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(2)", - "val": "kind", - }, - ], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(2)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(3)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(4)", - "uri": "record(4)", - }, - }, - }, - "text": "hi im carol", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(2)", - "viewer": Object { - "like": "record(17)", - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(3)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(5)", - "val": "self-label-a", - }, - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(5)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(4)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(4)", - "val": "kind", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(4)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(13)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(13)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(18)", - "val": "self-label", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "labels": Object { - "$type": "com.atproto.label.defs#selfLabels", - "values": Array [ - Object { - "val": "self-label", - }, - ], - }, - "text": "hey there", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(18)", - "viewer": Object {}, - }, - }, -] -`; - -exports[`timeline views fetches authenticated user's home feed w/ reverse-chronological algorithm 3`] = ` -Array [ - Object { - "post": Object { - "author": Object { - "did": "user(0)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(0)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(1)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(3)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(4)", - "val": "self-label-a", - }, - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(4)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(3)", - "muted": false, - }, - }, - "cid": "cids(4)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(2)", - "val": "kind", - }, - ], - "uri": "record(2)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(1)", - "val": "kind", - }, - ], - "uri": "record(1)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(2)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(3)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(4)", - "uri": "record(2)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(1)", - "uri": "record(1)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(1)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object { - "repost": "record(5)", - }, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(6)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(9)", - "uri": "record(11)", - }, - "root": Object { - "cid": "cids(8)", - "uri": "record(10)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(6)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(3)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(4)", - "val": "self-label-a", - }, - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(4)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(3)", - "muted": false, - }, - }, - "cid": "cids(9)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(9)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(11)", - "val": "test-label", - }, - Object { - "cid": "cids(9)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(11)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(2)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(8)", - "uri": "record(10)", - }, - "root": Object { - "cid": "cids(8)", - "uri": "record(10)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(11)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(10)", - "viewer": Object { - "like": "record(12)", - }, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(10)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(8)", - "uri": "record(10)", - }, - "root": Object { - "cid": "cids(8)", - "uri": "record(10)", - }, - }, - "text": "of course", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(13)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(10)", - "viewer": Object { - "like": "record(12)", - }, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(10)", - "viewer": Object { - "like": "record(12)", - }, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(11)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(0)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(0)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(1)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(1)", - "val": "kind", - }, - ], - "uri": "record(1)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(2)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(3)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(4)", - "uri": "record(2)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(0)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(1)", - "uri": "record(1)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(1)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(11)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(14)", - "val": "test-label", - }, - ], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(0)", - "uri": "record(0)", - }, - }, - "text": "yoohoo label_me", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(14)", - "viewer": Object { - "like": "record(15)", - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(10)", - "viewer": Object { - "like": "record(12)", - }, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(1)", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(3)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(4)", - "val": "self-label-a", - }, - Object { - "cid": "cids(5)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(3)", - "uri": "record(4)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(3)", - "muted": false, - }, - }, - "cid": "cids(4)", - "embeds": Array [], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(2)", - "val": "kind", - }, - ], - "uri": "record(2)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(1)", - "val": "kind", - }, - ], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(2)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(3)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(4)", - "uri": "record(2)", - }, - }, - }, - "text": "hi im carol", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(1)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(12)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(12)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(16)", - "val": "self-label", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "labels": Object { - "$type": "com.atproto.label.defs#selfLabels", - "values": Array [ - Object { - "val": "self-label", - }, - ], - }, - "text": "hey there", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(16)", - "viewer": Object {}, - }, - }, -] -`; - -exports[`timeline views fetches authenticated user's home feed w/ reverse-chronological algorithm 4`] = ` -Array [ - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(4)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(3)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object { - "repost": "record(5)", - }, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(8)", - "muted": false, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(4)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(4)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(3)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(3)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(4)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(3)", - "viewer": Object { - "like": "record(7)", - "repost": "record(6)", - }, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(3)", - "viewer": Object { - "like": "record(7)", - "repost": "record(6)", - }, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(8)", - "muted": false, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(4)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(4)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(3)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(3)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(4)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(3)", - "viewer": Object { - "like": "record(7)", - "repost": "record(6)", - }, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(2)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(1)", - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(3)", - "viewer": Object { - "like": "record(7)", - "repost": "record(6)", - }, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(8)", - "muted": false, - }, - }, - "cid": "cids(6)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000+00:00", - "text": "bobby boy here", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(10)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(7)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(3)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(8)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(8)", - "muted": false, - }, - }, - "cid": "cids(10)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(10)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(13)", - "val": "kind", - }, - ], - "uri": "record(13)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(8)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(12)", - "val": "kind", - }, - ], - "uri": "record(12)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(9)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(10)", - "uri": "record(13)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(8)", - "uri": "record(12)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(0)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(11)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(11)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "text": "dan here!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(14)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(9)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(9)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(8)", - "muted": false, - }, - }, - "cid": "cids(10)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(10)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(13)", - "val": "kind", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(13)", - "viewer": Object {}, - }, - }, -] -`; - -exports[`timeline views omits posts and reposts of muted authors. 1`] = ` -Array [ - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(3)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object {}, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": true, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(3)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(3)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": true, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(3)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(6)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(7)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(3)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(12)", - "following": "record(11)", - "muted": true, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(8)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(10)", - "val": "kind", - }, - ], - "uri": "record(10)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(9)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(10)", - "uri": "record(13)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(9)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(8)", - "uri": "record(10)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(0)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(6)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(8)", - "val": "test-label", - }, - ], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(7)", - "uri": "record(9)", - }, - }, - "text": "yoohoo label_me", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(8)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(7)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(3)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(12)", - "following": "record(11)", - "muted": true, - }, - }, - "cid": "cids(8)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": true, - }, - }, - "cid": "cids(10)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(10)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(13)", - "val": "kind", - }, - ], - "uri": "record(13)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(8)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(10)", - "val": "kind", - }, - ], - "uri": "record(10)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(9)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(10)", - "uri": "record(13)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(8)", - "uri": "record(10)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(0)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(9)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(11)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "text": "dan here!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(14)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(12)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(12)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(15)", - "val": "self-label", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "labels": Object { - "$type": "com.atproto.label.defs#selfLabels", - "values": Array [ - Object { - "val": "self-label", - }, - ], - }, - "text": "hey there", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(15)", - "viewer": Object {}, - }, - }, -] -`; diff --git a/packages/pds/tests/views/actor-likes.test.ts b/packages/pds/tests/views/actor-likes.test.ts deleted file mode 100644 index 27c1ab4385c..00000000000 --- a/packages/pds/tests/views/actor-likes.test.ts +++ /dev/null @@ -1,117 +0,0 @@ -import AtpAgent, { AtUri } from '@atproto/api' -import { runTestServer, CloseFn } from '../_util' -import { SeedClient } from '../seeds/client' -import basicSeed from '../seeds/basic' - -describe('pds actor likes feed views', () => { - let agent: AtpAgent - let close: CloseFn - let sc: SeedClient - - // account dids, for convenience - let alice: string - let bob: string - let carol: string - let dan: string - - beforeAll(async () => { - const server = await runTestServer({ - dbPostgresSchema: 'pds_views_actor_likes', - }) - close = server.close - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await basicSeed(sc) - alice = sc.dids.alice - bob = sc.dids.bob - carol = sc.dids.carol - dan = sc.dids.dan - await server.processAll() - }) - - afterAll(async () => { - await close() - }) - - it('returns posts liked by actor', async () => { - const { - data: { feed: bobLikes }, - } = await agent.api.app.bsky.feed.getActorLikes( - { actor: sc.accounts[bob].handle }, - { headers: sc.getHeaders(bob) }, - ) - - expect(bobLikes).toHaveLength(3) - - await expect( - agent.api.app.bsky.feed.getActorLikes( - { actor: sc.accounts[bob].handle }, - { headers: sc.getHeaders(carol) }, - ), - ).rejects.toThrow('Profile not found') - }) - - it('viewer has blocked author of liked post(s)', async () => { - const bobBlocksAlice = await agent.api.app.bsky.graph.block.create( - { - repo: bob, // bob blocks alice - }, - { - subject: alice, - createdAt: new Date().toISOString(), - }, - sc.getHeaders(bob), - ) - - const { - data: { feed }, - } = await agent.api.app.bsky.feed.getActorLikes( - { actor: sc.accounts[bob].handle }, - { headers: sc.getHeaders(bob) }, - ) - - expect( - feed.every((item) => { - return item.post.author.did !== alice - }), - ).toBe(true) - - // unblock - await agent.api.app.bsky.graph.block.delete( - { repo: bob, rkey: new AtUri(bobBlocksAlice.uri).rkey }, - sc.getHeaders(bob), - ) - }) - - it('liked post author has blocked viewer', async () => { - const aliceBlocksBob = await agent.api.app.bsky.graph.block.create( - { - repo: alice, // alice blocks bob - }, - { - subject: bob, - createdAt: new Date().toISOString(), - }, - sc.getHeaders(alice), - ) - - const { - data: { feed }, - } = await agent.api.app.bsky.feed.getActorLikes( - { actor: sc.accounts[bob].handle }, - { headers: sc.getHeaders(bob) }, - ) - - expect( - feed.every((item) => { - return item.post.author.did !== alice - }), - ).toBe(true) - - // unblock - await agent.api.app.bsky.graph.block.delete( - { repo: alice, rkey: new AtUri(aliceBlocksBob.uri).rkey }, - sc.getHeaders(alice), - ) - }) -}) diff --git a/packages/pds/tests/views/actor-search.test.ts b/packages/pds/tests/views/actor-search.test.ts deleted file mode 100644 index d110d8e2e68..00000000000 --- a/packages/pds/tests/views/actor-search.test.ts +++ /dev/null @@ -1,464 +0,0 @@ -import AtpAgent from '@atproto/api' -import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/defs' -import { - runTestServer, - forSnapshot, - CloseFn, - paginateAll, - adminAuth, -} from '../_util' -import { SeedClient } from '../seeds/client' -import usersBulkSeed from '../seeds/users-bulk' -import { Database } from '../../src' - -describe('pds user search views', () => { - let agent: AtpAgent - let db: Database - let close: CloseFn - let sc: SeedClient - let headers: { [s: string]: string } - - beforeAll(async () => { - const server = await runTestServer({ - dbPostgresSchema: 'views_user_search', - }) - close = server.close - db = server.ctx.db - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await usersBulkSeed(sc) - headers = sc.getHeaders(Object.values(sc.dids)[0]) - await server.processAll() - }) - - afterAll(async () => { - await close() - }) - - it('typeahead gives relevant results', async () => { - const result = await agent.api.app.bsky.actor.searchActorsTypeahead( - { term: 'car' }, - { headers }, - ) - - const handles = result.data.actors.map((u) => u.handle) - - const shouldContain = [ - 'cara-wiegand69.test', - 'eudora-dietrich4.test', // Carol Littel - 'shane-torphy52.test', // Sadie Carter - 'aliya-hodkiewicz.test', // Carlton Abernathy IV - 'carlos6.test', - 'carolina-mcdermott77.test', - ] - - shouldContain.forEach((handle) => expect(handles).toContain(handle)) - - if (db.dialect === 'pg') { - expect(handles).toContain('cayla-marquardt39.test') // Fuzzy match supported by postgres - } else { - expect(handles).not.toContain('cayla-marquardt39.test') - } - - const shouldNotContain = [ - 'sven70.test', - 'hilario84.test', - 'santa-hermann78.test', - 'dylan61.test', - 'preston-harris.test', - 'loyce95.test', - 'melyna-zboncak.test', - ] - - shouldNotContain.forEach((handle) => expect(handles).not.toContain(handle)) - - const sorted = result.data.actors.sort((a, b) => - a.handle > b.handle ? 1 : -1, - ) - if (db.dialect === 'pg') { - expect(forSnapshot(sorted)).toEqual(snapTypeaheadPg) - } else { - expect(forSnapshot(sorted)).toEqual(snapTypeaheadSqlite) - } - }) - - it('typeahead gives empty result set when provided empty term', async () => { - const result = await agent.api.app.bsky.actor.searchActorsTypeahead( - { term: '' }, - { headers }, - ) - - expect(result.data.actors).toEqual([]) - }) - - it('typeahead applies limit', async () => { - const full = await agent.api.app.bsky.actor.searchActorsTypeahead( - { term: 'p' }, - { headers }, - ) - - expect(full.data.actors.length).toBeGreaterThan(5) - - const limited = await agent.api.app.bsky.actor.searchActorsTypeahead( - { term: 'p', limit: 5 }, - { headers }, - ) - - // @NOTE it's expected that searchActorsTypeahead doesn't have stable pagination - - const limitedIndexInFull = limited.data.actors.map((needle) => { - return full.data.actors.findIndex( - (haystack) => needle.did === haystack.did, - ) - }) - - // subset exists in full and is monotonic - expect(limitedIndexInFull.every((idx) => idx !== -1)).toEqual(true) - expect(limitedIndexInFull).toEqual( - [...limitedIndexInFull].sort((a, b) => a - b), - ) - }) - - it('search gives relevant results', async () => { - const result = await agent.api.app.bsky.actor.searchActors( - { term: 'car' }, - { headers }, - ) - - const handles = result.data.actors.map((u) => u.handle) - - const shouldContain = [ - 'cara-wiegand69.test', - 'eudora-dietrich4.test', // Carol Littel - 'shane-torphy52.test', // Sadie Carter - 'aliya-hodkiewicz.test', // Carlton Abernathy IV - 'carlos6.test', - 'carolina-mcdermott77.test', - ] - - shouldContain.forEach((handle) => expect(handles).toContain(handle)) - - if (db.dialect === 'pg') { - expect(handles).toContain('cayla-marquardt39.test') // Fuzzy match supported by postgres - } else { - expect(handles).not.toContain('cayla-marquardt39.test') - } - - const shouldNotContain = [ - 'sven70.test', - 'hilario84.test', - 'santa-hermann78.test', - 'dylan61.test', - 'preston-harris.test', - 'loyce95.test', - 'melyna-zboncak.test', - ] - - shouldNotContain.forEach((handle) => expect(handles).not.toContain(handle)) - - const sorted = result.data.actors.sort((a, b) => - a.handle > b.handle ? 1 : -1, - ) - if (db.dialect === 'pg') { - expect(forSnapshot(sorted)).toEqual(snapSearchPg) - } else { - expect(forSnapshot(sorted)).toEqual(snapSearchSqlite) - } - }) - - it('search gives empty result set when provided empty term', async () => { - const result = await agent.api.app.bsky.actor.searchActors( - { term: '' }, - { headers }, - ) - - expect(result.data.actors).toEqual([]) - }) - - it('paginates', async () => { - const results = (results) => results.flatMap((res) => res.actors) - const paginator = async (cursor?: string) => { - const res = await agent.api.app.bsky.actor.searchActors( - { term: 'p', cursor, limit: 3 }, - { headers }, - ) - return res.data - } - - const paginatedAll = await paginateAll(paginator) - paginatedAll.forEach((res) => - expect(res.actors.length).toBeLessThanOrEqual(3), - ) - - const full = await agent.api.app.bsky.actor.searchActors( - { term: 'p' }, - { headers }, - ) - - expect(full.data.actors.length).toBeGreaterThan(5) - const sortedFull = results([full.data]).sort((a, b) => - a.handle > b.handle ? 1 : -1, - ) - const sortedPaginated = results(paginatedAll).sort((a, b) => - a.handle > b.handle ? 1 : -1, - ) - expect(sortedPaginated).toEqual(sortedFull) - }) - - it('search handles bad input', async () => { - // Mostly for sqlite's benefit, since it uses LIKE and these are special characters that will - // get stripped. This input triggers a special case where there are no "safe" words for sqlite to search on. - const result = await agent.api.app.bsky.actor.searchActors( - { term: ' % _ ' }, - { headers }, - ) - - expect(result.data.actors).toEqual([]) - }) - - it('search blocks by actor takedown', async () => { - await agent.api.com.atproto.admin.takeModerationAction( - { - action: TAKEDOWN, - subject: { - $type: 'com.atproto.admin.defs#repoRef', - did: sc.dids['cara-wiegand69.test'], - }, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - const result = await agent.api.app.bsky.actor.searchActorsTypeahead( - { term: 'car' }, - { headers }, - ) - const handles = result.data.actors.map((u) => u.handle) - expect(handles).toContain('carlos6.test') - expect(handles).toContain('carolina-mcdermott77.test') - expect(handles).not.toContain('cara-wiegand69.test') - }) -}) - -// Not using jest snapshots because it doesn't handle the conditional pg/sqlite very well: -// you can achieve it using named snapshots, but when you run the tests for pg the test suite fails -// since the sqlite snapshots appear obsolete to jest (and vice-versa when you run the sqlite suite). - -const avatar = - 'https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg' - -const snapTypeaheadPg = [ - { - did: 'user(0)', - displayName: 'Carlton Abernathy IV', - handle: 'aliya-hodkiewicz.test', - avatar, - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(1)', - handle: 'cara-wiegand69.test', - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(2)', - handle: 'carlos6.test', - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(3)', - displayName: 'Latoya Windler', - handle: 'carolina-mcdermott77.test', - avatar, - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(4)', - displayName: 'Rachel Kshlerin', - handle: 'cayla-marquardt39.test', - avatar, - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(5)', - displayName: 'Carol Littel', - handle: 'eudora-dietrich4.test', - avatar, - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(6)', - displayName: 'Sadie Carter', - handle: 'shane-torphy52.test', - avatar, - viewer: { blockedBy: false, muted: false }, - labels: [], - }, -] - -const snapTypeaheadSqlite = [ - { - did: 'user(0)', - displayName: 'Carlton Abernathy IV', - handle: 'aliya-hodkiewicz.test', - avatar, - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(1)', - handle: 'cara-wiegand69.test', - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(2)', - handle: 'carlos6.test', - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(3)', - displayName: 'Latoya Windler', - handle: 'carolina-mcdermott77.test', - avatar, - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(4)', - displayName: 'Carol Littel', - handle: 'eudora-dietrich4.test', - avatar, - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(5)', - displayName: 'Sadie Carter', - handle: 'shane-torphy52.test', - avatar, - viewer: { blockedBy: false, muted: false }, - labels: [], - }, -] - -const snapSearchPg = [ - { - did: 'user(0)', - displayName: 'Carlton Abernathy IV', - indexedAt: '1970-01-01T00:00:00.000Z', - handle: 'aliya-hodkiewicz.test', - avatar, - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(1)', - handle: 'cara-wiegand69.test', - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(2)', - handle: 'carlos6.test', - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(3)', - displayName: 'Latoya Windler', - indexedAt: '1970-01-01T00:00:00.000Z', - handle: 'carolina-mcdermott77.test', - avatar, - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(4)', - displayName: 'Rachel Kshlerin', - indexedAt: '1970-01-01T00:00:00.000Z', - handle: 'cayla-marquardt39.test', - avatar, - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(5)', - displayName: 'Carol Littel', - indexedAt: '1970-01-01T00:00:00.000Z', - handle: 'eudora-dietrich4.test', - avatar, - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(6)', - displayName: 'Sadie Carter', - indexedAt: '1970-01-01T00:00:00.000Z', - handle: 'shane-torphy52.test', - avatar, - viewer: { blockedBy: false, muted: false }, - labels: [], - }, -] - -const snapSearchSqlite = [ - { - did: 'user(0)', - displayName: 'Carlton Abernathy IV', - indexedAt: '1970-01-01T00:00:00.000Z', - handle: 'aliya-hodkiewicz.test', - avatar, - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(1)', - handle: 'cara-wiegand69.test', - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(2)', - handle: 'carlos6.test', - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(3)', - displayName: 'Latoya Windler', - indexedAt: '1970-01-01T00:00:00.000Z', - handle: 'carolina-mcdermott77.test', - avatar, - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(4)', - displayName: 'Carol Littel', - indexedAt: '1970-01-01T00:00:00.000Z', - handle: 'eudora-dietrich4.test', - avatar, - viewer: { blockedBy: false, muted: false }, - labels: [], - }, - { - did: 'user(5)', - displayName: 'Sadie Carter', - indexedAt: '1970-01-01T00:00:00.000Z', - handle: 'shane-torphy52.test', - avatar, - viewer: { blockedBy: false, muted: false }, - labels: [], - }, -] diff --git a/packages/pds/tests/views/author-feed.test.ts b/packages/pds/tests/views/author-feed.test.ts deleted file mode 100644 index 2f9e8603b7b..00000000000 --- a/packages/pds/tests/views/author-feed.test.ts +++ /dev/null @@ -1,363 +0,0 @@ -import AtpAgent, { ComAtprotoAdminTakeModerationAction } from '@atproto/api' -import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/defs' -import { - runTestServer, - forSnapshot, - CloseFn, - paginateAll, - adminAuth, -} from '../_util' -import { SeedClient } from '../seeds/client' -import basicSeed from '../seeds/basic' -import { isRecord } from '../../src/lexicon/types/app/bsky/feed/post' -import { isView as isEmbedRecordWithMedia } from '../../src/lexicon/types/app/bsky/embed/recordWithMedia' -import { isView as isImageEmbed } from '../../src/lexicon/types/app/bsky/embed/images' - -describe('pds author feed views', () => { - let agent: AtpAgent - let close: CloseFn - let sc: SeedClient - - // account dids, for convenience - let alice: string - let bob: string - let carol: string - let dan: string - - const reverseModerationAction = async (id) => - agent.api.com.atproto.admin.reverseModerationAction( - { - id, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - - const takedownSubject = async ( - subject: ComAtprotoAdminTakeModerationAction.InputSchema['subject'], - ) => - agent.api.com.atproto.admin.takeModerationAction( - { - action: TAKEDOWN, - subject, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - - beforeAll(async () => { - const server = await runTestServer({ - dbPostgresSchema: 'views_author_feed', - }) - close = server.close - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await basicSeed(sc) - alice = sc.dids.alice - bob = sc.dids.bob - carol = sc.dids.carol - dan = sc.dids.dan - await server.processAll() - }) - - afterAll(async () => { - await close() - }) - - it('fetches full author feeds for self (sorted, minimal viewer state).', async () => { - const aliceForAlice = await agent.api.app.bsky.feed.getAuthorFeed( - { actor: sc.accounts[alice].handle }, - { - headers: sc.getHeaders(alice), - }, - ) - - expect(forSnapshot(aliceForAlice.data.feed)).toMatchSnapshot() - - const bobForBob = await agent.api.app.bsky.feed.getAuthorFeed( - { actor: sc.accounts[bob].handle }, - { - headers: sc.getHeaders(bob), - }, - ) - - expect(forSnapshot(bobForBob.data.feed)).toMatchSnapshot() - - const carolForCarol = await agent.api.app.bsky.feed.getAuthorFeed( - { actor: sc.accounts[carol].handle }, - { - headers: sc.getHeaders(carol), - }, - ) - - expect(forSnapshot(carolForCarol.data.feed)).toMatchSnapshot() - - const danForDan = await agent.api.app.bsky.feed.getAuthorFeed( - { actor: sc.accounts[dan].handle }, - { - headers: sc.getHeaders(dan), - }, - ) - - expect(forSnapshot(danForDan.data.feed)).toMatchSnapshot() - }) - - it("reflects fetching user's state in the feed.", async () => { - const aliceForCarol = await agent.api.app.bsky.feed.getAuthorFeed( - { actor: sc.accounts[alice].handle }, - { - headers: sc.getHeaders(carol), - }, - ) - - aliceForCarol.data.feed.forEach((postView) => { - const { viewer, uri } = postView.post - expect(viewer?.like).toEqual(sc.likes[carol]?.[uri]?.toString()) - expect(viewer?.repost).toEqual(sc.reposts[carol][uri]?.toString()) - }) - - expect(forSnapshot(aliceForCarol.data.feed)).toMatchSnapshot() - }) - - it('omits reposts from muted users.', async () => { - await agent.api.app.bsky.graph.muteActor( - { actor: alice }, // Has a repost by dan: will be omitted from dan's feed - { headers: sc.getHeaders(bob), encoding: 'application/json' }, - ) - await agent.api.app.bsky.graph.muteActor( - { actor: dan }, // Feed author: their posts will still appear - { headers: sc.getHeaders(bob), encoding: 'application/json' }, - ) - const bobForDan = await agent.api.app.bsky.feed.getAuthorFeed( - { actor: sc.accounts[dan].handle }, - { headers: sc.getHeaders(bob) }, - ) - - expect(forSnapshot(bobForDan.data.feed)).toMatchSnapshot() - - await agent.api.app.bsky.graph.unmuteActor( - { actor: alice }, - { headers: sc.getHeaders(bob), encoding: 'application/json' }, - ) - await agent.api.app.bsky.graph.unmuteActor( - { actor: dan }, - { headers: sc.getHeaders(bob), encoding: 'application/json' }, - ) - }) - - it('paginates', async () => { - const results = (results) => results.flatMap((res) => res.feed) - const paginator = async (cursor?: string) => { - const res = await agent.api.app.bsky.feed.getAuthorFeed( - { - actor: sc.accounts[alice].handle, - cursor, - limit: 2, - }, - { headers: sc.getHeaders(dan) }, - ) - return res.data - } - - const paginatedAll = await paginateAll(paginator) - paginatedAll.forEach((res) => - expect(res.feed.length).toBeLessThanOrEqual(2), - ) - - const full = await agent.api.app.bsky.feed.getAuthorFeed( - { - actor: sc.accounts[alice].handle, - }, - { headers: sc.getHeaders(dan) }, - ) - - expect(full.data.feed.length).toEqual(4) - expect(results(paginatedAll)).toEqual(results([full.data])) - }) - - it('blocked by actor takedown.', async () => { - const { data: preBlock } = await agent.api.app.bsky.feed.getAuthorFeed( - { actor: alice }, - { headers: sc.getHeaders(carol) }, - ) - - expect(preBlock.feed.length).toBeGreaterThan(0) - - const { data: action } = await takedownSubject({ - $type: 'com.atproto.admin.defs#repoRef', - did: alice, - }) - - const attempt = agent.api.app.bsky.feed.getAuthorFeed( - { actor: alice }, - { headers: sc.getHeaders(carol) }, - ) - await expect(attempt).rejects.toThrow('Profile not found') - - // Cleanup - await reverseModerationAction(action.id) - }) - - it('blocked by record takedown.', async () => { - const { data: preBlock } = await agent.api.app.bsky.feed.getAuthorFeed( - { actor: alice }, - { headers: sc.getHeaders(carol) }, - ) - - expect(preBlock.feed.length).toBeGreaterThan(0) - - const post = preBlock.feed[0].post - - const { data: action } = await takedownSubject({ - $type: 'com.atproto.repo.strongRef', - uri: post.uri, - cid: post.cid, - }) - - const { data: postBlock } = await agent.api.app.bsky.feed.getAuthorFeed( - { actor: alice }, - { headers: sc.getHeaders(carol) }, - ) - - expect(postBlock.feed.length).toEqual(preBlock.feed.length - 1) - expect(postBlock.feed.map((item) => item.post.uri)).not.toContain(post.uri) - - // Cleanup - await reverseModerationAction(action.id) - }) - - it('includes takendown posts for admins', async () => { - const { data: preTakedown } = await agent.api.app.bsky.feed.getAuthorFeed( - { actor: alice }, - { headers: sc.getHeaders(carol) }, - ) - - expect(preTakedown.feed.length).toBeGreaterThan(0) - - const post = preTakedown.feed[0].post - - const { data: takedownAction } = await takedownSubject({ - $type: 'com.atproto.repo.strongRef', - uri: post.uri, - cid: post.cid, - }) - - const [{ data: postTakedownForCarol }, { data: postTakedownForAdmin }] = - await Promise.all([ - agent.api.app.bsky.feed.getAuthorFeed( - { actor: alice }, - { headers: sc.getHeaders(carol) }, - ), - agent.api.app.bsky.feed.getAuthorFeed( - { actor: alice }, - { headers: { authorization: adminAuth() } }, - ), - ]) - - const takendownPostInCarolsFeed = postTakedownForCarol.feed.find( - (item) => item.post.uri === post.uri || !!post.takedownId, - ) - const takendownPostInAdminsFeed = postTakedownForAdmin.feed.find( - (item) => item.post.uri === post.uri || !!post.takedownId, - ) - expect(takendownPostInCarolsFeed).toBeFalsy() - expect(takendownPostInAdminsFeed).toBeTruthy() - expect(takendownPostInAdminsFeed?.post.takedownId).toEqual( - takedownAction.id, - ) - - // Cleanup - await reverseModerationAction(takedownAction.id) - }) - - it('can filter by posts_with_media', async () => { - const { data: carolFeed } = await agent.api.app.bsky.feed.getAuthorFeed( - { - actor: carol, - filter: 'posts_with_media', - }, - { - headers: sc.getHeaders(alice), - }, - ) - - expect(carolFeed.feed.length).toBeGreaterThan(0) - expect( - carolFeed.feed.every(({ post }) => { - const isRecordWithActorMedia = - isEmbedRecordWithMedia(post.embed) && isImageEmbed(post.embed?.media) - const isActorMedia = isImageEmbed(post.embed) - const isFromActor = post.author.did === carol - - return (isRecordWithActorMedia || isActorMedia) && isFromActor - }), - ).toBeTruthy() - - const { data: bobFeed } = await agent.api.app.bsky.feed.getAuthorFeed( - { - actor: bob, - filter: 'posts_with_media', - }, - { - headers: sc.getHeaders(alice), - }, - ) - - expect(bobFeed.feed.length).toBeGreaterThan(0) - expect( - bobFeed.feed.every(({ post }) => { - return isImageEmbed(post.embed) && post.author.did === bob - }), - ).toBeTruthy() - - const { data: danFeed } = await agent.api.app.bsky.feed.getAuthorFeed( - { - actor: dan, - filter: 'posts_with_media', - }, - { - headers: sc.getHeaders(alice), - }, - ) - - expect(danFeed.feed.length).toEqual(0) - }) - - it('filters by posts_no_replies', async () => { - const { data: carolFeed } = await agent.api.app.bsky.feed.getAuthorFeed( - { actor: carol, filter: 'posts_no_replies' }, - { headers: sc.getHeaders(alice) }, - ) - - expect( - carolFeed.feed.every(({ post }) => { - return ( - (isRecord(post.record) && !post.record.reply) || - (isRecord(post.record) && post.record.reply) - ) - }), - ).toBeTruthy() - - const { data: danFeed } = await agent.api.app.bsky.feed.getAuthorFeed( - { actor: dan, filter: 'posts_no_replies' }, - { headers: sc.getHeaders(alice) }, - ) - - expect( - danFeed.feed.every(({ post }) => { - return ( - (isRecord(post.record) && !post.record.reply) || - (isRecord(post.record) && post.record.reply) - ) - }), - ).toBeTruthy() - }) -}) diff --git a/packages/pds/tests/views/blocks.test.ts b/packages/pds/tests/views/blocks.test.ts deleted file mode 100644 index 8d8fb3dba76..00000000000 --- a/packages/pds/tests/views/blocks.test.ts +++ /dev/null @@ -1,531 +0,0 @@ -import assert from 'assert' -import AtpAgent, { AtUri } from '@atproto/api' -import { RecordRef } from '@atproto/bsky/tests/seeds/client' -import { BlockedActorError } from '@atproto/api/src/client/types/app/bsky/feed/getAuthorFeed' -import { BlockedByActorError } from '@atproto/api/src/client/types/app/bsky/feed/getAuthorFeed' -import { isThreadViewPost } from '@atproto/api/src/client/types/app/bsky/feed/defs' -import { - isViewRecord as isEmbedViewRecord, - isViewBlocked as isEmbedViewBlocked, -} from '@atproto/api/src/client/types/app/bsky/embed/record' -import { runTestServer, CloseFn, TestServerInfo, forSnapshot } from '../_util' -import { SeedClient } from '../seeds/client' -import basicSeed from '../seeds/basic' - -describe('pds views with blocking', () => { - let server: TestServerInfo - let agent: AtpAgent - let close: CloseFn - let sc: SeedClient - let danBlockCarol: { uri: string } - let aliceReplyToDan: { ref: RecordRef } - let carolReplyToDan: { ref: RecordRef } - - let alice: string - let carol: string - let dan: string - let danBlockUri: string - - beforeAll(async () => { - server = await runTestServer({ - dbPostgresSchema: 'views_block', - }) - close = server.close - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await basicSeed(sc) - alice = sc.dids.alice - carol = sc.dids.carol - dan = sc.dids.dan - // add follows to ensure blocks work even w follows - await sc.follow(carol, dan) - await sc.follow(dan, carol) - aliceReplyToDan = await sc.reply( - alice, - sc.posts[dan][0].ref, - sc.posts[dan][0].ref, - 'alice replies to dan', - ) - carolReplyToDan = await sc.reply( - carol, - sc.posts[dan][0].ref, - sc.posts[dan][0].ref, - 'carol replies to dan', - ) - // dan blocks carol - danBlockCarol = await agent.api.app.bsky.graph.block.create( - { repo: dan }, - { createdAt: new Date().toISOString(), subject: carol }, - sc.getHeaders(dan), - ) - danBlockUri = danBlockCarol.uri - await server.processAll() - }) - - afterAll(async () => { - await close() - }) - - it('blocks thread post', async () => { - const { data: threadAlice } = await agent.api.app.bsky.feed.getPostThread( - { depth: 1, uri: sc.posts[carol][0].ref.uriStr }, - { headers: sc.getHeaders(dan) }, - ) - expect(threadAlice).toEqual({ - thread: { - $type: 'app.bsky.feed.defs#blockedPost', - uri: sc.posts[carol][0].ref.uriStr, - blocked: true, - author: { - did: carol, - viewer: { - blockedBy: false, - blocking: danBlockUri, - }, - }, - }, - }) - const { data: threadCarol } = await agent.api.app.bsky.feed.getPostThread( - { depth: 1, uri: sc.posts[dan][0].ref.uriStr }, - { headers: sc.getHeaders(carol) }, - ) - expect(threadCarol).toEqual({ - thread: { - $type: 'app.bsky.feed.defs#blockedPost', - uri: sc.posts[dan][0].ref.uriStr, - blocked: true, - author: { - did: dan, - viewer: { - blockedBy: true, - }, - }, - }, - }) - }) - - it('blocks thread reply', async () => { - // Contains reply by carol - const { data: thread } = await agent.api.app.bsky.feed.getPostThread( - { depth: 1, uri: sc.posts[alice][1].ref.uriStr }, - { headers: sc.getHeaders(dan) }, - ) - expect(forSnapshot(thread)).toMatchSnapshot() - }) - - it('blocks thread parent', async () => { - // Parent is a post by dan - const { data: thread } = await agent.api.app.bsky.feed.getPostThread( - { depth: 1, uri: aliceReplyToDan.ref.uriStr }, - { headers: sc.getHeaders(carol) }, - ) - expect(forSnapshot(thread)).toMatchSnapshot() - }) - - it('blocks record embeds', async () => { - // Contains a deep embed of carol's post, blocked by dan - const { data: thread } = await agent.api.app.bsky.feed.getPostThread( - { depth: 0, uri: sc.posts[alice][2].ref.uriStr }, - { headers: sc.getHeaders(dan) }, - ) - expect(forSnapshot(thread)).toMatchSnapshot() - }) - - it('errors on getting author feed', async () => { - const attempt1 = agent.api.app.bsky.feed.getAuthorFeed( - { actor: carol }, - { headers: sc.getHeaders(dan) }, - ) - await expect(attempt1).rejects.toThrow(BlockedActorError) - - const attempt2 = agent.api.app.bsky.feed.getAuthorFeed( - { actor: dan }, - { headers: sc.getHeaders(carol) }, - ) - await expect(attempt2).rejects.toThrow(BlockedByActorError) - }) - - it('strips blocked users out of getTimeline', async () => { - const resCarol = await agent.api.app.bsky.feed.getTimeline( - { limit: 100 }, - { headers: sc.getHeaders(carol) }, - ) - expect( - resCarol.data.feed.some((post) => post.post.author.did === dan), - ).toBeFalsy() - - const resDan = await agent.api.app.bsky.feed.getTimeline( - { limit: 100 }, - { headers: sc.getHeaders(dan) }, - ) - expect( - resDan.data.feed.some((post) => post.post.author.did === carol), - ).toBeFalsy() - }) - - it('strips blocked users out of getPopular', async () => { - for (let i = 0; i < 15; i++) { - const name = `user${i}` - await sc.createAccount(name, { - handle: `user${i}.test`, - email: `user${i}@test.com`, - password: 'password', - }) - await sc.like(sc.dids[name], sc.posts[alice][0].ref) - await sc.like(sc.dids[name], sc.posts[carol][0].ref) - await sc.like(sc.dids[name], sc.posts[dan][0].ref) - } - - const resCarol = await agent.api.app.bsky.unspecced.getPopular( - {}, - { headers: sc.getHeaders(carol) }, - ) - expect( - resCarol.data.feed.some((post) => post.post.author.did === alice), - ).toBeTruthy() - expect( - resCarol.data.feed.some((post) => post.post.author.did === carol), - ).toBeTruthy() - expect( - resCarol.data.feed.some((post) => post.post.author.did === dan), - ).toBeFalsy() - - const resDan = await agent.api.app.bsky.unspecced.getPopular( - {}, - { headers: sc.getHeaders(dan) }, - ) - expect( - resDan.data.feed.some((post) => post.post.author.did === alice), - ).toBeTruthy() - expect( - resDan.data.feed.some((post) => post.post.author.did === carol), - ).toBeFalsy() - expect( - resDan.data.feed.some((post) => post.post.author.did === dan), - ).toBeTruthy() - }) - - it('returns block status on getProfile', async () => { - const resCarol = await agent.api.app.bsky.actor.getProfile( - { actor: dan }, - { headers: sc.getHeaders(carol) }, - ) - expect(resCarol.data.viewer?.blocking).toBeUndefined - expect(resCarol.data.viewer?.blockedBy).toBe(true) - - const resDan = await agent.api.app.bsky.actor.getProfile( - { actor: carol }, - { headers: sc.getHeaders(dan) }, - ) - expect(resDan.data.viewer?.blocking).toBeDefined - expect(resDan.data.viewer?.blockedBy).toBe(false) - }) - - it('returns block status on getProfiles', async () => { - const resCarol = await agent.api.app.bsky.actor.getProfiles( - { actors: [alice, dan] }, - { headers: sc.getHeaders(carol) }, - ) - expect(resCarol.data.profiles[0].viewer?.blocking).toBeUndefined() - expect(resCarol.data.profiles[0].viewer?.blockedBy).toBe(false) - expect(resCarol.data.profiles[1].viewer?.blocking).toBeUndefined() - expect(resCarol.data.profiles[1].viewer?.blockedBy).toBe(true) - - const resDan = await agent.api.app.bsky.actor.getProfiles( - { actors: [alice, carol] }, - { headers: sc.getHeaders(dan) }, - ) - expect(resDan.data.profiles[0].viewer?.blocking).toBeUndefined() - expect(resDan.data.profiles[0].viewer?.blockedBy).toBe(false) - expect(resDan.data.profiles[1].viewer?.blocking).toBeDefined() - expect(resDan.data.profiles[1].viewer?.blockedBy).toBe(false) - }) - - it('does not return block violating follows', async () => { - const resCarol = await agent.api.app.bsky.graph.getFollows( - { actor: carol }, - { headers: sc.getHeaders(alice) }, - ) - expect(resCarol.data.follows.some((f) => f.did === dan)).toBe(false) - - const resDan = await agent.api.app.bsky.graph.getFollows( - { actor: dan }, - { headers: sc.getHeaders(alice) }, - ) - expect(resDan.data.follows.some((f) => f.did === carol)).toBe(false) - }) - - it('does not return block violating followers', async () => { - const resCarol = await agent.api.app.bsky.graph.getFollowers( - { actor: carol }, - { headers: sc.getHeaders(alice) }, - ) - expect(resCarol.data.followers.some((f) => f.did === dan)).toBe(false) - - const resDan = await agent.api.app.bsky.graph.getFollowers( - { actor: dan }, - { headers: sc.getHeaders(alice) }, - ) - expect(resDan.data.followers.some((f) => f.did === carol)).toBe(false) - }) - - it('does not return posts from blocked users', async () => { - const alicePost = sc.posts[alice][0].ref.uriStr - const carolPost = sc.posts[carol][0].ref.uriStr - const danPost = sc.posts[dan][0].ref.uriStr - - const resCarol = await agent.api.app.bsky.feed.getPosts( - { uris: [alicePost, carolPost, danPost] }, - { headers: sc.getHeaders(carol) }, - ) - expect(resCarol.data.posts.some((p) => p.uri === alicePost)).toBe(true) - expect(resCarol.data.posts.some((p) => p.uri === carolPost)).toBe(true) - expect(resCarol.data.posts.some((p) => p.uri === danPost)).toBe(false) - - const resDan = await agent.api.app.bsky.feed.getPosts( - { uris: [alicePost, carolPost, danPost] }, - { headers: sc.getHeaders(dan) }, - ) - expect(resDan.data.posts.some((p) => p.uri === alicePost)).toBe(true) - expect(resDan.data.posts.some((p) => p.uri === carolPost)).toBe(false) - expect(resDan.data.posts.some((p) => p.uri === danPost)).toBe(true) - }) - - it('does not return notifs for blocked accounts', async () => { - const resCarol = await agent.api.app.bsky.notification.listNotifications( - { - limit: 100, - }, - { headers: sc.getHeaders(carol) }, - ) - expect( - resCarol.data.notifications.some((notif) => notif.author.did === dan), - ).toBeFalsy() - - const resDan = await agent.api.app.bsky.notification.listNotifications( - { - limit: 100, - }, - { headers: sc.getHeaders(dan) }, - ) - expect( - resDan.data.notifications.some((notif) => notif.author.did === carol), - ).toBeFalsy() - }) - - it('does not return blocked accounts in actor search', async () => { - const resCarol = await agent.api.app.bsky.actor.searchActors( - { - term: 'dan.test', - }, - { headers: sc.getHeaders(carol) }, - ) - expect(resCarol.data.actors.some((actor) => actor.did === dan)).toBeFalsy() - - const resDan = await agent.api.app.bsky.actor.searchActors( - { - term: 'carol.test', - }, - { headers: sc.getHeaders(dan) }, - ) - expect(resDan.data.actors.some((actor) => actor.did === carol)).toBeFalsy() - }) - - it('does not return blocked accounts in actor search typeahead', async () => { - const resCarol = await agent.api.app.bsky.actor.searchActorsTypeahead( - { - term: 'dan.test', - }, - { headers: sc.getHeaders(carol) }, - ) - expect(resCarol.data.actors.some((actor) => actor.did === dan)).toBeFalsy() - - const resDan = await agent.api.app.bsky.actor.searchActorsTypeahead( - { - term: 'carol.test', - }, - { headers: sc.getHeaders(dan) }, - ) - expect(resDan.data.actors.some((actor) => actor.did === carol)).toBeFalsy() - }) - - it('does not return blocked accounts in get suggestions', async () => { - // unfollow so they _would_ show up in suggestions if not for block - await sc.unfollow(carol, dan) - await sc.unfollow(dan, carol) - - const resCarol = await agent.api.app.bsky.actor.getSuggestions( - { - limit: 100, - }, - { headers: sc.getHeaders(carol) }, - ) - expect(resCarol.data.actors.some((actor) => actor.did === dan)).toBeFalsy() - - const resDan = await agent.api.app.bsky.actor.getSuggestions( - { - limit: 100, - }, - { headers: sc.getHeaders(dan) }, - ) - expect(resDan.data.actors.some((actor) => actor.did === carol)).toBeFalsy() - }) - - it('does not serve blocked replies', async () => { - const getThreadPostUri = (r) => r?.['post']?.['uri'] - // reply then block - const { data: replyThenBlock } = - await agent.api.app.bsky.feed.getPostThread( - { depth: 1, uri: sc.posts[dan][0].ref.uriStr }, - { headers: sc.getHeaders(alice) }, - ) - assert(isThreadViewPost(replyThenBlock.thread)) - expect(replyThenBlock.thread.replies?.map(getThreadPostUri)).toEqual([ - aliceReplyToDan.ref.uriStr, - ]) - - // unblock - await agent.api.app.bsky.graph.block.delete( - { repo: dan, rkey: new AtUri(danBlockCarol.uri).rkey }, - sc.getHeaders(dan), - ) - const { data: unblock } = await agent.api.app.bsky.feed.getPostThread( - { depth: 1, uri: sc.posts[dan][0].ref.uriStr }, - { headers: sc.getHeaders(alice) }, - ) - assert(isThreadViewPost(unblock.thread)) - expect(unblock.thread.replies?.map(getThreadPostUri)).toEqual([ - carolReplyToDan.ref.uriStr, - aliceReplyToDan.ref.uriStr, - ]) - - // block then reply - danBlockCarol = await agent.api.app.bsky.graph.block.create( - { repo: dan }, - { createdAt: new Date().toISOString(), subject: carol }, - sc.getHeaders(dan), - ) - const carolReplyToDan2 = await sc.reply( - carol, - sc.posts[dan][1].ref, - sc.posts[dan][1].ref, - 'carol replies to dan again', - ) - const { data: blockThenReply } = - await agent.api.app.bsky.feed.getPostThread( - { depth: 1, uri: sc.posts[dan][0].ref.uriStr }, - { headers: sc.getHeaders(alice) }, - ) - assert(isThreadViewPost(blockThenReply.thread)) - expect(replyThenBlock.thread.replies?.map(getThreadPostUri)).toEqual([ - aliceReplyToDan.ref.uriStr, - ]) - - // cleanup - await agent.api.app.bsky.feed.post.delete( - { repo: carol, rkey: carolReplyToDan2.ref.uri.rkey }, - sc.getHeaders(carol), - ) - }) - - it('does not serve blocked embeds to third-party', async () => { - // embed then block - const { data: embedThenBlock } = - await agent.api.app.bsky.feed.getPostThread( - { depth: 0, uri: sc.posts[dan][1].ref.uriStr }, - { headers: sc.getHeaders(alice) }, - ) - assert(isThreadViewPost(embedThenBlock.thread)) - assert(isEmbedViewBlocked(embedThenBlock.thread.post.embed?.record)) - - // unblock - await agent.api.app.bsky.graph.block.delete( - { repo: dan, rkey: new AtUri(danBlockCarol.uri).rkey }, - sc.getHeaders(dan), - ) - const { data: unblock } = await agent.api.app.bsky.feed.getPostThread( - { depth: 0, uri: sc.posts[dan][1].ref.uriStr }, - { headers: sc.getHeaders(alice) }, - ) - assert(isThreadViewPost(unblock.thread)) - assert(isEmbedViewRecord(unblock.thread.post.embed?.record)) - - // block then embed - danBlockCarol = await agent.api.app.bsky.graph.block.create( - { repo: dan }, - { createdAt: new Date().toISOString(), subject: carol }, - sc.getHeaders(dan), - ) - const carolEmbedsDan = await sc.post( - carol, - 'carol embeds dan', - undefined, - undefined, - sc.posts[dan][0].ref, - ) - const { data: blockThenEmbed } = - await agent.api.app.bsky.feed.getPostThread( - { depth: 0, uri: carolEmbedsDan.ref.uriStr }, - { headers: sc.getHeaders(alice) }, - ) - assert(isThreadViewPost(blockThenEmbed.thread)) - assert(isEmbedViewBlocked(blockThenEmbed.thread.post.embed?.record)) - - // cleanup - await agent.api.app.bsky.feed.post.delete( - { repo: carol, rkey: carolEmbedsDan.ref.uri.rkey }, - sc.getHeaders(carol), - ) - }) - - it('applies third-party blocking rules in feeds.', async () => { - // alice follows carol and dan, block exists between carol and dan. - const replyBlockedUri = carolReplyToDan.ref.uriStr - const embedBlockedUri = sc.posts[dan][1].ref.uriStr - const { data: timeline } = await agent.api.app.bsky.feed.getTimeline( - { limit: 100 }, - { headers: sc.getHeaders(alice) }, - ) - const replyBlockedPost = timeline.feed.find( - (item) => item.post.uri === replyBlockedUri, - ) - expect(replyBlockedPost).toBeUndefined() - const embedBlockedPost = timeline.feed.find( - (item) => item.post.uri === embedBlockedUri, - ) - assert(embedBlockedPost) - assert(isEmbedViewBlocked(embedBlockedPost.post.embed?.record)) - }) - - it('returns a list of blocks', async () => { - await agent.api.app.bsky.graph.block.create( - { repo: dan }, - { createdAt: new Date().toISOString(), subject: alice }, - sc.getHeaders(dan), - ) - - const res = await agent.api.app.bsky.graph.getBlocks( - {}, - { headers: sc.getHeaders(dan) }, - ) - const dids = res.data.blocks.map((block) => block.did).sort() - expect(dids).toEqual([alice, carol].sort()) - }) - - it('paginates getBlocks', async () => { - const full = await agent.api.app.bsky.graph.getBlocks( - {}, - { headers: sc.getHeaders(dan) }, - ) - const first = await agent.api.app.bsky.graph.getBlocks( - { limit: 1 }, - { headers: sc.getHeaders(dan) }, - ) - const second = await agent.api.app.bsky.graph.getBlocks( - { cursor: first.data.cursor }, - { headers: sc.getHeaders(dan) }, - ) - const combined = [...first.data.blocks, ...second.data.blocks] - expect(combined).toEqual(full.data.blocks) - }) -}) diff --git a/packages/pds/tests/views/follows.test.ts b/packages/pds/tests/views/follows.test.ts deleted file mode 100644 index 606b85f9d48..00000000000 --- a/packages/pds/tests/views/follows.test.ts +++ /dev/null @@ -1,266 +0,0 @@ -import AtpAgent from '@atproto/api' -import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/defs' -import { - forSnapshot, - paginateAll, - adminAuth, - runTestServer, - CloseFn, -} from '../_util' -import { SeedClient } from '../seeds/client' -import followsSeed from '../seeds/follows' - -describe('pds follow views', () => { - let agent: AtpAgent - let close: CloseFn - let sc: SeedClient - - // account dids, for convenience - let alice: string - - beforeAll(async () => { - const server = await runTestServer({ - dbPostgresSchema: 'views_follows', - }) - close = server.close - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await followsSeed(sc) - alice = sc.dids.alice - await server.processAll() - }) - - afterAll(async () => { - await close() - }) - - it('fetches followers', async () => { - const aliceFollowers = await agent.api.app.bsky.graph.getFollowers( - { actor: sc.dids.alice }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(aliceFollowers.data)).toMatchSnapshot() - - const bobFollowers = await agent.api.app.bsky.graph.getFollowers( - { actor: sc.dids.bob }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(bobFollowers.data)).toMatchSnapshot() - - const carolFollowers = await agent.api.app.bsky.graph.getFollowers( - { actor: sc.dids.carol }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(carolFollowers.data)).toMatchSnapshot() - - const danFollowers = await agent.api.app.bsky.graph.getFollowers( - { actor: sc.dids.dan }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(danFollowers.data)).toMatchSnapshot() - - const eveFollowers = await agent.api.app.bsky.graph.getFollowers( - { actor: sc.dids.eve }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(eveFollowers.data)).toMatchSnapshot() - }) - - it('fetches followers by handle', async () => { - const byDid = await agent.api.app.bsky.graph.getFollowers( - { actor: sc.dids.alice }, - { headers: sc.getHeaders(alice) }, - ) - const byHandle = await agent.api.app.bsky.graph.getFollowers( - { actor: sc.accounts[alice].handle }, - { headers: sc.getHeaders(alice) }, - ) - expect(byHandle.data).toEqual(byDid.data) - }) - - it('paginates followers', async () => { - const results = (results) => results.flatMap((res) => res.followers) - const paginator = async (cursor?: string) => { - const res = await agent.api.app.bsky.graph.getFollowers( - { - actor: sc.dids.alice, - cursor, - limit: 2, - }, - { headers: sc.getHeaders(alice) }, - ) - return res.data - } - - const paginatedAll = await paginateAll(paginator) - paginatedAll.forEach((res) => - expect(res.followers.length).toBeLessThanOrEqual(2), - ) - - const full = await agent.api.app.bsky.graph.getFollowers( - { actor: sc.dids.alice }, - { headers: sc.getHeaders(alice) }, - ) - - expect(full.data.followers.length).toEqual(4) - expect(results(paginatedAll)).toEqual(results([full.data])) - }) - - it('blocks followers by actor takedown', async () => { - const { data: modAction } = - await agent.api.com.atproto.admin.takeModerationAction( - { - action: TAKEDOWN, - subject: { - $type: 'com.atproto.admin.defs#repoRef', - did: sc.dids.dan, - }, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - - const aliceFollowers = await agent.api.app.bsky.graph.getFollowers( - { actor: sc.dids.alice }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(aliceFollowers.data)).toMatchSnapshot() - - await agent.api.com.atproto.admin.reverseModerationAction( - { - id: modAction.id, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - }) - - it('fetches follows', async () => { - const aliceFollowers = await agent.api.app.bsky.graph.getFollows( - { actor: sc.dids.alice }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(aliceFollowers.data)).toMatchSnapshot() - - const bobFollowers = await agent.api.app.bsky.graph.getFollows( - { actor: sc.dids.bob }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(bobFollowers.data)).toMatchSnapshot() - - const carolFollowers = await agent.api.app.bsky.graph.getFollows( - { actor: sc.dids.carol }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(carolFollowers.data)).toMatchSnapshot() - - const danFollowers = await agent.api.app.bsky.graph.getFollows( - { actor: sc.dids.dan }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(danFollowers.data)).toMatchSnapshot() - - const eveFollowers = await agent.api.app.bsky.graph.getFollows( - { actor: sc.dids.eve }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(eveFollowers.data)).toMatchSnapshot() - }) - - it('fetches follows by handle', async () => { - const byDid = await agent.api.app.bsky.graph.getFollows( - { actor: sc.dids.alice }, - { headers: sc.getHeaders(alice) }, - ) - const byHandle = await agent.api.app.bsky.graph.getFollows( - { actor: sc.accounts[alice].handle }, - { headers: sc.getHeaders(alice) }, - ) - expect(byHandle.data).toEqual(byDid.data) - }) - - it('paginates follows', async () => { - const results = (results) => results.flatMap((res) => res.follows) - const paginator = async (cursor?: string) => { - const res = await agent.api.app.bsky.graph.getFollows( - { - actor: sc.dids.alice, - cursor, - limit: 2, - }, - { headers: sc.getHeaders(alice) }, - ) - return res.data - } - - const paginatedAll = await paginateAll(paginator) - paginatedAll.forEach((res) => - expect(res.follows.length).toBeLessThanOrEqual(2), - ) - - const full = await agent.api.app.bsky.graph.getFollows( - { actor: sc.dids.alice }, - { headers: sc.getHeaders(alice) }, - ) - - expect(full.data.follows.length).toEqual(4) - expect(results(paginatedAll)).toEqual(results([full.data])) - }) - - it('blocks follows by actor takedown', async () => { - const { data: modAction } = - await agent.api.com.atproto.admin.takeModerationAction( - { - action: TAKEDOWN, - subject: { - $type: 'com.atproto.admin.defs#repoRef', - did: sc.dids.dan, - }, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - - const aliceFollows = await agent.api.app.bsky.graph.getFollows( - { actor: sc.dids.alice }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(aliceFollows.data)).toMatchSnapshot() - - await agent.api.com.atproto.admin.reverseModerationAction( - { - id: modAction.id, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - }) -}) diff --git a/packages/pds/tests/views/likes.test.ts b/packages/pds/tests/views/likes.test.ts deleted file mode 100644 index d77251d8b25..00000000000 --- a/packages/pds/tests/views/likes.test.ts +++ /dev/null @@ -1,97 +0,0 @@ -import AtpAgent from '@atproto/api' -import { SeedClient } from '../seeds/client' -import likesSeed from '../seeds/likes' -import { - CloseFn, - constantDate, - forSnapshot, - paginateAll, - runTestServer, -} from '../_util' - -describe('pds like views', () => { - let agent: AtpAgent - let close: CloseFn - let sc: SeedClient - - // account dids, for convenience - let alice: string - let bob: string - - beforeAll(async () => { - const server = await runTestServer({ - dbPostgresSchema: 'views_likes', - }) - close = server.close - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await likesSeed(sc) - alice = sc.dids.alice - bob = sc.dids.bob - await server.processAll() - }) - - afterAll(async () => { - await close() - }) - - const getCursors = (items: { createdAt?: string }[]) => - items.map((item) => item.createdAt ?? constantDate) - - const getSortedCursors = (items: { createdAt?: string }[]) => - getCursors(items).sort((a, b) => tstamp(b) - tstamp(a)) - - const tstamp = (x: string) => new Date(x).getTime() - - it('fetches post likes', async () => { - const alicePost = await agent.api.app.bsky.feed.getLikes( - { uri: sc.posts[alice][1].ref.uriStr }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(alicePost.data)).toMatchSnapshot() - expect(getCursors(alicePost.data.likes)).toEqual( - getSortedCursors(alicePost.data.likes), - ) - }) - - it('fetches reply likes', async () => { - const bobReply = await agent.api.app.bsky.feed.getLikes( - { uri: sc.replies[bob][0].ref.uriStr }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(bobReply.data)).toMatchSnapshot() - expect(getCursors(bobReply.data.likes)).toEqual( - getSortedCursors(bobReply.data.likes), - ) - }) - - it('paginates', async () => { - const results = (results) => results.flatMap((res) => res.likes) - const paginator = async (cursor?: string) => { - const res = await agent.api.app.bsky.feed.getLikes( - { - uri: sc.posts[alice][1].ref.uriStr, - cursor, - limit: 2, - }, - { headers: sc.getHeaders(alice) }, - ) - return res.data - } - - const paginatedAll = await paginateAll(paginator) - paginatedAll.forEach((res) => - expect(res.likes.length).toBeLessThanOrEqual(2), - ) - - const full = await agent.api.app.bsky.feed.getLikes( - { uri: sc.posts[alice][1].ref.uriStr }, - { headers: sc.getHeaders(alice) }, - ) - - expect(full.data.likes.length).toEqual(4) - expect(results(paginatedAll)).toEqual(results([full.data])) - }) -}) diff --git a/packages/pds/tests/views/mute-lists.test.ts b/packages/pds/tests/views/mute-lists.test.ts deleted file mode 100644 index 64692a81b24..00000000000 --- a/packages/pds/tests/views/mute-lists.test.ts +++ /dev/null @@ -1,355 +0,0 @@ -import AtpAgent, { AtUri } from '@atproto/api' -import { runTestServer, CloseFn, TestServerInfo, forSnapshot } from '../_util' -import { SeedClient } from '../seeds/client' -import basicSeed from '../seeds/basic' -import { RecordRef } from '../seeds/client' - -describe('pds views with mutes from mute lists', () => { - let server: TestServerInfo - let agent: AtpAgent - let close: CloseFn - let sc: SeedClient - - let alice: string - let bob: string - let carol: string - let dan: string - - beforeAll(async () => { - server = await runTestServer({ - dbPostgresSchema: 'views_mute_lists', - }) - close = server.close - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await basicSeed(sc) - alice = sc.dids.alice - bob = sc.dids.bob - carol = sc.dids.carol - dan = sc.dids.dan - // add follows to ensure mutes work even w follows - await sc.follow(carol, dan) - await sc.follow(dan, carol) - await server.processAll() - }) - - afterAll(async () => { - await close() - }) - - let listUri: string - let listCid: string - - it('creates a list with some items', async () => { - const avatar = await sc.uploadFile( - alice, - 'tests/image/fixtures/key-portrait-small.jpg', - 'image/jpeg', - ) - // alice creates mute list with bob & carol that dan uses - const list = await agent.api.app.bsky.graph.list.create( - { repo: alice }, - { - name: 'alice mutes', - purpose: 'app.bsky.graph.defs#modlist', - description: 'big list of mutes', - avatar: avatar.image, - createdAt: new Date().toISOString(), - }, - sc.getHeaders(alice), - ) - listUri = list.uri - listCid = list.cid - await agent.api.app.bsky.graph.listitem.create( - { repo: alice }, - { - subject: sc.dids.bob, - list: list.uri, - reason: 'because', - createdAt: new Date().toISOString(), - }, - sc.getHeaders(alice), - ) - await agent.api.app.bsky.graph.listitem.create( - { repo: alice }, - { - subject: sc.dids.carol, - list: list.uri, - reason: 'idk', - createdAt: new Date().toISOString(), - }, - sc.getHeaders(alice), - ) - }) - - it('uses a list for mutes', async () => { - await agent.api.app.bsky.graph.muteActorList( - { - list: listUri, - }, - { encoding: 'application/json', headers: sc.getHeaders(dan) }, - ) - }) - - it('flags mutes in threads', async () => { - const res = await agent.api.app.bsky.feed.getPostThread( - { depth: 1, uri: sc.posts[alice][1].ref.uriStr }, - { headers: sc.getHeaders(dan) }, - ) - expect(forSnapshot(res.data.thread)).toMatchSnapshot() - }) - - it('does not show reposted content from a muted account in author feed', async () => { - await sc.repost(alice, sc.posts[carol][0].ref) - - const res = await agent.api.app.bsky.feed.getAuthorFeed( - { actor: alice }, - { headers: sc.getHeaders(dan) }, - ) - expect( - res.data.feed.some((post) => [bob, carol].includes(post.post.author.did)), - ).toBe(false) - }) - - it('removes content from muted users on getTimeline', async () => { - const res = await agent.api.app.bsky.feed.getTimeline( - { limit: 100 }, - { headers: sc.getHeaders(dan) }, - ) - expect( - res.data.feed.some((post) => [bob, carol].includes(post.post.author.did)), - ).toBe(false) - }) - - it('flags muted users on getPopular', async () => { - for (let i = 0; i < 15; i++) { - const name = `user${i}` - await sc.createAccount(name, { - handle: `user${i}.test`, - email: `user${i}@test.com`, - password: 'password', - }) - await sc.like(sc.dids[name], sc.posts[alice][0].ref) - await sc.like(sc.dids[name], sc.posts[bob][0].ref) - await sc.like(sc.dids[name], sc.posts[carol][0].ref) - await sc.like(sc.dids[name], sc.posts[dan][0].ref) - } - - const res = await agent.api.app.bsky.unspecced.getPopular( - {}, - { headers: sc.getHeaders(dan) }, - ) - expect( - res.data.feed.some((post) => [bob, carol].includes(post.post.author.did)), - ).toBe(false) - }) - - it('returns mute status on getProfile', async () => { - const res = await agent.api.app.bsky.actor.getProfile( - { actor: carol }, - { headers: sc.getHeaders(dan) }, - ) - expect(res.data.viewer?.muted).toBe(true) - expect(res.data.viewer?.mutedByList?.uri).toBe(listUri) - }) - - it('returns mute status on getProfiles', async () => { - const res = await agent.api.app.bsky.actor.getProfiles( - { actors: [alice, carol] }, - { headers: sc.getHeaders(dan) }, - ) - expect(res.data.profiles[0].viewer?.muted).toBe(false) - expect(res.data.profiles[0].viewer?.mutedByList).toBeUndefined() - expect(res.data.profiles[1].viewer?.muted).toBe(true) - expect(res.data.profiles[1].viewer?.mutedByList?.uri).toEqual(listUri) - }) - - it('does not return notifs for muted accounts', async () => { - const res = await agent.api.app.bsky.notification.listNotifications( - { - limit: 100, - }, - { headers: sc.getHeaders(dan) }, - ) - expect( - res.data.notifications.some((notif) => - [bob, carol].includes(notif.author.did), - ), - ).toBeFalsy() - }) - - it('flags muted accounts in in get suggestions', async () => { - // unfollow so they _would_ show up in suggestions if not for mute - await sc.unfollow(dan, carol) - - const res = await agent.api.app.bsky.actor.getSuggestions( - { - limit: 100, - }, - { headers: sc.getHeaders(dan) }, - ) - for (const actor of res.data.actors) { - if ([bob, carol].includes(actor.did)) { - expect(actor.viewer?.muted).toBe(true) - expect(actor.viewer?.mutedByList?.uri).toEqual(listUri) - } else { - expect(actor.viewer?.muted).toBe(false) - expect(actor.viewer?.mutedByList).toBeUndefined() - } - } - }) - - it('returns the contents of a list', async () => { - const res = await agent.api.app.bsky.graph.getList( - { list: listUri }, - { headers: sc.getHeaders(dan) }, - ) - expect(forSnapshot(res.data)).toMatchSnapshot() - }) - - it('paginates getList', async () => { - const full = await agent.api.app.bsky.graph.getList( - { list: listUri }, - { headers: sc.getHeaders(dan) }, - ) - const first = await agent.api.app.bsky.graph.getList( - { list: listUri, limit: 1 }, - { headers: sc.getHeaders(dan) }, - ) - const second = await agent.api.app.bsky.graph.getList( - { list: listUri, cursor: first.data.cursor }, - { headers: sc.getHeaders(dan) }, - ) - const combined = [...first.data.items, ...second.data.items] - expect(combined).toEqual(full.data.items) - }) - - let otherListUri: string - - it('returns lists associated with a user', async () => { - const listRes = await agent.api.app.bsky.graph.list.create( - { repo: alice }, - { - name: 'new list', - purpose: 'app.bsky.graph.defs#modlist', - description: 'blah blah', - createdAt: new Date().toISOString(), - }, - sc.getHeaders(alice), - ) - otherListUri = listRes.uri - - const res = await agent.api.app.bsky.graph.getLists( - { actor: alice }, - { headers: sc.getHeaders(dan) }, - ) - expect(forSnapshot(res.data)).toMatchSnapshot() - }) - - it('paginates getLists', async () => { - const full = await agent.api.app.bsky.graph.getLists( - { actor: alice }, - { headers: sc.getHeaders(dan) }, - ) - const first = await agent.api.app.bsky.graph.getLists( - { actor: alice, limit: 1 }, - { headers: sc.getHeaders(dan) }, - ) - const second = await agent.api.app.bsky.graph.getLists( - { actor: alice, cursor: first.data.cursor }, - { headers: sc.getHeaders(dan) }, - ) - const combined = [...first.data.lists, ...second.data.lists] - expect(combined).toEqual(full.data.lists) - }) - - it('returns a users own list mutes', async () => { - await agent.api.app.bsky.graph.muteActorList( - { - list: otherListUri, - }, - { encoding: 'application/json', headers: sc.getHeaders(dan) }, - ) - - const res = await agent.api.app.bsky.graph.getListMutes( - {}, - { headers: sc.getHeaders(dan) }, - ) - expect(forSnapshot(res.data)).toMatchSnapshot() - }) - - it('paginates getListMutes', async () => { - const full = await agent.api.app.bsky.graph.getListMutes( - {}, - { headers: sc.getHeaders(dan) }, - ) - const first = await agent.api.app.bsky.graph.getListMutes( - { limit: 1 }, - { headers: sc.getHeaders(dan) }, - ) - const second = await agent.api.app.bsky.graph.getListMutes( - { cursor: first.data.cursor }, - { headers: sc.getHeaders(dan) }, - ) - const combined = [...first.data.lists, ...second.data.lists] - expect(combined).toEqual(full.data.lists) - }) - - it('allows unsubscribing from a mute list', async () => { - await agent.api.app.bsky.graph.unmuteActorList( - { - list: otherListUri, - }, - { encoding: 'application/json', headers: sc.getHeaders(dan) }, - ) - - const res = await agent.api.app.bsky.graph.getListMutes( - {}, - { headers: sc.getHeaders(dan) }, - ) - expect(res.data.lists.length).toBe(1) - }) - - it('updates list', async () => { - const uri = new AtUri(listUri) - await agent.api.com.atproto.repo.putRecord( - { - repo: uri.hostname, - collection: uri.collection, - rkey: uri.rkey, - record: { - name: 'updated alice mutes', - purpose: 'app.bsky.graph.defs#modlist', - description: 'new descript', - createdAt: new Date().toISOString(), - }, - }, - { headers: sc.getHeaders(alice), encoding: 'application/json' }, - ) - - const got = await agent.api.app.bsky.graph.getList( - { list: listUri }, - { headers: sc.getHeaders(alice) }, - ) - expect(got.data.list.name).toBe('updated alice mutes') - expect(got.data.list.description).toBe('new descript') - expect(got.data.list.avatar).toBeUndefined() - expect(got.data.items.length).toBe(2) - }) - - it('embeds lists in posts', async () => { - const postRef = await sc.post( - alice, - 'list embed!', - undefined, - undefined, - new RecordRef(listUri, listCid), - ) - const res = await agent.api.app.bsky.feed.getPosts( - { uris: [postRef.ref.uriStr] }, - { headers: sc.getHeaders(alice) }, - ) - expect(res.data.posts.length).toBe(1) - expect(forSnapshot(res.data.posts[0])).toMatchSnapshot() - }) -}) diff --git a/packages/pds/tests/views/mutes.test.ts b/packages/pds/tests/views/mutes.test.ts deleted file mode 100644 index ac32d43ab6f..00000000000 --- a/packages/pds/tests/views/mutes.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -import AtpAgent from '@atproto/api' -import { runTestServer, forSnapshot, CloseFn, paginateAll } from '../_util' -import { SeedClient } from '../seeds/client' -import usersBulkSeed from '../seeds/users-bulk' - -describe('mute views', () => { - let agent: AtpAgent - let close: CloseFn - let sc: SeedClient - let silas: string - - beforeAll(async () => { - const server = await runTestServer({ - dbPostgresSchema: 'views_mutes', - }) - close = server.close - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await usersBulkSeed(sc, 10) - silas = sc.dids['silas77.test'] - const mutes = [ - 'aliya-hodkiewicz.test', - 'adrienne49.test', - 'jeffrey-sawayn87.test', - 'nicolas-krajcik10.test', - 'magnus53.test', - 'elta48.test', - ] - for (const did of mutes) { - await agent.api.app.bsky.graph.muteActor( - { actor: did }, - { headers: sc.getHeaders(silas), encoding: 'application/json' }, - ) - } - await server.processAll() - }) - - afterAll(async () => { - await close() - }) - - it('fetches mutes for the logged-in user.', async () => { - const { data: view } = await agent.api.app.bsky.graph.getMutes( - {}, - { headers: sc.getHeaders(silas) }, - ) - expect(forSnapshot(view.mutes)).toMatchSnapshot() - }) - - it('paginates.', async () => { - const results = (results) => results.flatMap((res) => res.mutes) - const paginator = async (cursor?: string) => { - const { data: view } = await agent.api.app.bsky.graph.getMutes( - { cursor, limit: 2 }, - { headers: sc.getHeaders(silas) }, - ) - return view - } - - const paginatedAll = await paginateAll(paginator) - paginatedAll.forEach((res) => - expect(res.mutes.length).toBeLessThanOrEqual(2), - ) - - const full = await agent.api.app.bsky.graph.getMutes( - {}, - { headers: sc.getHeaders(silas) }, - ) - - expect(full.data.mutes.length).toEqual(6) - expect(results(paginatedAll)).toEqual(results([full.data])) - }) - - it('removes mute.', async () => { - const { data: initial } = await agent.api.app.bsky.graph.getMutes( - {}, - { headers: sc.getHeaders(silas) }, - ) - expect(initial.mutes.length).toEqual(6) - expect(initial.mutes.map((m) => m.handle)).toContain('elta48.test') - - await agent.api.app.bsky.graph.unmuteActor( - { actor: sc.dids['elta48.test'] }, - { headers: sc.getHeaders(silas), encoding: 'application/json' }, - ) - - const { data: final } = await agent.api.app.bsky.graph.getMutes( - {}, - { headers: sc.getHeaders(silas) }, - ) - expect(final.mutes.length).toEqual(5) - expect(final.mutes.map((m) => m.handle)).not.toContain('elta48.test') - - await agent.api.app.bsky.graph.muteActor( - { actor: sc.dids['elta48.test'] }, - { headers: sc.getHeaders(silas), encoding: 'application/json' }, - ) - }) - - it('does not allow muting self.', async () => { - const promise = agent.api.app.bsky.graph.muteActor( - { actor: silas }, - { headers: sc.getHeaders(silas), encoding: 'application/json' }, - ) - await expect(promise).rejects.toThrow('Cannot mute oneself') - }) -}) diff --git a/packages/pds/tests/views/notifications.test.ts b/packages/pds/tests/views/notifications.test.ts deleted file mode 100644 index 1cc86455126..00000000000 --- a/packages/pds/tests/views/notifications.test.ts +++ /dev/null @@ -1,310 +0,0 @@ -import AtpAgent from '@atproto/api' -import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/defs' -import { - runTestServer, - forSnapshot, - CloseFn, - paginateAll, - adminAuth, - TestServerInfo, -} from '../_util' -import { SeedClient } from '../seeds/client' -import basicSeed from '../seeds/basic' -import { Database } from '../../src' -import { Notification } from '../../src/lexicon/types/app/bsky/notification/listNotifications' - -describe('pds notification views', () => { - let server: TestServerInfo - let agent: AtpAgent - let close: CloseFn - let db: Database - let sc: SeedClient - - // account dids, for convenience - let alice: string - - beforeAll(async () => { - server = await runTestServer({ - dbPostgresSchema: 'views_notifications', - }) - close = server.close - db = server.ctx.db - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await basicSeed(sc) - alice = sc.dids.alice - await server.processAll() - }) - - afterAll(async () => { - await close() - }) - - const sort = (notifs: Notification[]) => { - return notifs.sort((a, b) => { - if (a.indexedAt === b.indexedAt) { - return a.uri > b.uri ? -1 : 1 - } - return a.indexedAt > b.indexedAt ? -a : 1 - }) - } - - it('fetches notification count without a last-seen', async () => { - const notifCountAlice = - await agent.api.app.bsky.notification.getUnreadCount( - {}, - { headers: sc.getHeaders(alice) }, - ) - - expect(notifCountAlice.data.count).toBe(12) - - const notifCountBob = await agent.api.app.bsky.notification.getUnreadCount( - {}, - { headers: sc.getHeaders(sc.dids.bob) }, - ) - - expect(notifCountBob.data.count).toBe(4) - }) - - it('generates notifications for all reply ancestors', async () => { - // Add to reply chain, post ancestors: alice -> bob -> alice -> carol. - // Should have added one notification for each of alice and bob. - await sc.reply( - sc.dids.carol, - sc.posts[alice][1].ref, - sc.replies[alice][0].ref, - 'indeed', - ) - await server.processAll() - - const notifCountAlice = - await agent.api.app.bsky.notification.getUnreadCount( - {}, - { headers: sc.getHeaders(alice) }, - ) - - expect(notifCountAlice.data.count).toBe(13) - - const notifCountBob = await agent.api.app.bsky.notification.getUnreadCount( - {}, - { headers: sc.getHeaders(sc.dids.bob) }, - ) - - expect(notifCountBob.data.count).toBe(5) - }) - - it('does not give notifs for a deleted subject', async () => { - const root = await sc.post(sc.dids.alice, 'root') - const first = await sc.reply(sc.dids.bob, root.ref, root.ref, 'first') - await sc.deletePost(sc.dids.alice, root.ref.uri) - const second = await sc.reply(sc.dids.carol, root.ref, first.ref, 'second') - - const notifsAlice = await agent.api.app.bsky.notification.listNotifications( - {}, - { headers: sc.getHeaders(sc.dids.alice) }, - ) - const hasNotif = notifsAlice.data.notifications.some( - (notif) => notif.uri === second.ref.uriStr, - ) - expect(hasNotif).toBe(false) - - // cleanup - await sc.deletePost(sc.dids.bob, first.ref.uri) - await sc.deletePost(sc.dids.carol, second.ref.uri) - }) - - it('generates notifications for quotes', async () => { - // Dan was quoted by alice - const notifsDan = await agent.api.app.bsky.notification.listNotifications( - {}, - { headers: sc.getHeaders(sc.dids.dan) }, - ) - expect(forSnapshot(notifsDan.data)).toMatchSnapshot() - }) - - it('fetches notifications without a last-seen', async () => { - const notifRes = await agent.api.app.bsky.notification.listNotifications( - {}, - { headers: sc.getHeaders(alice) }, - ) - - const notifs = sort(notifRes.data.notifications) - expect(notifs.length).toBe(13) - - const readStates = notifs.map((notif) => notif.isRead) - expect(readStates).toEqual(notifs.map(() => false)) - - // @TODO while the exact order of these is not critically important, - // it's odd to see carol's follow after bob's. In the seed they occur in - // the opposite ordering. - expect(forSnapshot(notifs)).toMatchSnapshot() - }) - - it('fetches notifications omitting records by a muted user', async () => { - await agent.api.app.bsky.graph.muteActor( - { actor: sc.dids.carol }, // Replier - { headers: sc.getHeaders(alice), encoding: 'application/json' }, - ) - await agent.api.app.bsky.graph.muteActor( - { actor: sc.dids.dan }, // Mentioner - { headers: sc.getHeaders(alice), encoding: 'application/json' }, - ) - - const notifRes = await agent.api.app.bsky.notification.listNotifications( - {}, - { headers: sc.getHeaders(alice) }, - ) - const notifCount = await agent.api.app.bsky.notification.getUnreadCount( - {}, - { headers: sc.getHeaders(alice) }, - ) - - const notifs = sort(notifRes.data.notifications) - expect(notifs.length).toBe(4) - expect(forSnapshot(notifs)).toMatchSnapshot() - expect(notifCount.data.count).toBe(4) - - // Cleanup - await agent.api.app.bsky.graph.unmuteActor( - { actor: sc.dids.carol }, - { headers: sc.getHeaders(alice), encoding: 'application/json' }, - ) - await agent.api.app.bsky.graph.unmuteActor( - { actor: sc.dids.dan }, - { headers: sc.getHeaders(alice), encoding: 'application/json' }, - ) - }) - - it('fetches notifications omitting mentions and replies for taken-down posts', async () => { - const postRef1 = sc.replies[sc.dids.carol][0].ref // Reply - const postRef2 = sc.posts[sc.dids.dan][1].ref // Mention - const actionResults = await Promise.all( - [postRef1, postRef2].map((postRef) => - agent.api.com.atproto.admin.takeModerationAction( - { - action: TAKEDOWN, - subject: { - $type: 'com.atproto.repo.strongRef', - uri: postRef.uriStr, - cid: postRef.cidStr, - }, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ), - ), - ) - - const notifRes = await agent.api.app.bsky.notification.listNotifications( - {}, - { headers: sc.getHeaders(alice) }, - ) - const notifCount = await agent.api.app.bsky.notification.getUnreadCount( - {}, - { headers: sc.getHeaders(alice) }, - ) - - const notifs = sort(notifRes.data.notifications) - expect(notifs.length).toBe(11) - expect(forSnapshot(notifs)).toMatchSnapshot() - expect(notifCount.data.count).toBe(11) - - // Cleanup - await Promise.all( - actionResults.map((result) => - agent.api.com.atproto.admin.reverseModerationAction( - { - id: result.data.id, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ), - ), - ) - }) - - it('paginates', async () => { - const results = (results) => - sort(results.flatMap((res) => res.notifications)) - const paginator = async (cursor?: string) => { - const res = await agent.api.app.bsky.notification.listNotifications( - { - cursor, - limit: 6, - }, - { headers: sc.getHeaders(alice) }, - ) - return res.data - } - - const paginatedAll = await paginateAll(paginator) - paginatedAll.forEach((res) => - expect(res.notifications.length).toBeLessThanOrEqual(6), - ) - - const full = await agent.api.app.bsky.notification.listNotifications( - {}, - { - headers: sc.getHeaders(alice), - }, - ) - - expect(full.data.notifications.length).toEqual(13) - expect(results(paginatedAll)).toEqual(results([full.data])) - }) - - it('updates notifications last seen', async () => { - const full = await agent.api.app.bsky.notification.listNotifications( - {}, - { - headers: sc.getHeaders(alice), - }, - ) - - // Need to look-up createdAt time as a cursor since it's not in the method's output - const beforeNotif = await db.db - .selectFrom('user_notification') - .selectAll() - .where('recordUri', '=', full.data.notifications[3].uri) - .executeTakeFirstOrThrow() - - await agent.api.app.bsky.notification.updateSeen( - { seenAt: beforeNotif.indexedAt }, - { encoding: 'application/json', headers: sc.getHeaders(alice) }, - ) - }) - - it('fetches notification count with a last-seen', async () => { - const notifCount = await agent.api.app.bsky.notification.getUnreadCount( - {}, - { headers: sc.getHeaders(alice) }, - ) - - expect(notifCount.data.count).toBe(3) - }) - - it('fetches notifications with a last-seen', async () => { - const notifRes = await agent.api.app.bsky.notification.listNotifications( - {}, - { - headers: sc.getHeaders(alice), - }, - ) - - const notifs = sort(notifRes.data.notifications) - expect(notifs.length).toBe(13) - - const readStates = notifs.map((notif) => notif.isRead) - expect(readStates).toEqual(notifs.map((_, i) => i >= 3)) - - expect(forSnapshot(notifs)).toMatchSnapshot() - }) -}) diff --git a/packages/pds/tests/views/popular.test.ts b/packages/pds/tests/views/popular.test.ts deleted file mode 100644 index ca21ee31f4f..00000000000 --- a/packages/pds/tests/views/popular.test.ts +++ /dev/null @@ -1,110 +0,0 @@ -import AtpAgent from '@atproto/api' -import { runTestServer, CloseFn, TestServerInfo } from '../_util' -import { SeedClient } from '../seeds/client' -import basicSeed from '../seeds/basic' - -describe('popular views', () => { - let server: TestServerInfo - let agent: AtpAgent - let close: CloseFn - let sc: SeedClient - - // account dids, for convenience - let alice: string - let bob: string - - const account = { - email: 'blah@test.com', - password: 'blh-pass', - } - - beforeAll(async () => { - server = await runTestServer({ - dbPostgresSchema: 'views_popular', - }) - close = server.close - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await basicSeed(sc) - - await sc.createAccount('eve', { - ...account, - email: 'eve@test.com', - handle: 'eve.test', - password: 'eve-pass', - }) - await sc.createAccount('frank', { - ...account, - email: 'frank@test.com', - handle: 'frank.test', - password: 'frank-pass', - }) - await sc.createAccount('george', { - ...account, - email: 'george@test.com', - handle: 'george.test', - password: 'george-pass', - }) - await sc.createAccount('helen', { - ...account, - email: 'helen@test.com', - handle: 'helen.test', - password: 'helen-pass', - }) - - alice = sc.dids.alice - bob = sc.dids.bob - await server.processAll() - }) - - afterAll(async () => { - await close() - }) - - it('returns well liked posts', async () => { - const img = await sc.uploadFile( - alice, - 'tests/image/fixtures/key-landscape-small.jpg', - 'image/jpeg', - ) - const one = await sc.post(alice, 'first post', undefined, [img]) - const two = await sc.post(bob, 'bobby boi') - const three = await sc.reply(bob, one.ref, one.ref, 'reply') - - for (let i = 0; i < 12; i++) { - const name = `user${i}` - await sc.createAccount(name, { - handle: `user${i}.test`, - email: `user${i}@test.com`, - password: 'password', - }) - await sc.like(sc.dids[name], one.ref) - await sc.like(sc.dids[name], two.ref) - await sc.like(sc.dids[name], three.ref) - } - await server.processAll() - - const res = await agent.api.app.bsky.unspecced.getPopular( - {}, - { headers: sc.getHeaders(alice) }, - ) - const feedUris = res.data.feed.map((i) => i.post.uri).sort() - const expected = [one.ref.uriStr, two.ref.uriStr].sort() - expect(feedUris).toEqual(expected) - }) - - it('does not return muted posts', async () => { - await agent.api.app.bsky.graph.muteActor( - { actor: bob }, - { headers: sc.getHeaders(alice), encoding: 'application/json' }, - ) - - const res = await agent.api.app.bsky.unspecced.getPopular( - {}, - { headers: sc.getHeaders(alice) }, - ) - expect(res.data.feed.length).toBe(1) - const dids = res.data.feed.map((post) => post.post.author.did) - expect(dids.includes(bob)).toBe(false) - }) -}) diff --git a/packages/pds/tests/views/posts.test.ts b/packages/pds/tests/views/posts.test.ts deleted file mode 100644 index f6aa0551b07..00000000000 --- a/packages/pds/tests/views/posts.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import AtpAgent from '@atproto/api' -import { runTestServer, forSnapshot, TestServerInfo } from '../_util' -import { SeedClient } from '../seeds/client' -import basicSeed from '../seeds/basic' - -describe('pds posts views', () => { - let server: TestServerInfo - let agent: AtpAgent - let sc: SeedClient - - beforeAll(async () => { - server = await runTestServer({ - dbPostgresSchema: 'views_posts', - }) - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await basicSeed(sc) - await server.processAll() - }) - - afterAll(async () => { - await server.close() - }) - - it('fetches posts', async () => { - const uris = [ - sc.posts[sc.dids.alice][0].ref.uriStr, - sc.posts[sc.dids.alice][1].ref.uriStr, - sc.posts[sc.dids.bob][0].ref.uriStr, - sc.posts[sc.dids.carol][0].ref.uriStr, - sc.posts[sc.dids.dan][1].ref.uriStr, - sc.replies[sc.dids.alice][0].ref.uriStr, - ] - const posts = await agent.api.app.bsky.feed.getPosts( - { uris }, - { headers: sc.getHeaders(sc.dids.alice) }, - ) - - expect(posts.data.posts.length).toBe(uris.length) - expect(forSnapshot(posts.data.posts)).toMatchSnapshot() - }) - - it('handles repeat uris', async () => { - const uris = [ - sc.posts[sc.dids.alice][0].ref.uriStr, - sc.posts[sc.dids.alice][0].ref.uriStr, - sc.posts[sc.dids.bob][0].ref.uriStr, - sc.posts[sc.dids.alice][0].ref.uriStr, - sc.posts[sc.dids.bob][0].ref.uriStr, - ] - - const posts = await agent.api.app.bsky.feed.getPosts( - { uris }, - { headers: sc.getHeaders(sc.dids.alice) }, - ) - - expect(posts.data.posts.length).toBe(2) - const receivedUris = posts.data.posts.map((p) => p.uri).sort() - const expected = [ - sc.posts[sc.dids.alice][0].ref.uriStr, - sc.posts[sc.dids.bob][0].ref.uriStr, - ].sort() - expect(receivedUris).toEqual(expected) - }) -}) diff --git a/packages/pds/tests/views/profile.test.ts b/packages/pds/tests/views/profile.test.ts deleted file mode 100644 index b1a3e5003a9..00000000000 --- a/packages/pds/tests/views/profile.test.ts +++ /dev/null @@ -1,285 +0,0 @@ -import fs from 'fs/promises' -import AtpAgent from '@atproto/api' -import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/defs' -import { ids } from '../../src/lexicon/lexicons' -import { runTestServer, forSnapshot, CloseFn, adminAuth } from '../_util' -import { SeedClient } from '../seeds/client' -import basicSeed from '../seeds/basic' - -describe('pds profile views', () => { - let agent: AtpAgent - let close: CloseFn - let sc: SeedClient - - // account dids, for convenience - let alice: string - let bob: string - let dan: string - - beforeAll(async () => { - const server = await runTestServer({ - dbPostgresSchema: 'views_profile', - }) - close = server.close - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await basicSeed(sc) - alice = sc.dids.alice - bob = sc.dids.bob - dan = sc.dids.dan - await server.processAll() - }) - - afterAll(async () => { - await close() - }) - - it('fetches own profile', async () => { - const aliceForAlice = await agent.api.app.bsky.actor.getProfile( - { actor: alice }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(aliceForAlice.data)).toMatchSnapshot() - }) - - it('reflects self-labels', async () => { - const aliceForBob = await agent.api.app.bsky.actor.getProfile( - { actor: alice }, - { headers: sc.getHeaders(bob) }, - ) - - const labels = aliceForBob.data.labels - ?.filter((label) => label.src === alice) - .map((label) => label.val) - .sort() - - expect(labels).toEqual(['self-label-a', 'self-label-b']) - }) - - it("fetches other's profile, with a follow", async () => { - const aliceForBob = await agent.api.app.bsky.actor.getProfile( - { actor: alice }, - { headers: sc.getHeaders(bob) }, - ) - - expect(forSnapshot(aliceForBob.data)).toMatchSnapshot() - }) - - it("fetches other's profile, without a follow", async () => { - const danForBob = await agent.api.app.bsky.actor.getProfile( - { actor: dan }, - { headers: sc.getHeaders(bob) }, - ) - - expect(forSnapshot(danForBob.data)).toMatchSnapshot() - }) - - it('fetches multiple profiles', async () => { - const { - data: { profiles }, - } = await agent.api.app.bsky.actor.getProfiles( - { - actors: [ - alice, - 'bob.test', - 'did:example:missing', - 'carol.test', - dan, - 'missing.test', - ], - }, - { headers: sc.getHeaders(bob) }, - ) - - expect(profiles.map((p) => p.handle)).toEqual([ - 'alice.test', - 'bob.test', - 'carol.test', - 'dan.test', - ]) - - expect(forSnapshot(profiles)).toMatchSnapshot() - }) - - it('updates profile', async () => { - await updateProfile(agent, alice, { - displayName: 'ali', - description: 'new descript', - avatar: sc.profiles[alice].avatar, - }) - - const aliceForAlice = await agent.api.app.bsky.actor.getProfile( - { actor: alice }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(aliceForAlice.data)).toMatchSnapshot() - }) - - it('handles avatars & banners', async () => { - const avatarImg = await fs.readFile( - 'tests/image/fixtures/key-portrait-small.jpg', - ) - const bannerImg = await fs.readFile( - 'tests/image/fixtures/key-landscape-small.jpg', - ) - const avatarRes = await agent.api.com.atproto.repo.uploadBlob(avatarImg, { - headers: sc.getHeaders(alice), - encoding: 'image/jpeg', - }) - const bannerRes = await agent.api.com.atproto.repo.uploadBlob(bannerImg, { - headers: sc.getHeaders(alice), - encoding: 'image/jpeg', - }) - - await updateProfile(agent, alice, { - displayName: 'ali', - description: 'new descript', - avatar: avatarRes.data.blob, - banner: bannerRes.data.blob, - }) - - const aliceForAlice = await agent.api.app.bsky.actor.getProfile( - { actor: alice }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(aliceForAlice.data)).toMatchSnapshot() - }) - - it('handles unsetting profile fields', async () => { - await updateProfile(agent, alice, {}) - - const aliceForAlice = await agent.api.app.bsky.actor.getProfile( - { actor: alice }, - { headers: sc.getHeaders(alice) }, - ) - - expect(aliceForAlice.data.displayName).toBeUndefined() - expect(aliceForAlice.data.description).toBeUndefined() - expect(aliceForAlice.data.avatar).toBeUndefined() - expect(aliceForAlice.data.banner).toBeUndefined() - expect(forSnapshot(aliceForAlice.data)).toMatchSnapshot() - }) - - it('creates new profile', async () => { - await updateProfile(agent, dan, { displayName: 'danny boy' }) - - const danForDan = await agent.api.app.bsky.actor.getProfile( - { actor: dan }, - { headers: sc.getHeaders(dan) }, - ) - - expect(forSnapshot(danForDan.data)).toMatchSnapshot() - }) - - it('fetches profile by handle', async () => { - const byDid = await agent.api.app.bsky.actor.getProfile( - { actor: alice }, - { - headers: sc.getHeaders(bob), - }, - ) - - const byHandle = await agent.api.app.bsky.actor.getProfile( - { actor: sc.accounts[alice].handle }, - { headers: sc.getHeaders(bob) }, - ) - - expect(byHandle.data).toEqual(byDid.data) - }) - - it('blocked by actor takedown', async () => { - const { data: action } = - await agent.api.com.atproto.admin.takeModerationAction( - { - action: TAKEDOWN, - subject: { - $type: 'com.atproto.admin.defs#repoRef', - did: alice, - }, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - const promise = agent.api.app.bsky.actor.getProfile( - { actor: alice }, - { headers: sc.getHeaders(bob) }, - ) - - await expect(promise).rejects.toThrow('Account has been taken down') - - // Cleanup - await agent.api.com.atproto.admin.reverseModerationAction( - { - id: action.id, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - }) - - it('includes muted status.', async () => { - const { data: initial } = await agent.api.app.bsky.actor.getProfile( - { actor: alice }, - { headers: sc.getHeaders(bob) }, - ) - - expect(initial.viewer?.muted).toEqual(false) - - await agent.api.app.bsky.graph.muteActor( - { actor: alice }, - { headers: sc.getHeaders(bob), encoding: 'application/json' }, - ) - const { data: muted } = await agent.api.app.bsky.actor.getProfile( - { actor: alice }, - { headers: sc.getHeaders(bob) }, - ) - - expect(muted.viewer?.muted).toEqual(true) - - const { data: fromBobUnrelated } = - await agent.api.app.bsky.actor.getProfile( - { actor: dan }, - { headers: sc.getHeaders(bob) }, - ) - const { data: toAliceUnrelated } = - await agent.api.app.bsky.actor.getProfile( - { actor: alice }, - { headers: sc.getHeaders(dan) }, - ) - - expect(fromBobUnrelated.viewer?.muted).toEqual(false) - expect(toAliceUnrelated.viewer?.muted).toEqual(false) - - await agent.api.app.bsky.graph.unmuteActor( - { actor: alice }, - { headers: sc.getHeaders(bob), encoding: 'application/json' }, - ) - }) - - async function updateProfile( - agent: AtpAgent, - did: string, - record: Record, - ) { - return await agent.api.com.atproto.repo.putRecord( - { - repo: did, - collection: ids.AppBskyActorProfile, - rkey: 'self', - record, - }, - { headers: sc.getHeaders(did), encoding: 'application/json' }, - ) - } -}) diff --git a/packages/pds/tests/views/reposts.test.ts b/packages/pds/tests/views/reposts.test.ts deleted file mode 100644 index 1c61215e3e5..00000000000 --- a/packages/pds/tests/views/reposts.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import AtpAgent from '@atproto/api' -import { runTestServer, forSnapshot, CloseFn, paginateAll } from '../_util' -import { SeedClient } from '../seeds/client' -import repostsSeed from '../seeds/reposts' - -describe('pds repost views', () => { - let agent: AtpAgent - let close: CloseFn - let sc: SeedClient - - // account dids, for convenience - let alice: string - let bob: string - - beforeAll(async () => { - const server = await runTestServer({ - dbPostgresSchema: 'views_reposts', - }) - close = server.close - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await repostsSeed(sc) - alice = sc.dids.alice - bob = sc.dids.bob - await server.processAll() - }) - - afterAll(async () => { - await close() - }) - - it('fetches reposted-by for a post', async () => { - const view = await agent.api.app.bsky.feed.getRepostedBy( - { uri: sc.posts[alice][2].ref.uriStr }, - { headers: sc.getHeaders(alice) }, - ) - expect(view.data.uri).toEqual(sc.posts[sc.dids.alice][2].ref.uriStr) - expect(forSnapshot(view.data.repostedBy)).toMatchSnapshot() - }) - - it('fetches reposted-by for a reply', async () => { - const view = await agent.api.app.bsky.feed.getRepostedBy( - { uri: sc.replies[bob][0].ref.uriStr }, - { headers: sc.getHeaders(alice) }, - ) - expect(view.data.uri).toEqual(sc.replies[sc.dids.bob][0].ref.uriStr) - expect(forSnapshot(view.data.repostedBy)).toMatchSnapshot() - }) - - it('paginates', async () => { - const results = (results) => results.flatMap((res) => res.repostedBy) - const paginator = async (cursor?: string) => { - const res = await agent.api.app.bsky.feed.getRepostedBy( - { - uri: sc.posts[alice][2].ref.uriStr, - cursor, - limit: 2, - }, - { headers: sc.getHeaders(alice) }, - ) - return res.data - } - - const paginatedAll = await paginateAll(paginator) - paginatedAll.forEach((res) => - expect(res.repostedBy.length).toBeLessThanOrEqual(2), - ) - - const full = await agent.api.app.bsky.feed.getRepostedBy( - { uri: sc.posts[alice][2].ref.uriStr }, - { headers: sc.getHeaders(alice) }, - ) - - expect(full.data.repostedBy.length).toEqual(4) - expect(results(paginatedAll)).toEqual(results([full.data])) - }) -}) diff --git a/packages/pds/tests/views/suggestions.test.ts b/packages/pds/tests/views/suggestions.test.ts deleted file mode 100644 index 4bec6dc3dd2..00000000000 --- a/packages/pds/tests/views/suggestions.test.ts +++ /dev/null @@ -1,75 +0,0 @@ -import AtpAgent from '@atproto/api' -import { runTestServer, CloseFn } from '../_util' -import { SeedClient } from '../seeds/client' -import basicSeed from '../seeds/basic' - -describe('pds user search views', () => { - let agent: AtpAgent - let close: CloseFn - let sc: SeedClient - - beforeAll(async () => { - const server = await runTestServer({ - dbPostgresSchema: 'views_suggestions', - }) - close = server.close - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await basicSeed(sc) - await server.processAll() - - const suggestions = [ - { did: sc.dids.bob, order: 1 }, - { did: sc.dids.carol, order: 2 }, - { did: sc.dids.dan, order: 3 }, - ] - await server.ctx.db.db - .insertInto('suggested_follow') - .values(suggestions) - .execute() - }) - - afterAll(async () => { - await close() - }) - - it('actor suggestion gives users', async () => { - const result = await agent.api.app.bsky.actor.getSuggestions( - {}, - { headers: sc.getHeaders(sc.dids.carol) }, - ) - - // does not include carol, because she is requesting - expect(result.data.actors.length).toBe(2) - expect(result.data.actors[0].handle).toEqual('bob.test') - expect(result.data.actors[0].displayName).toEqual('bobby') - expect(result.data.actors[1].handle).toEqual('dan.test') - expect(result.data.actors[1].displayName).toBeUndefined() - }) - - it('does not suggest followed users', async () => { - const result = await agent.api.app.bsky.actor.getSuggestions( - {}, - { headers: sc.getHeaders(sc.dids.alice) }, - ) - - // alice follows everyone - expect(result.data.actors.length).toBe(0) - }) - - it('paginates', async () => { - const result1 = await agent.api.app.bsky.actor.getSuggestions( - { limit: 1 }, - { headers: sc.getHeaders(sc.dids.carol) }, - ) - const result2 = await agent.api.app.bsky.actor.getSuggestions( - { limit: 1, cursor: result1.data.cursor }, - { headers: sc.getHeaders(sc.dids.carol) }, - ) - - expect(result1.data.actors.length).toBe(1) - expect(result1.data.actors[0].handle).toEqual('bob.test') - expect(result2.data.actors.length).toBe(1) - expect(result2.data.actors[0].handle).toEqual('dan.test') - }) -}) diff --git a/packages/pds/tests/views/thread.test.ts b/packages/pds/tests/views/thread.test.ts deleted file mode 100644 index 1efee8b0526..00000000000 --- a/packages/pds/tests/views/thread.test.ts +++ /dev/null @@ -1,456 +0,0 @@ -import assert from 'assert' -import AtpAgent, { AppBskyFeedGetPostThread } from '@atproto/api' -import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/defs' -import { isThreadViewPost } from '@atproto/api/src/client/types/app/bsky/feed/defs' -import { - runTestServer, - forSnapshot, - CloseFn, - adminAuth, - TestServerInfo, -} from '../_util' -import { SeedClient } from '../seeds/client' -import basicSeed from '../seeds/basic' - -describe('pds thread views', () => { - let server: TestServerInfo - let agent: AtpAgent - let close: CloseFn - let sc: SeedClient - - // account dids, for convenience - let alice: string - let bob: string - let carol: string - - beforeAll(async () => { - server = await runTestServer({ - dbPostgresSchema: 'views_thread', - }) - close = server.close - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await basicSeed(sc) - await server.processAll() - alice = sc.dids.alice - bob = sc.dids.bob - carol = sc.dids.carol - }) - - beforeAll(async () => { - // Add a repost of a reply so that we can confirm viewer state in the thread - await sc.repost(bob, sc.replies[alice][0].ref) - await server.processAll() - }) - - afterAll(async () => { - await close() - }) - - it('fetches deep post thread', async () => { - const thread = await agent.api.app.bsky.feed.getPostThread( - { uri: sc.posts[alice][1].ref.uriStr }, - { headers: sc.getHeaders(bob) }, - ) - - expect(forSnapshot(thread.data.thread)).toMatchSnapshot() - }) - - it('fetches shallow post thread', async () => { - const thread = await agent.api.app.bsky.feed.getPostThread( - { depth: 1, uri: sc.posts[alice][1].ref.uriStr }, - { headers: sc.getHeaders(bob) }, - ) - - expect(forSnapshot(thread.data.thread)).toMatchSnapshot() - }) - - it('fetches ancestors', async () => { - const thread = await agent.api.app.bsky.feed.getPostThread( - { depth: 1, uri: sc.replies[alice][0].ref.uriStr }, - { headers: sc.getHeaders(bob) }, - ) - - expect(forSnapshot(thread.data.thread)).toMatchSnapshot() - }) - - it('fetches shallow ancestors', async () => { - const thread = await agent.api.app.bsky.feed.getPostThread( - { depth: 1, parentHeight: 1, uri: sc.replies[alice][0].ref.uriStr }, - { headers: sc.getHeaders(bob) }, - ) - - expect(forSnapshot(thread.data.thread)).toMatchSnapshot() - }) - - it('fails for an unknown post', async () => { - const promise = agent.api.app.bsky.feed.getPostThread( - { uri: 'at://did:example:fake/does.not.exist/self' }, - { headers: sc.getHeaders(bob) }, - ) - - await expect(promise).rejects.toThrow( - AppBskyFeedGetPostThread.NotFoundError, - ) - }) - - it('includes the muted status of post authors.', async () => { - await agent.api.app.bsky.graph.muteActor( - { actor: alice }, - { headers: sc.getHeaders(bob), encoding: 'application/json' }, - ) - const thread = await agent.api.app.bsky.feed.getPostThread( - { uri: sc.posts[alice][1].ref.uriStr }, - { headers: sc.getHeaders(bob) }, - ) - - expect(forSnapshot(thread.data.thread)).toMatchSnapshot() - - await agent.api.app.bsky.graph.unmuteActor( - { actor: alice }, - { encoding: 'application/json', headers: sc.getHeaders(bob) }, - ) - }) - - it('handles deleted posts correctly', async () => { - const alice = sc.dids.alice - const bob = sc.dids.bob - - const indexes = { - aliceRoot: -1, - bobReply: -1, - aliceReplyReply: -1, - } - - await sc.post(alice, 'Deletion thread') - indexes.aliceRoot = sc.posts[alice].length - 1 - - await sc.reply( - bob, - sc.posts[alice][indexes.aliceRoot].ref, - sc.posts[alice][indexes.aliceRoot].ref, - 'Reply', - ) - indexes.bobReply = sc.replies[bob].length - 1 - await sc.reply( - alice, - sc.posts[alice][indexes.aliceRoot].ref, - sc.replies[bob][indexes.bobReply].ref, - 'Reply reply', - ) - indexes.aliceReplyReply = sc.replies[alice].length - 1 - await server.processAll() - - const thread1 = await agent.api.app.bsky.feed.getPostThread( - { uri: sc.posts[alice][indexes.aliceRoot].ref.uriStr }, - { headers: sc.getHeaders(bob) }, - ) - expect(forSnapshot(thread1.data.thread)).toMatchSnapshot() - - await sc.deletePost(bob, sc.replies[bob][indexes.bobReply].ref.uri) - await server.processAll() - - const thread2 = await agent.api.app.bsky.feed.getPostThread( - { uri: sc.posts[alice][indexes.aliceRoot].ref.uriStr }, - { headers: sc.getHeaders(bob) }, - ) - expect(forSnapshot(thread2.data.thread)).toMatchSnapshot() - - const thread3 = await agent.api.app.bsky.feed.getPostThread( - { uri: sc.replies[alice][indexes.aliceReplyReply].ref.uriStr }, - { headers: sc.getHeaders(bob) }, - ) - expect(forSnapshot(thread3.data.thread)).toMatchSnapshot() - }) - - it('reflects self-labels', async () => { - const { data: thread } = await agent.api.app.bsky.feed.getPostThread( - { uri: sc.posts[alice][0].ref.uriStr }, - { headers: sc.getHeaders(bob) }, - ) - - assert(isThreadViewPost(thread.thread), 'post does not exist') - const post = thread.thread.post - - const postSelfLabels = post.labels - ?.filter((label) => label.src === alice) - .map((label) => label.val) - - expect(postSelfLabels).toEqual(['self-label']) - - const authorSelfLabels = post.author.labels - ?.filter((label) => label.src === alice) - .map((label) => label.val) - .sort() - - expect(authorSelfLabels).toEqual(['self-label-a', 'self-label-b']) - }) - - it('blocks post by actor takedown', async () => { - const { data: modAction } = - await agent.api.com.atproto.admin.takeModerationAction( - { - action: TAKEDOWN, - subject: { - $type: 'com.atproto.admin.defs#repoRef', - did: alice, - }, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - - // Same as shallow post thread test, minus alice - const promise = agent.api.app.bsky.feed.getPostThread( - { depth: 1, uri: sc.posts[alice][1].ref.uriStr }, - { headers: sc.getHeaders(bob) }, - ) - - await expect(promise).rejects.toThrow( - AppBskyFeedGetPostThread.NotFoundError, - ) - - // Cleanup - await agent.api.com.atproto.admin.reverseModerationAction( - { - id: modAction.id, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - }) - - it('blocks replies by actor takedown', async () => { - const { data: modAction } = - await agent.api.com.atproto.admin.takeModerationAction( - { - action: TAKEDOWN, - subject: { - $type: 'com.atproto.admin.defs#repoRef', - did: carol, - }, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - - // Same as deep post thread test, minus carol - const thread = await agent.api.app.bsky.feed.getPostThread( - { uri: sc.posts[alice][1].ref.uriStr }, - { headers: sc.getHeaders(bob) }, - ) - - expect(forSnapshot(thread.data.thread)).toMatchSnapshot() - - // Cleanup - await agent.api.com.atproto.admin.reverseModerationAction( - { - id: modAction.id, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - }) - - it('blocks ancestors by actor takedown', async () => { - const { data: modAction } = - await agent.api.com.atproto.admin.takeModerationAction( - { - action: TAKEDOWN, - subject: { - $type: 'com.atproto.admin.defs#repoRef', - did: bob, - }, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - - // Same as ancestor post thread test, minus bob - const thread = await agent.api.app.bsky.feed.getPostThread( - { depth: 1, uri: sc.replies[alice][0].ref.uriStr }, - { headers: sc.getHeaders(bob) }, - ) - - expect(forSnapshot(thread.data.thread)).toMatchSnapshot() - - // Cleanup - await agent.api.com.atproto.admin.reverseModerationAction( - { - id: modAction.id, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - }) - - it('blocks post by record takedown', async () => { - const postRef = sc.posts[alice][1].ref - const { data: modAction } = - await agent.api.com.atproto.admin.takeModerationAction( - { - action: TAKEDOWN, - subject: { - $type: 'com.atproto.repo.strongRef', - uri: postRef.uriStr, - cid: postRef.cidStr, - }, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - - const promise = agent.api.app.bsky.feed.getPostThread( - { depth: 1, uri: postRef.uriStr }, - { headers: sc.getHeaders(bob) }, - ) - - await expect(promise).rejects.toThrow( - AppBskyFeedGetPostThread.NotFoundError, - ) - - // Cleanup - await agent.api.com.atproto.admin.reverseModerationAction( - { - id: modAction.id, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - }) - - it('blocks ancestors by record takedown', async () => { - const threadPreTakedown = await agent.api.app.bsky.feed.getPostThread( - { depth: 1, uri: sc.replies[alice][0].ref.uriStr }, - { headers: sc.getHeaders(bob) }, - ) - - const parent = threadPreTakedown.data.thread.parent?.['post'] - - const { data: modAction } = - await agent.api.com.atproto.admin.takeModerationAction( - { - action: TAKEDOWN, - subject: { - $type: 'com.atproto.repo.strongRef', - uri: parent.uri, - cid: parent.cid, - }, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - - // Same as ancestor post thread test, minus parent post - const thread = await agent.api.app.bsky.feed.getPostThread( - { depth: 1, uri: sc.replies[alice][0].ref.uriStr }, - { headers: sc.getHeaders(bob) }, - ) - - expect(forSnapshot(thread.data.thread)).toMatchSnapshot() - - // Cleanup - await agent.api.com.atproto.admin.reverseModerationAction( - { - id: modAction.id, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - }) - - it('blocks replies by record takedown', async () => { - const threadPreTakedown = await agent.api.app.bsky.feed.getPostThread( - { uri: sc.posts[alice][1].ref.uriStr }, - { headers: sc.getHeaders(bob) }, - ) - const post1 = threadPreTakedown.data.thread.replies?.[0].post - const post2 = threadPreTakedown.data.thread.replies?.[1].replies[0].post - - const actionResults = await Promise.all( - [post1, post2].map((post) => - agent.api.com.atproto.admin.takeModerationAction( - { - action: TAKEDOWN, - subject: { - $type: 'com.atproto.repo.strongRef', - uri: post.uri, - cid: post.cid, - }, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ), - ), - ) - - // Same as deep post thread test, minus some replies - const thread = await agent.api.app.bsky.feed.getPostThread( - { uri: sc.posts[alice][1].ref.uriStr }, - { headers: sc.getHeaders(bob) }, - ) - - expect(forSnapshot(thread.data.thread)).toMatchSnapshot() - - // Cleanup - await Promise.all( - actionResults.map((result) => - agent.api.com.atproto.admin.reverseModerationAction( - { - id: result.data.id, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ), - ), - ) - }) -}) diff --git a/packages/pds/tests/views/timeline.test.ts b/packages/pds/tests/views/timeline.test.ts deleted file mode 100644 index 5dd9ba4a893..00000000000 --- a/packages/pds/tests/views/timeline.test.ts +++ /dev/null @@ -1,310 +0,0 @@ -import assert from 'assert' -import AtpAgent from '@atproto/api' -import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/defs' -import { FeedViewPost } from '../../src/lexicon/types/app/bsky/feed/defs' -import { - runTestServer, - forSnapshot, - CloseFn, - getOriginator, - paginateAll, - adminAuth, -} from '../_util' -import { SeedClient } from '../seeds/client' -import basicSeed from '../seeds/basic' -import { FeedAlgorithm } from '../../src/app-view/api/app/bsky/util/feed' - -describe('timeline views', () => { - let agent: AtpAgent - let close: CloseFn - let sc: SeedClient - - // account dids, for convenience - let alice: string - let bob: string - let carol: string - let dan: string - - beforeAll(async () => { - const server = await runTestServer({ - dbPostgresSchema: 'views_home_feed', - }) - close = server.close - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await basicSeed(sc) - alice = sc.dids.alice - bob = sc.dids.bob - carol = sc.dids.carol - dan = sc.dids.dan - // Label posts as "kind" to check labels on embed views - const labelPostA = sc.posts[bob][0].ref - const labelPostB = sc.posts[carol][0].ref - await server.ctx.services.appView - .label(server.ctx.db) - .formatAndCreate( - server.ctx.cfg.labelerDid, - labelPostA.uriStr, - labelPostA.cidStr, - { create: ['kind'] }, - ) - await server.ctx.services.appView - .label(server.ctx.db) - .formatAndCreate( - server.ctx.cfg.labelerDid, - labelPostB.uriStr, - labelPostB.cidStr, - { create: ['kind'] }, - ) - await server.processAll() - }) - - afterAll(async () => { - await close() - }) - - it("fetches authenticated user's home feed w/ reverse-chronological algorithm", async () => { - const expectOriginatorFollowedBy = (did) => (item: FeedViewPost) => { - const originator = getOriginator(item) - // The user expects to see posts & reposts from themselves and follows - if (did !== originator) { - expect(sc.follows[did]).toHaveProperty(originator) - } - } - - const aliceTL = await agent.api.app.bsky.feed.getTimeline( - { algorithm: FeedAlgorithm.ReverseChronological }, - { - headers: sc.getHeaders(alice), - }, - ) - - expect(forSnapshot(aliceTL.data.feed)).toMatchSnapshot() - aliceTL.data.feed.forEach(expectOriginatorFollowedBy(alice)) - - const bobTL = await agent.api.app.bsky.feed.getTimeline( - { algorithm: FeedAlgorithm.ReverseChronological }, - { - headers: sc.getHeaders(bob), - }, - ) - - expect(forSnapshot(bobTL.data.feed)).toMatchSnapshot() - bobTL.data.feed.forEach(expectOriginatorFollowedBy(bob)) - - const carolTL = await agent.api.app.bsky.feed.getTimeline( - { algorithm: FeedAlgorithm.ReverseChronological }, - { - headers: sc.getHeaders(carol), - }, - ) - - expect(forSnapshot(carolTL.data.feed)).toMatchSnapshot() - carolTL.data.feed.forEach(expectOriginatorFollowedBy(carol)) - - const danTL = await agent.api.app.bsky.feed.getTimeline( - { algorithm: FeedAlgorithm.ReverseChronological }, - { - headers: sc.getHeaders(dan), - }, - ) - - expect(forSnapshot(danTL.data.feed)).toMatchSnapshot() - danTL.data.feed.forEach(expectOriginatorFollowedBy(dan)) - }) - - it("fetches authenticated user's home feed w/ default algorithm", async () => { - const defaultTL = await agent.api.app.bsky.feed.getTimeline( - {}, - { - headers: sc.getHeaders(alice), - }, - ) - const reverseChronologicalTL = await agent.api.app.bsky.feed.getTimeline( - { algorithm: FeedAlgorithm.ReverseChronological }, - { - headers: sc.getHeaders(alice), - }, - ) - expect(defaultTL.data.feed).toEqual(reverseChronologicalTL.data.feed) - }) - - it('omits posts and reposts of muted authors.', async () => { - await agent.api.app.bsky.graph.muteActor( - { actor: bob }, - { headers: sc.getHeaders(alice), encoding: 'application/json' }, - ) - await agent.api.app.bsky.graph.muteActor( - { actor: carol }, - { headers: sc.getHeaders(alice), encoding: 'application/json' }, - ) - - const aliceTL = await agent.api.app.bsky.feed.getTimeline( - { algorithm: FeedAlgorithm.ReverseChronological }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(aliceTL.data.feed)).toMatchSnapshot() - - // Cleanup - await agent.api.app.bsky.graph.unmuteActor( - { actor: bob }, - { encoding: 'application/json', headers: sc.getHeaders(alice) }, - ) - await agent.api.app.bsky.graph.unmuteActor( - { actor: carol }, - { encoding: 'application/json', headers: sc.getHeaders(alice) }, - ) - }) - - it('paginates reverse-chronological feed', async () => { - const results = (results) => results.flatMap((res) => res.feed) - const paginator = async (cursor?: string) => { - const res = await agent.api.app.bsky.feed.getTimeline( - { - algorithm: FeedAlgorithm.ReverseChronological, - cursor, - limit: 4, - }, - { headers: sc.getHeaders(carol) }, - ) - return res.data - } - - const paginatedAll = await paginateAll(paginator) - paginatedAll.forEach((res) => - expect(res.feed.length).toBeLessThanOrEqual(4), - ) - - const full = await agent.api.app.bsky.feed.getTimeline( - { - algorithm: FeedAlgorithm.ReverseChronological, - }, - { headers: sc.getHeaders(carol) }, - ) - - expect(full.data.feed.length).toEqual(7) - expect(results(paginatedAll)).toEqual(results([full.data])) - }) - - it('reflects self-labels', async () => { - const carolTL = await agent.api.app.bsky.feed.getTimeline( - {}, - { headers: sc.getHeaders(carol) }, - ) - - const alicePost = carolTL.data.feed.find( - ({ post }) => post.uri === sc.posts[alice][0].ref.uriStr, - )?.post - - assert(alicePost, 'post does not exist') - - const postSelfLabels = alicePost.labels - ?.filter((label) => label.src === alice) - .map((label) => label.val) - - expect(postSelfLabels).toEqual(['self-label']) - - const authorSelfLabels = alicePost.author.labels - ?.filter((label) => label.src === alice) - .map((label) => label.val) - .sort() - - expect(authorSelfLabels).toEqual(['self-label-a', 'self-label-b']) - }) - - it('blocks posts, reposts, replies by actor takedown', async () => { - const actionResults = await Promise.all( - [bob, carol].map((did) => - agent.api.com.atproto.admin.takeModerationAction( - { - action: TAKEDOWN, - subject: { - $type: 'com.atproto.admin.defs#repoRef', - did, - }, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ), - ), - ) - - const aliceTL = await agent.api.app.bsky.feed.getTimeline( - { algorithm: FeedAlgorithm.ReverseChronological }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(aliceTL.data.feed)).toMatchSnapshot() - - // Cleanup - await Promise.all( - actionResults.map((result) => - agent.api.com.atproto.admin.reverseModerationAction( - { - id: result.data.id, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ), - ), - ) - }) - - it('blocks posts, reposts, replies by record takedown.', async () => { - const postRef1 = sc.posts[dan][1].ref // Repost - const postRef2 = sc.replies[bob][0].ref // Post and reply parent - const actionResults = await Promise.all( - [postRef1, postRef2].map((postRef) => - agent.api.com.atproto.admin.takeModerationAction( - { - action: TAKEDOWN, - subject: { - $type: 'com.atproto.repo.strongRef', - uri: postRef.uriStr, - cid: postRef.cidStr, - }, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ), - ), - ) - - const aliceTL = await agent.api.app.bsky.feed.getTimeline( - { algorithm: FeedAlgorithm.ReverseChronological }, - { headers: sc.getHeaders(alice) }, - ) - - expect(forSnapshot(aliceTL.data.feed)).toMatchSnapshot() - - // Cleanup - await Promise.all( - actionResults.map((result) => - agent.api.com.atproto.admin.reverseModerationAction( - { - id: result.data.id, - createdBy: 'did:example:admin', - reason: 'Y', - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ), - ), - ) - }) -}) From 631d7578c079f954a0acffa6d2aea79598e8a609 Mon Sep 17 00:00:00 2001 From: dholms Date: Wed, 20 Sep 2023 21:50:15 -0500 Subject: [PATCH 07/31] fix up tests --- packages/dev-env/src/pds.ts | 10 +- .../api/com/atproto/identity/resolveHandle.ts | 4 +- .../pds/src/api/com/atproto/repo/getRecord.ts | 20 +- .../feed-generation.test.ts.snap | 1382 -------- .../tests/__snapshots__/indexing.test.ts.snap | 141 - packages/pds/tests/_util.ts | 252 +- packages/pds/tests/account-deletion.test.ts | 23 - packages/pds/tests/file-uploads.test.ts | 20 - packages/pds/tests/handles.test.ts | 66 +- packages/pds/tests/indexing.test.ts | 299 -- .../timeline-skeleton.test.ts.snap | 2798 ----------------- .../tests/proxied/timeline-skeleton.test.ts | 99 - packages/pds/tests/server.test.ts | 4 +- 13 files changed, 163 insertions(+), 4955 deletions(-) delete mode 100644 packages/pds/tests/__snapshots__/feed-generation.test.ts.snap delete mode 100644 packages/pds/tests/__snapshots__/indexing.test.ts.snap delete mode 100644 packages/pds/tests/indexing.test.ts delete mode 100644 packages/pds/tests/proxied/__snapshots__/timeline-skeleton.test.ts.snap delete mode 100644 packages/pds/tests/proxied/timeline-skeleton.test.ts diff --git a/packages/dev-env/src/pds.ts b/packages/dev-env/src/pds.ts index 97b8ceb7452..5a137e59e6f 100644 --- a/packages/dev-env/src/pds.ts +++ b/packages/dev-env/src/pds.ts @@ -67,7 +67,8 @@ export class TestPds { labelerKeywords: { label_me: 'test-label', label_me_2: 'test-label-2' }, feedGenDid: 'did:example:feedGen', dbTxLockNonce: await randomStr(32, 'base32'), - bskyAppViewProxy: !!cfg.bskyAppViewEndpoint, + bskyAppViewEndpoint: cfg.bskyAppViewEndpoint ?? 'http://fake_address', + bskyAppViewDid: cfg.bskyAppViewDid ?? 'did:example:fake', bskyAppViewCdnUrlPattern: 'http://cdn.appview.com/%s/%s/%s', ...cfg, }) @@ -82,11 +83,7 @@ export class TestPds { : pds.Database.memory() await db.migrateToLatestOrThrow() - if ( - config.bskyAppViewEndpoint && - config.bskyAppViewProxy && - !cfg.enableInProcessAppView - ) { + if (cfg.bskyAppViewEndpoint && !cfg.enableInProcessAppView) { // Disable communication to app view within pds MessageDispatcher.prototype.send = async () => {} } @@ -97,7 +94,6 @@ export class TestPds { repoSigningKey, plcRotationKey, config, - algos: cfg.algos, }) await server.start() diff --git a/packages/pds/src/api/com/atproto/identity/resolveHandle.ts b/packages/pds/src/api/com/atproto/identity/resolveHandle.ts index b45c28f9dbd..4894103d1c1 100644 --- a/packages/pds/src/api/com/atproto/identity/resolveHandle.ts +++ b/packages/pds/src/api/com/atproto/identity/resolveHandle.ts @@ -33,7 +33,9 @@ export default function (server: Server, ctx: AppContext) { } // this is not someone on our server, but we help with resolving anyway - did = await tryResolveFromAppview(ctx.appviewAgent, handle) + if (!did) { + did = await tryResolveFromAppview(ctx.appviewAgent, handle) + } if (!did) { did = await ctx.idResolver.handle.resolve(handle) diff --git a/packages/pds/src/api/com/atproto/repo/getRecord.ts b/packages/pds/src/api/com/atproto/repo/getRecord.ts index 84b2475e4ca..29194c94843 100644 --- a/packages/pds/src/api/com/atproto/repo/getRecord.ts +++ b/packages/pds/src/api/com/atproto/repo/getRecord.ts @@ -1,6 +1,7 @@ import { AtUri } from '@atproto/syntax' import { Server } from '../../../../lexicon' import AppContext from '../../../../context' +import { InvalidRequestError } from '@atproto/xrpc-server' export default function (server: Server, ctx: AppContext) { server.com.atproto.repo.getRecord(async ({ params }) => { @@ -13,15 +14,16 @@ export default function (server: Server, ctx: AppContext) { const record = await ctx.services .record(ctx.db) .getRecord(uri, cid || null) - if (record) { - return { - encoding: 'application/json', - body: { - uri: record.uri, - cid: record.cid, - value: record.value, - }, - } + if (!record) { + throw new InvalidRequestError(`Could not locate record: ${uri}`) + } + return { + encoding: 'application/json', + body: { + uri: record.uri, + cid: record.cid, + value: record.value, + }, } } diff --git a/packages/pds/tests/__snapshots__/feed-generation.test.ts.snap b/packages/pds/tests/__snapshots__/feed-generation.test.ts.snap deleted file mode 100644 index b1c5319fd7d..00000000000 --- a/packages/pds/tests/__snapshots__/feed-generation.test.ts.snap +++ /dev/null @@ -1,1382 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`feed generation does not embed taken-down feed generator records in posts 1`] = ` -Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewNotFound", - "notFound": true, - "uri": "record(2)", - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "weird feed", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(0)", - "viewer": Object {}, -} -`; - -exports[`feed generation embeds feed generator records in posts 1`] = ` -Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.feed.defs#generatorView", - "cid": "cids(2)", - "creator": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(5)", - "val": "self-label-a", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(5)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(4)", - "following": "record(3)", - "muted": false, - }, - }, - "description": "Provides all feed candidates", - "did": "user(1)", - "displayName": "All", - "indexedAt": "1970-01-01T00:00:00.000Z", - "likeCount": 2, - "uri": "record(2)", - "viewer": Object { - "like": "record(6)", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "cool feed!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(0)", - "viewer": Object {}, -} -`; - -exports[`feed generation getActorFeeds fetches feed generators by actor. 1`] = ` -Array [ - Object { - "cid": "cids(0)", - "creator": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "its me!", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "description": "Provides odd-indexed feed candidates", - "did": "user(0)", - "displayName": "Odd", - "indexedAt": "1970-01-01T00:00:00.000Z", - "likeCount": 0, - "uri": "record(0)", - "viewer": Object {}, - }, - Object { - "cid": "cids(2)", - "creator": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "its me!", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "description": "Provides all feed candidates, blindly ignoring pagination limit", - "did": "user(0)", - "displayName": "Bad Pagination", - "indexedAt": "1970-01-01T00:00:00.000Z", - "likeCount": 0, - "uri": "record(4)", - "viewer": Object {}, - }, - Object { - "cid": "cids(3)", - "creator": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "its me!", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "description": "Provides even-indexed feed candidates", - "did": "user(0)", - "displayName": "Even", - "indexedAt": "1970-01-01T00:00:00.000Z", - "likeCount": 0, - "uri": "record(5)", - "viewer": Object {}, - }, - Object { - "cid": "cids(4)", - "creator": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "description": "its me!", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "description": "Provides all feed candidates", - "did": "user(0)", - "displayName": "All", - "indexedAt": "1970-01-01T00:00:00.000Z", - "likeCount": 2, - "uri": "record(6)", - "viewer": Object { - "like": "record(7)", - }, - }, -] -`; - -exports[`feed generation getFeed paginates, handling replies and reposts. 1`] = ` -Array [ - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(0)", - "val": "self-label", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "labels": Object { - "$type": "com.atproto.label.defs#selfLabels", - "values": Array [ - Object { - "val": "self-label", - }, - ], - }, - "text": "hey there", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(0)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(5)", - "val": "self-label-a", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(5)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(4)", - "following": "record(3)", - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(4)", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(5)", - "val": "self-label-a", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(5)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(4)", - "following": "record(3)", - "muted": false, - }, - }, - "cid": "cids(2)", - "embeds": Array [], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(2)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(6)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - }, - "text": "hi im carol", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(6)", - "viewer": Object { - "like": "record(9)", - }, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(7)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(8)", - "uri": "record(11)", - }, - "root": Object { - "cid": "cids(8)", - "uri": "record(11)", - }, - }, - "text": "of course", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(10)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(11)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(11)", - "viewer": Object {}, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(3)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(3)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(13)", - "muted": false, - }, - }, - "cid": "cids(9)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "cid": "cids(4)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(5)", - "val": "self-label-a", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(5)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(4)", - "following": "record(3)", - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(2)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(6)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(6)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(4)", - "uri": "record(6)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(0)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(12)", - "viewer": Object {}, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(8)", - "following": "record(7)", - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - }, -] -`; - -exports[`feed generation getFeed resolves basic feed contents. 1`] = ` -Array [ - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(0)", - "val": "self-label", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "labels": Object { - "$type": "com.atproto.label.defs#selfLabels", - "values": Array [ - Object { - "val": "self-label", - }, - ], - }, - "text": "hey there", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(0)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(4)", - "following": "record(3)", - "muted": false, - }, - }, - "cid": "cids(2)", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(6)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(8)", - "val": "self-label-a", - }, - Object { - "cid": "cids(6)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(8)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(7)", - "following": "record(6)", - "muted": false, - }, - }, - "cid": "cids(5)", - "embeds": Array [], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(5)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(3)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(4)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(5)", - "uri": "record(5)", - }, - }, - }, - "text": "hi im carol", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(2)", - "viewer": Object { - "like": "record(9)", - }, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(4)", - "following": "record(3)", - "muted": false, - }, - }, - "cid": "cids(7)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(8)", - "uri": "record(11)", - }, - "root": Object { - "cid": "cids(8)", - "uri": "record(11)", - }, - }, - "text": "of course", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(10)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(11)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(8)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(11)", - "viewer": Object {}, - }, - }, - }, -] -`; - -exports[`feed generation getFeedGenerator describes a feed gen & returns online status 1`] = ` -Object { - "isOnline": true, - "isValid": true, - "view": Object { - "cid": "cids(0)", - "creator": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "description": "Provides all feed candidates", - "did": "user(0)", - "displayName": "All", - "indexedAt": "1970-01-01T00:00:00.000Z", - "likeCount": 2, - "uri": "record(0)", - "viewer": Object { - "like": "record(4)", - }, - }, -} -`; - -exports[`feed generation getFeedGenerators describes multiple feed gens 1`] = ` -Object { - "feeds": Array [ - Object { - "cid": "cids(0)", - "creator": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "description": "Provides all feed candidates", - "did": "user(0)", - "displayName": "All", - "indexedAt": "1970-01-01T00:00:00.000Z", - "likeCount": 2, - "uri": "record(0)", - "viewer": Object { - "like": "record(4)", - }, - }, - Object { - "cid": "cids(2)", - "creator": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(3)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(3)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(2)", - "following": "record(1)", - "muted": false, - }, - }, - "description": "Provides even-indexed feed candidates", - "did": "user(0)", - "displayName": "Even", - "indexedAt": "1970-01-01T00:00:00.000Z", - "likeCount": 0, - "uri": "record(5)", - "viewer": Object {}, - }, - ], -} -`; diff --git a/packages/pds/tests/__snapshots__/indexing.test.ts.snap b/packages/pds/tests/__snapshots__/indexing.test.ts.snap deleted file mode 100644 index c4bc3f83cf0..00000000000 --- a/packages/pds/tests/__snapshots__/indexing.test.ts.snap +++ /dev/null @@ -1,141 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`indexing indexes posts. 1`] = ` -Object { - "thread": Object { - "$type": "app.bsky.feed.defs#threadViewPost", - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(1)", - }, - ], - "index": Object { - "byteEnd": 9, - "byteStart": 0, - }, - }, - ], - "text": "@bob.test how are you?", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(0)", - "viewer": Object {}, - }, - "replies": Array [], - }, -} -`; - -exports[`indexing indexes posts. 2`] = ` -Object { - "createNotifications": Array [ - Object { - "author": "user(1)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "reason": "mention", - "reasonSubject": null, - "recordCid": "cids(0)", - "recordUri": "record(0)", - "userDid": "user(0)", - }, - ], - "deleteNotifications": Array [], -} -`; - -exports[`indexing indexes profiles. 1`] = ` -Object { - "did": "user(0)", - "displayName": "dan", - "followersCount": 0, - "followsCount": 0, - "handle": "dan.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "postsCount": 0, - "viewer": Object { - "blockedBy": false, - "muted": false, - }, -} -`; - -exports[`indexing indexes profiles. 2`] = ` -Object { - "did": "user(0)", - "displayName": "danny", - "followersCount": 0, - "followsCount": 0, - "handle": "dan.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "postsCount": 0, - "viewer": Object { - "blockedBy": false, - "muted": false, - }, -} -`; - -exports[`indexing indexes profiles. 3`] = ` -Object { - "did": "user(0)", - "followersCount": 0, - "followsCount": 0, - "handle": "dan.test", - "labels": Array [], - "postsCount": 0, - "viewer": Object { - "blockedBy": false, - "muted": false, - }, -} -`; - -exports[`indexing indexes profiles. 4`] = ` -Object { - "createNotifications": Array [], - "deleteNotifications": Array [], - "updateNotifications": Array [], -} -`; diff --git a/packages/pds/tests/_util.ts b/packages/pds/tests/_util.ts index bdfb72ce02d..67aadaeaf2c 100644 --- a/packages/pds/tests/_util.ts +++ b/packages/pds/tests/_util.ts @@ -32,144 +32,146 @@ export type TestServerOpts = { migration?: string } -// export const runTestServer = async ( -// params: Partial = {}, -// opts: TestServerOpts = {}, -// ): Promise => { -// const repoSigningKey = await crypto.Secp256k1Keypair.create() -// const plcRotationKey = await crypto.Secp256k1Keypair.create() +export const runTestServer = async ( + params: Partial = {}, + opts: TestServerOpts = {}, +): Promise => { + const repoSigningKey = await crypto.Secp256k1Keypair.create() + const plcRotationKey = await crypto.Secp256k1Keypair.create() -// const dbPostgresUrl = params.dbPostgresUrl || process.env.DB_POSTGRES_URL -// const dbPostgresSchema = -// params.dbPostgresSchema || process.env.DB_POSTGRES_SCHEMA -// // run plc server + const dbPostgresUrl = params.dbPostgresUrl || process.env.DB_POSTGRES_URL + const dbPostgresSchema = + params.dbPostgresSchema || process.env.DB_POSTGRES_SCHEMA + // run plc server -// let plcDb -// if (dbPostgresUrl !== undefined) { -// plcDb = PlcDatabase.postgres({ -// url: dbPostgresUrl, -// schema: `plc_test_${dbPostgresSchema}`, -// }) -// await plcDb.migrateToLatestOrThrow() -// } else { -// plcDb = PlcDatabase.mock() -// } + let plcDb + if (dbPostgresUrl !== undefined) { + plcDb = PlcDatabase.postgres({ + url: dbPostgresUrl, + schema: `plc_test_${dbPostgresSchema}`, + }) + await plcDb.migrateToLatestOrThrow() + } else { + plcDb = PlcDatabase.mock() + } -// const plcServer = PlcServer.create({ db: plcDb }) -// const plcListener = await plcServer.start() -// const plcPort = (plcListener.address() as AddressInfo).port -// const plcUrl = `http://localhost:${plcPort}` + const plcServer = PlcServer.create({ db: plcDb }) + const plcListener = await plcServer.start() + const plcPort = (plcListener.address() as AddressInfo).port + const plcUrl = `http://localhost:${plcPort}` -// const recoveryKey = (await crypto.Secp256k1Keypair.create()).did() + const recoveryKey = (await crypto.Secp256k1Keypair.create()).did() -// const plcClient = new plc.Client(plcUrl) -// const serverDid = await plcClient.createDid({ -// signingKey: repoSigningKey.did(), -// rotationKeys: [recoveryKey, plcRotationKey.did()], -// handle: 'localhost', -// pds: 'https://pds.public.url', -// signer: plcRotationKey, -// }) + const plcClient = new plc.Client(plcUrl) + const serverDid = await plcClient.createDid({ + signingKey: repoSigningKey.did(), + rotationKeys: [recoveryKey, plcRotationKey.did()], + handle: 'localhost', + pds: 'https://pds.public.url', + signer: plcRotationKey, + }) -// const blobstoreLoc = path.join(os.tmpdir(), randomStr(5, 'base32')) + const blobstoreLoc = path.join(os.tmpdir(), randomStr(5, 'base32')) -// const cfg = new ServerConfig({ -// debugMode: true, -// version: '0.0.0', -// scheme: 'http', -// hostname: 'localhost', -// serverDid, -// recoveryKey, -// adminPassword: ADMIN_PASSWORD, -// moderatorPassword: MODERATOR_PASSWORD, -// triagePassword: TRIAGE_PASSWORD, -// inviteRequired: false, -// userInviteInterval: null, -// userInviteEpoch: Date.now(), -// didPlcUrl: plcUrl, -// didCacheMaxTTL: DAY, -// didCacheStaleTTL: HOUR, -// jwtSecret: 'jwt-secret', -// availableUserDomains: ['.test'], -// rateLimitsEnabled: false, -// appUrlPasswordReset: 'app://forgot-password', -// emailNoReplyAddress: 'noreply@blueskyweb.xyz', -// publicUrl: 'https://pds.public.url', -// imgUriSalt: '9dd04221f5755bce5f55f47464c27e1e', -// imgUriKey: -// 'f23ecd142835025f42c3db2cf25dd813956c178392760256211f9d315f8ab4d8', -// dbPostgresUrl: process.env.DB_POSTGRES_URL, -// blobstoreLocation: `${blobstoreLoc}/blobs`, -// blobstoreTmp: `${blobstoreLoc}/tmp`, -// labelerDid: 'did:example:labeler', -// labelerKeywords: { label_me: 'test-label', label_me_2: 'test-label-2' }, -// feedGenDid: 'did:example:feedGen', -// maxSubscriptionBuffer: 200, -// repoBackfillLimitMs: HOUR, -// sequencerLeaderLockId: uniqueLockId(), -// dbTxLockNonce: await randomStr(32, 'base32'), -// ...params, -// }) + const cfg = new ServerConfig({ + debugMode: true, + version: '0.0.0', + scheme: 'http', + hostname: 'localhost', + serverDid, + recoveryKey, + adminPassword: ADMIN_PASSWORD, + moderatorPassword: MODERATOR_PASSWORD, + triagePassword: TRIAGE_PASSWORD, + inviteRequired: false, + userInviteInterval: null, + userInviteEpoch: Date.now(), + didPlcUrl: plcUrl, + didCacheMaxTTL: DAY, + didCacheStaleTTL: HOUR, + jwtSecret: 'jwt-secret', + availableUserDomains: ['.test'], + rateLimitsEnabled: false, + appUrlPasswordReset: 'app://forgot-password', + emailNoReplyAddress: 'noreply@blueskyweb.xyz', + publicUrl: 'https://pds.public.url', + imgUriSalt: '9dd04221f5755bce5f55f47464c27e1e', + imgUriKey: + 'f23ecd142835025f42c3db2cf25dd813956c178392760256211f9d315f8ab4d8', + dbPostgresUrl: process.env.DB_POSTGRES_URL, + blobstoreLocation: `${blobstoreLoc}/blobs`, + blobstoreTmp: `${blobstoreLoc}/tmp`, + labelerDid: 'did:example:labeler', + labelerKeywords: { label_me: 'test-label', label_me_2: 'test-label-2' }, + feedGenDid: 'did:example:feedGen', + maxSubscriptionBuffer: 200, + repoBackfillLimitMs: HOUR, + sequencerLeaderLockId: uniqueLockId(), + bskyAppViewEndpoint: 'http://fake_address.invalid', + bskyAppViewDid: 'did:example:fake', + dbTxLockNonce: await randomStr(32, 'base32'), + ...params, + }) -// const db = -// cfg.dbPostgresUrl !== undefined -// ? Database.postgres({ -// url: cfg.dbPostgresUrl, -// schema: cfg.dbPostgresSchema, -// txLockNonce: cfg.dbTxLockNonce, -// }) -// : Database.memory() + const db = + cfg.dbPostgresUrl !== undefined + ? Database.postgres({ + url: cfg.dbPostgresUrl, + schema: cfg.dbPostgresSchema, + txLockNonce: cfg.dbTxLockNonce, + }) + : Database.memory() -// // Separate migration db on postgres in case migration changes some -// // connection state that we need in the tests, e.g. "alter database ... set ..." -// const migrationDb = -// cfg.dbPostgresUrl !== undefined -// ? Database.postgres({ -// url: cfg.dbPostgresUrl, -// schema: cfg.dbPostgresSchema, -// txLockNonce: cfg.dbTxLockNonce, -// }) -// : db -// if (opts.migration) { -// await migrationDb.migrateToOrThrow(opts.migration) -// } else { -// await migrationDb.migrateToLatestOrThrow() -// } -// if (migrationDb !== db) { -// await migrationDb.close() -// } + // Separate migration db on postgres in case migration changes some + // connection state that we need in the tests, e.g. "alter database ... set ..." + const migrationDb = + cfg.dbPostgresUrl !== undefined + ? Database.postgres({ + url: cfg.dbPostgresUrl, + schema: cfg.dbPostgresSchema, + txLockNonce: cfg.dbTxLockNonce, + }) + : db + if (opts.migration) { + await migrationDb.migrateToOrThrow(opts.migration) + } else { + await migrationDb.migrateToLatestOrThrow() + } + if (migrationDb !== db) { + await migrationDb.close() + } -// const blobstore = -// cfg.blobstoreLocation !== undefined -// ? await DiskBlobStore.create(cfg.blobstoreLocation, cfg.blobstoreTmp) -// : new MemoryBlobStore() + const blobstore = + cfg.blobstoreLocation !== undefined + ? await DiskBlobStore.create(cfg.blobstoreLocation, cfg.blobstoreTmp) + : new MemoryBlobStore() -// const pds = PDS.create({ -// db, -// blobstore, -// repoSigningKey, -// plcRotationKey, -// config: cfg, -// }) -// const pdsServer = await pds.start() -// const pdsPort = (pdsServer.address() as AddressInfo).port + const pds = PDS.create({ + db, + blobstore, + repoSigningKey, + plcRotationKey, + config: cfg, + }) + const pdsServer = await pds.start() + const pdsPort = (pdsServer.address() as AddressInfo).port -// // we refresh label cache by hand in `processAll` instead of on a timer -// pds.ctx.labelCache.stop() + // we refresh label cache by hand in `processAll` instead of on a timer + pds.ctx.labelCache.stop() -// return { -// url: `http://localhost:${pdsPort}`, -// ctx: pds.ctx, -// close: async () => { -// await pds.destroy() -// await plcServer.destroy() -// }, -// processAll: async () => { -// await pds.ctx.backgroundQueue.processAll() -// await pds.ctx.labelCache.fullRefresh() -// }, -// } -// } + return { + url: `http://localhost:${pdsPort}`, + ctx: pds.ctx, + close: async () => { + await pds.destroy() + await plcServer.destroy() + }, + processAll: async () => { + await pds.ctx.backgroundQueue.processAll() + await pds.ctx.labelCache.fullRefresh() + }, + } +} export const adminAuth = () => { return basicAuth('admin', ADMIN_PASSWORD) diff --git a/packages/pds/tests/account-deletion.test.ts b/packages/pds/tests/account-deletion.test.ts index 31ffcd1af5b..b99807ec6d5 100644 --- a/packages/pds/tests/account-deletion.test.ts +++ b/packages/pds/tests/account-deletion.test.ts @@ -237,29 +237,6 @@ describe('account deletion', () => { ) }) - it('no longer displays the users posts in feeds', async () => { - const feed = await agent.api.app.bsky.feed.getTimeline(undefined, { - headers: sc.getHeaders(sc.dids.alice), - }) - const found = feed.data.feed.filter( - (item) => item.post.author.did === carol.did, - ) - expect(found.length).toBe(0) - }) - - it('removes notifications from the user', async () => { - const notifs = await agent.api.app.bsky.notification.listNotifications( - undefined, - { - headers: sc.getHeaders(sc.dids.alice), - }, - ) - const found = notifs.data.notifications.filter( - (item) => item.author.did === sc.dids.carol, - ) - expect(found.length).toBe(0) - }) - it('can delete an empty user', async () => { const eve = await sc.createAccount('eve', { handle: 'eve.test', diff --git a/packages/pds/tests/file-uploads.test.ts b/packages/pds/tests/file-uploads.test.ts index fa36a4fb473..cd26c7fa0a8 100644 --- a/packages/pds/tests/file-uploads.test.ts +++ b/packages/pds/tests/file-uploads.test.ts @@ -5,8 +5,6 @@ import { CloseFn, runTestServer, TestServerInfo } from './_util' import { Database, ServerConfig } from '../src' import DiskBlobStore from '../src/storage/disk-blobstore' import * as uint8arrays from 'uint8arrays' -import * as image from '../src/image' -import axios from 'axios' import { randomBytes } from '@atproto/crypto' import { BlobRef } from '@atproto/lexicon' import { ids } from '../src/lexicon/lexicons' @@ -147,24 +145,6 @@ describe('file uploads', () => { expect(uint8arrays.equals(smallFile, new Uint8Array(data))).toBeTruthy() }) - it('serves the referenced blob', async () => { - const profile = await aliceAgent.api.app.bsky.actor.getProfile({ - actor: 'alice.test', - }) - const avatar = profile.data.avatar as string - expect(typeof avatar).toBe('string') - const url = avatar.replace(cfg.publicUrl, serverUrl) - const res = await axios.get(url, { responseType: 'stream' }) - expect(res.headers['content-type']).toBe('image/jpeg') - const info = await image.getInfo(res.data) - expect(info).toEqual( - expect.objectContaining({ - height: 1000, - width: 1000, - }), - ) - }) - let largeBlob: BlobRef let largeFile: Uint8Array diff --git a/packages/pds/tests/handles.test.ts b/packages/pds/tests/handles.test.ts index b0827b2e953..d6b6fd60caa 100644 --- a/packages/pds/tests/handles.test.ts +++ b/packages/pds/tests/handles.test.ts @@ -50,6 +50,15 @@ describe('handles', () => { await close() }) + const getHandleFromDb = async (did: string): Promise => { + const res = await ctx.db.db + .selectFrom('did_handle') + .selectAll() + .where('did', '=', did) + .executeTakeFirst() + return res?.handle + } + it('resolves handles', async () => { const res = await agent.api.com.atproto.identity.resolveHandle({ handle: 'alice.test', @@ -93,35 +102,6 @@ describe('handles', () => { sc.accounts[alice].refreshJwt = res.data.refreshJwt }) - it('returns the correct handle in views', async () => { - const profile = await agent.api.app.bsky.actor.getProfile( - { actor: alice }, - { headers: sc.getHeaders(bob) }, - ) - expect(profile.data.handle).toBe(newHandle) - - const timeline = await agent.api.app.bsky.feed.getTimeline( - {}, - { headers: sc.getHeaders(bob) }, - ) - - const alicePosts = timeline.data.feed.filter( - (post) => post.post.author.did === alice, - ) - for (const post of alicePosts) { - expect(post.post.author.handle).toBe(newHandle) - } - - const followers = await agent.api.app.bsky.graph.getFollowers( - { actor: bob }, - { headers: sc.getHeaders(bob) }, - ) - - const aliceFollows = followers.data.followers.filter((f) => f.did === alice) - expect(aliceFollows.length).toBe(1) - expect(aliceFollows[0].handle).toBe(newHandle) - }) - it('does not allow taking a handle that already exists', async () => { const attempt = agent.api.com.atproto.identity.updateHandle( { handle: 'Bob.test' }, @@ -194,11 +174,8 @@ describe('handles', () => { }, { headers: sc.getHeaders(alice), encoding: 'application/json' }, ) - const profile = await agent.api.app.bsky.actor.getProfile( - { actor: alice }, - { headers: sc.getHeaders(bob) }, - ) - expect(profile.data.handle).toBe('alice.external') + const dbHandle = await getHandleFromDb(alice) + expect(dbHandle).toBe('alice.external') const data = await idResolver.did.resolveAtprotoData(alice) expect(data.handle).toBe('alice.external') @@ -225,11 +202,8 @@ describe('handles', () => { 'External handle did not resolve to DID', ) - const profile = await agent.api.app.bsky.actor.getProfile( - { actor: alice }, - { headers: sc.getHeaders(bob) }, - ) - expect(profile.data.handle).toBe('alice.external') + const dbHandle = await getHandleFromDb(alice) + expect(dbHandle).toBe('alice.external') }) it('allows admin overrules of service domains', async () => { @@ -244,11 +218,8 @@ describe('handles', () => { }, ) - const profile = await agent.api.app.bsky.actor.getProfile( - { actor: bob }, - { headers: sc.getHeaders(bob) }, - ) - expect(profile.data.handle).toBe('bob-alt.test') + const dbHandle = await getHandleFromDb(bob) + expect(dbHandle).toBe('bob-alt.test') }) it('allows admin override of reserved domains', async () => { @@ -263,11 +234,8 @@ describe('handles', () => { }, ) - const profile = await agent.api.app.bsky.actor.getProfile( - { actor: bob }, - { headers: sc.getHeaders(bob) }, - ) - expect(profile.data.handle).toBe('dril.test') + const dbHandle = await getHandleFromDb(bob) + expect(dbHandle).toBe('dril.test') }) it('requires admin auth', async () => { diff --git a/packages/pds/tests/indexing.test.ts b/packages/pds/tests/indexing.test.ts deleted file mode 100644 index a99053a07c7..00000000000 --- a/packages/pds/tests/indexing.test.ts +++ /dev/null @@ -1,299 +0,0 @@ -import AtpAgent, { - AppBskyActorProfile, - AppBskyFeedPost, - AppBskyFeedLike, - AppBskyFeedRepost, -} from '@atproto/api' -import { AtUri } from '@atproto/syntax' -import { CloseFn, forSnapshot, runTestServer, TestServerInfo } from './_util' -import { SeedClient } from './seeds/client' -import usersSeed from './seeds/users' -import { Database } from '../src' -import { prepareCreate, prepareDelete, prepareUpdate } from '../src/repo' -import { ids } from '../src/lexicon/lexicons' - -describe('indexing', () => { - let server: TestServerInfo - let close: CloseFn - let agent: AtpAgent - let sc: SeedClient - - beforeAll(async () => { - server = await runTestServer({ - dbPostgresSchema: 'indexing', - }) - close = server.close - agent = new AtpAgent({ service: server.url }) - sc = new SeedClient(agent) - await usersSeed(sc) - }) - - afterAll(async () => { - await close() - }) - - it('indexes posts.', async () => { - const { db, services } = server.ctx - const createdAt = new Date().toISOString() - const createRecord = await prepareCreate({ - did: sc.dids.alice, - collection: ids.AppBskyFeedPost, - record: { - $type: ids.AppBskyFeedPost, - text: '@bob.test how are you?', - facets: [ - { - index: { byteStart: 0, byteEnd: 9 }, - features: [ - { - $type: `${ids.AppBskyRichtextFacet}#mention`, - did: sc.dids.bob, - }, - ], - }, - ], - createdAt, - } as AppBskyFeedPost.Record, - }) - const { uri } = createRecord - const deleteRecord = prepareDelete({ - did: sc.dids.alice, - collection: ids.AppBskyFeedPost, - rkey: uri.rkey, - }) - - // Create - await services - .repo(db) - .processWrites({ did: sc.dids.alice, writes: [createRecord] }, 1) - await server.processAll() - - const getAfterCreate = await agent.api.app.bsky.feed.getPostThread( - { uri: uri.toString() }, - { headers: sc.getHeaders(sc.dids.alice) }, - ) - expect(forSnapshot(getAfterCreate.data)).toMatchSnapshot() - const createNotifications = await getNotifications(db, uri) - - // Delete - await services - .repo(db) - .processWrites({ did: sc.dids.alice, writes: [deleteRecord] }, 1) - await server.processAll() - - const getAfterDelete = agent.api.app.bsky.feed.getPostThread( - { uri: uri.toString() }, - { headers: sc.getHeaders(sc.dids.alice) }, - ) - await expect(getAfterDelete).rejects.toThrow(/Post not found:/) - const deleteNotifications = await getNotifications(db, uri) - - expect( - forSnapshot({ - createNotifications, - deleteNotifications, - }), - ).toMatchSnapshot() - }) - - it('indexes profiles.', async () => { - const { db, services } = server.ctx - const createRecord = await prepareCreate({ - did: sc.dids.dan, - collection: ids.AppBskyActorProfile, - rkey: 'self', - record: { - $type: ids.AppBskyActorProfile, - displayName: 'dan', - } as AppBskyActorProfile.Record, - }) - const { uri } = createRecord - const updateRecord = await prepareUpdate({ - did: sc.dids.dan, - collection: ids.AppBskyActorProfile, - rkey: uri.rkey, - record: { - $type: ids.AppBskyActorProfile, - displayName: 'danny', - } as AppBskyActorProfile.Record, - }) - const deleteRecord = prepareDelete({ - did: sc.dids.dan, - collection: ids.AppBskyActorProfile, - rkey: uri.rkey, - }) - - // Create - await services - .repo(db) - .processWrites({ did: sc.dids.dan, writes: [createRecord] }, 1) - await server.processAll() - - const getAfterCreate = await agent.api.app.bsky.actor.getProfile( - { actor: sc.dids.dan }, - { headers: sc.getHeaders(sc.dids.alice) }, - ) - expect(forSnapshot(getAfterCreate.data)).toMatchSnapshot() - const createNotifications = await getNotifications(db, uri) - - // Update - await services - .repo(db) - .processWrites({ did: sc.dids.dan, writes: [updateRecord] }, 1) - await server.processAll() - - const getAfterUpdate = await agent.api.app.bsky.actor.getProfile( - { actor: sc.dids.dan }, - { headers: sc.getHeaders(sc.dids.alice) }, - ) - expect(forSnapshot(getAfterUpdate.data)).toMatchSnapshot() - const updateNotifications = await getNotifications(db, uri) - - // Delete - await services - .repo(db) - .processWrites({ did: sc.dids.dan, writes: [deleteRecord] }, 1) - await server.processAll() - - const getAfterDelete = await agent.api.app.bsky.actor.getProfile( - { actor: sc.dids.dan }, - { headers: sc.getHeaders(sc.dids.alice) }, - ) - expect(forSnapshot(getAfterDelete.data)).toMatchSnapshot() - const deleteNotifications = await getNotifications(db, uri) - - expect( - forSnapshot({ - createNotifications, - updateNotifications, - deleteNotifications, - }), - ).toMatchSnapshot() - }) - - it('does not notify user of own like or repost', async () => { - const { db, services } = server.ctx - const createdAt = new Date().toISOString() - - const originalPost = await prepareCreate({ - did: sc.dids.bob, - collection: ids.AppBskyFeedPost, - record: { - $type: ids.AppBskyFeedPost, - text: 'original post', - createdAt, - } as AppBskyFeedPost.Record, - }) - - const originalPostRef = { - uri: originalPost.uri.toString(), - cid: originalPost.cid.toString(), - } - - // own actions - const ownLike = await prepareCreate({ - did: sc.dids.bob, - collection: ids.AppBskyFeedLike, - record: { - $type: ids.AppBskyFeedLike, - subject: originalPostRef, - createdAt, - } as AppBskyFeedLike.Record, - }) - const ownRepost = await prepareCreate({ - did: sc.dids.bob, - collection: ids.AppBskyFeedRepost, - record: { - $type: ids.AppBskyFeedRepost, - subject: originalPostRef, - createdAt, - } as AppBskyFeedRepost.Record, - }) - - // other actions - const aliceLike = await prepareCreate({ - did: sc.dids.alice, - collection: ids.AppBskyFeedLike, - record: { - $type: ids.AppBskyFeedLike, - subject: originalPostRef, - createdAt, - } as AppBskyFeedLike.Record, - }) - const aliceRepost = await prepareCreate({ - did: sc.dids.alice, - collection: ids.AppBskyFeedRepost, - record: { - $type: ids.AppBskyFeedRepost, - subject: originalPostRef, - createdAt, - } as AppBskyFeedRepost.Record, - }) - - await services.repo(db).processWrites( - { - did: sc.dids.bob, - writes: [originalPost, ownLike, ownRepost], - }, - 1, - ) - await services.repo(db).processWrites( - { - did: sc.dids.alice, - writes: [aliceLike, aliceRepost], - }, - 1, - ) - - await server.processAll() - - const { - data: { notifications }, - } = await agent.api.app.bsky.notification.listNotifications( - {}, - { headers: sc.getHeaders(sc.dids.bob) }, - ) - - expect(notifications).toHaveLength(2) - expect( - notifications.every((n) => { - return n.author.did !== sc.dids.bob - }), - ).toBeTruthy() - - // Cleanup - const del = (uri: AtUri) => { - return prepareDelete({ - did: uri.host, - collection: uri.collection, - rkey: uri.rkey, - }) - } - - // Delete - await services.repo(db).processWrites( - { - did: sc.dids.bob, - writes: [del(originalPost.uri), del(ownLike.uri), del(ownRepost.uri)], - }, - 1, - ) - await services.repo(db).processWrites( - { - did: sc.dids.alice, - writes: [del(aliceLike.uri), del(aliceRepost.uri)], - }, - 1, - ) - await server.processAll() - }) - - async function getNotifications(db: Database, uri: AtUri) { - return await db.db - .selectFrom('user_notification') - .selectAll() - .where('recordUri', '=', uri.toString()) - .orderBy('indexedAt') - .execute() - } -}) diff --git a/packages/pds/tests/proxied/__snapshots__/timeline-skeleton.test.ts.snap b/packages/pds/tests/proxied/__snapshots__/timeline-skeleton.test.ts.snap deleted file mode 100644 index 1bb947bb64c..00000000000 --- a/packages/pds/tests/proxied/__snapshots__/timeline-skeleton.test.ts.snap +++ /dev/null @@ -1,2798 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`proxies timeline skeleton feed skeleton construction 1`] = ` -Object { - "cursor": "0000000000000::bafycid", - "feed": Array [ - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(3)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(5)", - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(3)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(9)", - "following": "record(8)", - "muted": false, - }, - }, - "cid": "cids(6)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "of course", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(7)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(5)", - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(3)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(7)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(3)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(3)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(12)", - "muted": false, - }, - }, - "cid": "cids(8)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(9)", - "following": "record(8)", - "muted": false, - }, - }, - "cid": "cids(9)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(13)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(10)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(11)", - "uri": "record(14)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(11)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(9)", - "uri": "record(13)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(0)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(7)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(10)", - "val": "test-label", - }, - ], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(8)", - "uri": "record(11)", - }, - }, - "text": "yoohoo label_me", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(10)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(5)", - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(12)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000+00:00", - "text": "bobby boy here", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(15)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(2)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(9)", - "following": "record(8)", - "muted": false, - }, - }, - "cid": "cids(9)", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(5)", - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(11)", - "embeds": Array [], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(14)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(10)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(11)", - "uri": "record(14)", - }, - }, - }, - "text": "hi im carol", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(13)", - "viewer": Object { - "like": "record(16)", - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(1)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(1)", - "uri": "record(6)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(5)", - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(11)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(14)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(13)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(13)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(17)", - "val": "self-label", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "labels": Object { - "$type": "com.atproto.label.defs#selfLabels", - "values": Array [ - Object { - "val": "self-label", - }, - ], - }, - "text": "hey there", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(17)", - "viewer": Object {}, - }, - }, - ], -} -`; - -exports[`proxies timeline skeleton timeline skeleton construction 1`] = ` -Object { - "cursor": "0000000000000::bafycid", - "feed": Array [ - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(3)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object {}, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": false, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(3)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(6)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(3)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "cid": "cids(7)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": false, - }, - }, - "cid": "cids(9)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(12)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(9)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(8)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(9)", - "uri": "record(12)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(7)", - "uri": "record(9)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(0)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(8)", - "viewer": Object {}, - }, - "reason": Object { - "$type": "app.bsky.feed.defs#reasonRepost", - "by": Object { - "did": "user(3)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(3)", - "uri": "record(3)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "thanks bob", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(0)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": false, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(3)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(3)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "cid": "cids(10)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "of course", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(13)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": false, - }, - }, - "cid": "cids(3)", - "embed": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - ], - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label", - }, - Object { - "cid": "cids(3)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(3)", - "val": "test-label-2", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - ], - }, - "reply": Object { - "parent": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - "root": Object { - "cid": "cids(2)", - "uri": "record(2)", - }, - }, - "text": "hear that label_me label_me_2", - }, - "replyCount": 1, - "repostCount": 0, - "uri": "record(3)", - "viewer": Object {}, - }, - "reply": Object { - "parent": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - "root": Object { - "$type": "app.bsky.feed.defs#postView", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(11)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(6)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(3)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "cid": "cids(7)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(9)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(8)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(9)", - "uri": "record(12)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(8)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(7)", - "uri": "record(9)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(0)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(11)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(14)", - "val": "test-label", - }, - ], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(6)", - "uri": "record(8)", - }, - }, - "text": "yoohoo label_me", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(14)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": false, - }, - }, - "cid": "cids(12)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000+00:00", - "text": "bobby boy here", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(15)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(2)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 3, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000000Z", - "text": "again", - }, - "replyCount": 2, - "repostCount": 1, - "uri": "record(2)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(6)", - "embed": Object { - "$type": "app.bsky.embed.record#view", - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "did": "user(3)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "cid": "cids(7)", - "embeds": Array [ - Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": false, - }, - }, - "cid": "cids(9)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(12)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - ], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(9)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(8)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(9)", - "uri": "record(12)", - }, - }, - }, - "text": "hi im carol", - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.record", - "record": Object { - "cid": "cids(7)", - "uri": "record(9)", - }, - }, - "facets": Array [ - Object { - "features": Array [ - Object { - "$type": "app.bsky.richtext.facet#mention", - "did": "user(0)", - }, - ], - "index": Object { - "byteEnd": 18, - "byteStart": 0, - }, - }, - ], - "text": "@alice.bluesky.xyz is the best", - }, - "replyCount": 0, - "repostCount": 1, - "uri": "record(8)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(1)", - "handle": "dan.test", - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(1)", - "val": "repo-action-label", - }, - ], - "viewer": Object { - "blockedBy": false, - "following": "record(4)", - "muted": false, - }, - }, - "cid": "cids(13)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "text": "dan here!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(16)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "did": "user(3)", - "handle": "carol.test", - "labels": Array [], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(11)", - "following": "record(10)", - "muted": false, - }, - }, - "cid": "cids(7)", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia#view", - "media": Object { - "$type": "app.bsky.embed.images#view", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - "thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg", - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - "thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg", - }, - ], - }, - "record": Object { - "record": Object { - "$type": "app.bsky.embed.record#viewRecord", - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": false, - }, - }, - "cid": "cids(9)", - "embeds": Array [], - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "uri": "record(12)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - }, - }, - }, - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 2, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "embed": Object { - "$type": "app.bsky.embed.recordWithMedia", - "media": Object { - "$type": "app.bsky.embed.images", - "images": Array [ - Object { - "alt": "tests/image/fixtures/key-landscape-small.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(5)", - }, - "size": 4114, - }, - }, - Object { - "alt": "tests/image/fixtures/key-alt.jpg", - "image": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(8)", - }, - "size": 12736, - }, - }, - ], - }, - "record": Object { - "record": Object { - "cid": "cids(9)", - "uri": "record(12)", - }, - }, - }, - "text": "hi im carol", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(9)", - "viewer": Object { - "like": "record(17)", - }, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(2)", - "displayName": "bobby", - "handle": "bob.test", - "labels": Array [ - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-a", - }, - Object { - "cid": "cids(4)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(2)", - "uri": "record(7)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "followedBy": "record(6)", - "following": "record(5)", - "muted": false, - }, - }, - "cid": "cids(9)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "langs": Array [ - "en-US", - "i-klingon", - ], - "text": "bob back at it again!", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(12)", - "viewer": Object {}, - }, - }, - Object { - "post": Object { - "author": Object { - "avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg", - "did": "user(0)", - "displayName": "ali", - "handle": "alice.test", - "labels": Array [ - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-a", - }, - Object { - "cid": "cids(1)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(1)", - "val": "self-label-b", - }, - ], - "viewer": Object { - "blockedBy": false, - "muted": false, - }, - }, - "cid": "cids(14)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(14)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(18)", - "val": "self-label", - }, - ], - "likeCount": 0, - "record": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "labels": Object { - "$type": "com.atproto.label.defs#selfLabels", - "values": Array [ - Object { - "val": "self-label", - }, - ], - }, - "text": "hey there", - }, - "replyCount": 0, - "repostCount": 0, - "uri": "record(18)", - "viewer": Object {}, - }, - }, - ], -} -`; diff --git a/packages/pds/tests/proxied/timeline-skeleton.test.ts b/packages/pds/tests/proxied/timeline-skeleton.test.ts deleted file mode 100644 index d7617a74a16..00000000000 --- a/packages/pds/tests/proxied/timeline-skeleton.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -import AtpAgent, { AtUri } from '@atproto/api' -import { TestNetwork } from '@atproto/dev-env' -import { SeedClient } from '../seeds/client' -import basicSeed from '../seeds/basic' -import { forSnapshot } from '../_util' -import * as bsky from '@atproto/bsky' -import { makeAlgos } from '../../src' - -describe('proxies timeline skeleton', () => { - let network: TestNetwork - let agent: AtpAgent - let sc: SeedClient - - let alice: string - - const feedGenDid = 'did:example:feed-gen' - const feedPublisherDid = 'did:example:feed-publisher' - - beforeAll(async () => { - network = await TestNetwork.create({ - dbPostgresSchema: 'proxy_timeline_skeleton', - pds: { - feedGenDid, - algos: makeAlgos(feedPublisherDid), - enableInProcessAppView: true, - bskyAppViewProxy: false, - }, - bsky: { - feedGenDid, - algos: bsky.makeAlgos(feedPublisherDid), - }, - }) - agent = network.pds.getClient() - sc = new SeedClient(agent) - await basicSeed(sc) - await network.processAll() - alice = sc.dids.alice - }) - - afterAll(async () => { - await network.close() - }) - - it('timeline skeleton construction', async () => { - const res = await agent.api.app.bsky.feed.getTimeline( - {}, - { - headers: { ...sc.getHeaders(alice), 'x-appview-proxy': 'true' }, - }, - ) - - expect(forSnapshot(res.data)).toMatchSnapshot() - const pt1 = await agent.api.app.bsky.feed.getTimeline( - { - limit: 2, - }, - { - headers: { ...sc.getHeaders(alice), 'x-appview-proxy': 'true' }, - }, - ) - const pt2 = await agent.api.app.bsky.feed.getTimeline( - { - cursor: pt1.data.cursor, - }, - { - headers: { ...sc.getHeaders(alice), 'x-appview-proxy': 'true' }, - }, - ) - expect([...pt1.data.feed, ...pt2.data.feed]).toEqual(res.data.feed) - }) - - it('feed skeleton construction', async () => { - const uri = AtUri.make( - feedPublisherDid, - 'app.bsky.feed.generator', - 'mutuals', - ) - const res = await agent.api.app.bsky.feed.getFeed( - { feed: uri.toString() }, - { - headers: { ...sc.getHeaders(alice), 'x-appview-proxy': 'true' }, - }, - ) - expect(forSnapshot(res.data)).toMatchSnapshot() - const pt1 = await agent.api.app.bsky.feed.getFeed( - { feed: uri.toString(), limit: 2 }, - { - headers: { ...sc.getHeaders(alice), 'x-appview-proxy': 'true' }, - }, - ) - const pt2 = await agent.api.app.bsky.feed.getFeed( - { feed: uri.toString(), cursor: pt1.data.cursor }, - { - headers: { ...sc.getHeaders(alice), 'x-appview-proxy': 'true' }, - }, - ) - expect([...pt1.data.feed, ...pt2.data.feed]).toEqual(res.data.feed) - }) -}) diff --git a/packages/pds/tests/server.test.ts b/packages/pds/tests/server.test.ts index ae9f34fed26..34c08bc4e73 100644 --- a/packages/pds/tests/server.test.ts +++ b/packages/pds/tests/server.test.ts @@ -85,7 +85,7 @@ describe('server', () => { }) }) - it('compresses large json responses', async () => { + it.skip('compresses large json responses', async () => { const res = await axios.get( `${server.url}/xrpc/app.bsky.feed.getTimeline`, { @@ -120,7 +120,7 @@ describe('server', () => { it('healthcheck fails when database is unavailable.', async () => { // destroy to release lock & allow db to close - await server.ctx.sequencerLeader.destroy() + await server.ctx.sequencerLeader?.destroy() await db.close() let error: AxiosError From e95cee7ab85ae6cbc76ee1807f00bbfda9c414e6 Mon Sep 17 00:00:00 2001 From: dholms Date: Wed, 20 Sep 2023 21:57:47 -0500 Subject: [PATCH 08/31] fix api tests --- packages/api/tests/agent.test.ts | 19 ++++++++--- packages/api/tests/bsky-agent.test.ts | 45 +++++++++++++++++---------- 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/packages/api/tests/agent.test.ts b/packages/api/tests/agent.test.ts index 2444e7e0c61..c0fb67b9902 100644 --- a/packages/api/tests/agent.test.ts +++ b/packages/api/tests/agent.test.ts @@ -197,7 +197,7 @@ describe('agent', () => { // put the agent through the auth flow AtpAgent.configure({ fetch: tokenExpiredFetchHandler }) - const res1 = await agent.api.app.bsky.feed.getTimeline() + const res1 = await createPost(agent) AtpAgent.configure({ fetch: defaultFetchHandler }) expect(res1.success).toEqual(true) @@ -267,9 +267,9 @@ describe('agent', () => { // put the agent through the auth flow AtpAgent.configure({ fetch: tokenExpiredFetchHandler }) const [res1, res2, res3] = await Promise.all([ - agent.api.app.bsky.feed.getTimeline(), - agent.api.app.bsky.feed.getTimeline(), - agent.api.app.bsky.feed.getTimeline(), + createPost(agent), + createPost(agent), + createPost(agent), ]) AtpAgent.configure({ fetch: defaultFetchHandler }) @@ -462,3 +462,14 @@ describe('agent', () => { }) }) }) + +const createPost = async (agent: AtpAgent) => { + return agent.api.com.atproto.repo.createRecord({ + repo: agent.session?.did ?? '', + collection: 'app.bsky.feed.post', + record: { + text: 'hello there', + createdAt: new Date().toISOString(), + }, + }) +} diff --git a/packages/api/tests/bsky-agent.test.ts b/packages/api/tests/bsky-agent.test.ts index 24b40153458..07e76993221 100644 --- a/packages/api/tests/bsky-agent.test.ts +++ b/packages/api/tests/bsky-agent.test.ts @@ -20,6 +20,20 @@ describe('agent', () => { await close() }) + const getProfileDisplayName = async ( + agent: BskyAgent, + ): Promise => { + try { + const res = await agent.api.app.bsky.actor.profile.get({ + repo: agent.session?.did || '', + rkey: 'self', + }) + return res.value.displayName ?? '' + } catch (err) { + return undefined + } + } + it('upsertProfile correctly creates and updates profiles.', async () => { const agent = new BskyAgent({ service: server.url }) @@ -28,9 +42,8 @@ describe('agent', () => { email: 'user1@test.com', password: 'password', }) - - const profile1 = await agent.getProfile({ actor: agent.session?.did || '' }) - expect(profile1.data.displayName).toBeFalsy() + const displayName1 = await getProfileDisplayName(agent) + expect(displayName1).toBeFalsy() await agent.upsertProfile((existing) => { expect(existing).toBeFalsy() @@ -39,8 +52,8 @@ describe('agent', () => { } }) - const profile2 = await agent.getProfile({ actor: agent.session?.did || '' }) - expect(profile2.data.displayName).toBe('Bob') + const displayName2 = await getProfileDisplayName(agent) + expect(displayName2).toBe('Bob') await agent.upsertProfile((existing) => { expect(existing).toBeTruthy() @@ -49,8 +62,8 @@ describe('agent', () => { } }) - const profile3 = await agent.getProfile({ actor: agent.session?.did || '' }) - expect(profile3.data.displayName).toBe('BOB') + const displayName3 = await getProfileDisplayName(agent) + expect(displayName3).toBe('BOB') }) it('upsertProfile correctly handles CAS failures.', async () => { @@ -62,12 +75,12 @@ describe('agent', () => { password: 'password', }) - const profile1 = await agent.getProfile({ actor: agent.session?.did || '' }) - expect(profile1.data.displayName).toBeFalsy() + const displayName1 = await getProfileDisplayName(agent) + expect(displayName1).toBeFalsy() let hasConflicted = false let ranTwice = false - await agent.upsertProfile(async (existing) => { + await agent.upsertProfile(async (_existing) => { if (!hasConflicted) { await agent.com.atproto.repo.putRecord({ repo: agent.session?.did || '', @@ -88,8 +101,8 @@ describe('agent', () => { }) expect(ranTwice).toBe(true) - const profile2 = await agent.getProfile({ actor: agent.session?.did || '' }) - expect(profile2.data.displayName).toBe('Bob') + const displayName2 = await getProfileDisplayName(agent) + expect(displayName2).toBe('Bob') }) it('upsertProfile wont endlessly retry CAS failures.', async () => { @@ -101,10 +114,10 @@ describe('agent', () => { password: 'password', }) - const profile1 = await agent.getProfile({ actor: agent.session?.did || '' }) - expect(profile1.data.displayName).toBeFalsy() + const displayName1 = await getProfileDisplayName(agent) + expect(displayName1).toBeFalsy() - const p = agent.upsertProfile(async (existing) => { + const p = agent.upsertProfile(async (_existing) => { await agent.com.atproto.repo.putRecord({ repo: agent.session?.did || '', collection: 'app.bsky.actor.profile', @@ -130,7 +143,7 @@ describe('agent', () => { password: 'password', }) - const p = agent.upsertProfile((existing) => { + const p = agent.upsertProfile((_existing) => { return { displayName: { string: 'Bob' }, } as unknown as AppBskyActorProfile.Record From 5c4fca3de7d3e60921238dfbb1b67bc9167a54c7 Mon Sep 17 00:00:00 2001 From: dholms Date: Wed, 20 Sep 2023 22:23:23 -0500 Subject: [PATCH 09/31] fix build --- packages/dev-env/src/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/dev-env/src/types.ts b/packages/dev-env/src/types.ts index 0aac4f3aa25..d87e78a679a 100644 --- a/packages/dev-env/src/types.ts +++ b/packages/dev-env/src/types.ts @@ -11,7 +11,6 @@ export type PdsConfig = Partial & { plcUrl: string migration?: string enableInProcessAppView?: boolean - algos?: pds.MountedAlgos enableLabelsCache?: boolean } From 6e7fae7aed27f9472b85fbf00ea40a888bdb9784 Mon Sep 17 00:00:00 2001 From: dholms Date: Thu, 21 Sep 2023 11:00:29 -0500 Subject: [PATCH 10/31] fix compression test --- packages/pds/tests/server.test.ts | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/pds/tests/server.test.ts b/packages/pds/tests/server.test.ts index 34c08bc4e73..3994df3a9ab 100644 --- a/packages/pds/tests/server.test.ts +++ b/packages/pds/tests/server.test.ts @@ -1,12 +1,13 @@ import { AddressInfo } from 'net' import express from 'express' import axios, { AxiosError } from 'axios' -import AtpAgent from '@atproto/api' +import AtpAgent, { AtUri } from '@atproto/api' import { CloseFn, runTestServer, TestServerInfo } from './_util' import { handler as errorHandler } from '../src/error' import { SeedClient } from './seeds/client' import basicSeed from './seeds/basic' import { Database } from '../src' +import { randomStr } from '@atproto/crypto' describe('server', () => { let server: TestServerInfo @@ -85,9 +86,27 @@ describe('server', () => { }) }) - it.skip('compresses large json responses', async () => { + it('compresses large json responses', async () => { + // first create a large record + const record = { + text: 'blahblabh', + createdAt: new Date().toISOString(), + } + for (let i = 0; i < 100; i++) { + record[randomStr(8, 'base32')] = randomStr(32, 'base32') + } + const createRes = await agent.com.atproto.repo.createRecord( + { + repo: alice, + collection: 'app.bsky.feed.post', + record, + }, + { headers: sc.getHeaders(alice), encoding: 'application/json' }, + ) + const uri = new AtUri(createRes.data.uri) + const res = await axios.get( - `${server.url}/xrpc/app.bsky.feed.getTimeline`, + `${server.url}/xrpc/com.atproto.repo.getRecord?repo=${uri.host}&collection=${uri.collection}&rkey=${uri.rkey}`, { decompress: false, headers: { ...sc.getHeaders(alice), 'accept-encoding': 'gzip' }, From df3ecc67667eb5f50fb0c40810e502245588bdc9 Mon Sep 17 00:00:00 2001 From: dholms Date: Thu, 21 Sep 2023 14:53:37 -0500 Subject: [PATCH 11/31] update image tests --- packages/pds/src/config.ts | 33 --- packages/pds/src/context.ts | 6 - packages/pds/src/image/invalidator.ts | 26 --- packages/pds/src/image/logger.ts | 5 - packages/pds/src/image/server.ts | 170 --------------- packages/pds/src/image/uri.ts | 202 ----------------- packages/pds/src/index.ts | 40 ---- packages/pds/src/services/index.ts | 13 +- packages/pds/src/services/moderation/index.ts | 28 +-- packages/pds/tests/image/server.test.ts | 127 ----------- packages/pds/tests/image/uri.test.ts | 204 ------------------ packages/pds/tests/moderation.test.ts | 21 +- 12 files changed, 5 insertions(+), 870 deletions(-) delete mode 100644 packages/pds/src/image/invalidator.ts delete mode 100644 packages/pds/src/image/logger.ts delete mode 100644 packages/pds/src/image/server.ts delete mode 100644 packages/pds/src/image/uri.ts delete mode 100644 packages/pds/tests/image/server.test.ts delete mode 100644 packages/pds/tests/image/uri.test.ts diff --git a/packages/pds/src/config.ts b/packages/pds/src/config.ts index c67c322e50c..816aa511873 100644 --- a/packages/pds/src/config.ts +++ b/packages/pds/src/config.ts @@ -38,11 +38,6 @@ export interface ServerConfigValues { availableUserDomains: string[] handleResolveNameservers?: string[] - imgUriSalt: string - imgUriKey: string - imgUriEndpoint?: string - blobCacheLocation?: string - rateLimitsEnabled: boolean rateLimitBypassKey?: string rateLimitBypassIps?: string[] @@ -153,14 +148,6 @@ export class ServerConfig { ? process.env.HANDLE_RESOLVE_NAMESERVERS.split(',') : [] - const imgUriSalt = - process.env.IMG_URI_SALT || '9dd04221f5755bce5f55f47464c27e1e' - const imgUriKey = - process.env.IMG_URI_KEY || - 'f23ecd142835025f42c3db2cf25dd813956c178392760256211f9d315f8ab4d8' - const imgUriEndpoint = process.env.IMG_URI_ENDPOINT - const blobCacheLocation = process.env.BLOB_CACHE_LOC - const rateLimitsEnabled = process.env.RATE_LIMITS_ENABLED === 'true' const rateLimitBypassKey = nonemptyString(process.env.RATE_LIMIT_BYPASS_KEY) const rateLimitBypassIpsStr = nonemptyString( @@ -274,10 +261,6 @@ export class ServerConfig { databaseLocation, availableUserDomains, handleResolveNameservers, - imgUriSalt, - imgUriKey, - imgUriEndpoint, - blobCacheLocation, rateLimitsEnabled, rateLimitBypassKey, rateLimitBypassIps, @@ -444,22 +427,6 @@ export class ServerConfig { return this.cfg.handleResolveNameservers } - get imgUriSalt() { - return this.cfg.imgUriSalt - } - - get imgUriKey() { - return this.cfg.imgUriKey - } - - get imgUriEndpoint() { - return this.cfg.imgUriEndpoint - } - - get blobCacheLocation() { - return this.cfg.blobCacheLocation - } - get rateLimitsEnabled() { return this.cfg.rateLimitsEnabled } diff --git a/packages/pds/src/context.ts b/packages/pds/src/context.ts index 04edb9647dd..ea0990955e2 100644 --- a/packages/pds/src/context.ts +++ b/packages/pds/src/context.ts @@ -10,7 +10,6 @@ import * as auth from './auth' import { ServerMailer } from './mailer' import { ModerationMailer } from './mailer/moderation' import { BlobStore } from '@atproto/repo' -import { ImageUriBuilder } from './image/uri' import { Services } from './services' import { MessageDispatcher } from './event-stream/message-queue' import { Sequencer, SequencerLeader } from './sequencer' @@ -32,7 +31,6 @@ export class AppContext { idResolver: IdResolver didCache: DidSqlCache auth: auth.ServerAuth - imgUriBuilder: ImageUriBuilder cfg: ServerConfig mailer: ServerMailer moderationMailer: ModerationMailer @@ -101,10 +99,6 @@ export class AppContext { return auth.optionalAccessOrRoleVerifier(this.auth) } - get imgUriBuilder(): ImageUriBuilder { - return this.opts.imgUriBuilder - } - get cfg(): ServerConfig { return this.opts.cfg } diff --git a/packages/pds/src/image/invalidator.ts b/packages/pds/src/image/invalidator.ts deleted file mode 100644 index d1319951500..00000000000 --- a/packages/pds/src/image/invalidator.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { BlobCache } from './server' - -// Invalidation is a general interface for propagating an image blob -// takedown through any caches where a representation of it may be stored. -// @NOTE this does not remove the blob from storage: just invalidates it from caches. -// @NOTE keep in sync with same interface in aws/src/cloudfront.ts -export interface ImageInvalidator { - invalidate(subject: string, paths: string[]): Promise -} - -export class ImageProcessingServerInvalidator implements ImageInvalidator { - constructor(private cache: BlobCache) {} - async invalidate(_subject: string, paths: string[]) { - const results = await Promise.allSettled( - paths.map(async (path) => { - const [, signature] = path.split('/') - if (!signature) throw new Error('Missing signature') - await this.cache.clear(signature) - }), - ) - const rejection = results.find( - (result): result is PromiseRejectedResult => result.status === 'rejected', - ) - if (rejection) throw rejection.reason - } -} diff --git a/packages/pds/src/image/logger.ts b/packages/pds/src/image/logger.ts deleted file mode 100644 index f4bcb5c9a66..00000000000 --- a/packages/pds/src/image/logger.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { subsystemLogger } from '@atproto/common' - -export const logger = subsystemLogger('pds:image') - -export default logger diff --git a/packages/pds/src/image/server.ts b/packages/pds/src/image/server.ts deleted file mode 100644 index 8e52d5d1fdf..00000000000 --- a/packages/pds/src/image/server.ts +++ /dev/null @@ -1,170 +0,0 @@ -import fs from 'fs/promises' -import fsSync from 'fs' -import os from 'os' -import path from 'path' -import { Readable } from 'stream' -import express, { ErrorRequestHandler, NextFunction } from 'express' -import createError, { isHttpError } from 'http-errors' -import { BlobNotFoundError, BlobStore } from '@atproto/repo' -import { - cloneStream, - forwardStreamErrors, - isErrnoException, -} from '@atproto/common' -import { BadPathError, ImageUriBuilder } from './uri' -import log from './logger' -import { resize } from './sharp' -import { formatsToMimes, Options } from './util' - -export class ImageProcessingServer { - app = express() - uriBuilder: ImageUriBuilder - - constructor( - protected salt: string | Uint8Array, - protected key: string | Uint8Array, - protected storage: BlobStore, - public cache: BlobCache, - ) { - this.uriBuilder = new ImageUriBuilder('', salt, key) - this.app.get('*', this.handler.bind(this)) - this.app.use(errorMiddleware) - } - - async handler( - req: express.Request, - res: express.Response, - next: NextFunction, - ) { - try { - const path = req.path - const options = this.uriBuilder.getVerifiedOptions(path) - - // Cached flow - - try { - const cachedImage = await this.cache.get(options.signature) - res.statusCode = 200 - res.setHeader('x-cache', 'hit') - res.setHeader('content-type', getMime(options.format)) - res.setHeader('cache-control', `public, max-age=31536000`) // 1 year - res.setHeader('content-length', cachedImage.size) - forwardStreamErrors(cachedImage, res) - return cachedImage.pipe(res) - } catch (err) { - // Ignore BlobNotFoundError and move on to non-cached flow - if (!(err instanceof BlobNotFoundError)) throw err - } - - // Non-cached flow - - const imageStream = await this.storage.getStream(options.cid) - const processedImage = await resize(imageStream, options) - - // Cache in the background - this.cache - .put(options.signature, cloneStream(processedImage)) - .catch((err) => log.error(err, 'failed to cache image')) - // Respond - res.statusCode = 200 - res.setHeader('x-cache', 'miss') - res.setHeader('content-type', getMime(options.format)) - res.setHeader('cache-control', `public, max-age=31536000`) // 1 year - forwardStreamErrors(processedImage, res) - return ( - processedImage - // @NOTE sharp does emit this in time to be set as a header - .once('info', (info) => res.setHeader('content-length', info.size)) - .pipe(res) - ) - } catch (err: unknown) { - if (err instanceof BadPathError) { - return next(createError(400, err)) - } - if (err instanceof BlobNotFoundError) { - return next(createError(404, 'Image not found')) - } - return next(err) - } - } -} - -const errorMiddleware: ErrorRequestHandler = function (err, _req, res, next) { - if (isHttpError(err)) { - log.error(err, `error: ${err.message}`) - } else { - log.error(err, 'unhandled exception') - } - if (res.headersSent) { - return next(err) - } - const httpError = createError(err) - return res.status(httpError.status).json({ - message: httpError.expose ? httpError.message : 'Internal Server Error', - }) -} - -function getMime(format: Options['format']) { - const mime = formatsToMimes[format] - if (!mime) throw new Error('Unknown format') - return mime -} - -export interface BlobCache { - get(fileId: string): Promise - put(fileId: string, stream: Readable): Promise - clear(fileId: string): Promise - clearAll(): Promise -} - -export class BlobDiskCache implements BlobCache { - tempDir: string - constructor(basePath?: string) { - this.tempDir = basePath || path.join(os.tmpdir(), 'pds--processed-images') - if (!path.isAbsolute(this.tempDir)) { - throw new Error('Must provide an absolute path') - } - try { - fsSync.mkdirSync(this.tempDir, { recursive: true }) - } catch (err) { - // All good if cache dir already exists - if (isErrnoException(err) && err.code === 'EEXIST') return - } - } - - async get(fileId: string) { - const filePath = path.join(this.tempDir, fileId) - try { - const { size } = await fs.stat(filePath) - if (size === 0) { - throw new BlobNotFoundError() - } - return Object.assign(fsSync.createReadStream(filePath), { size }) - } catch (err) { - if (isErrnoException(err) && err.code === 'ENOENT') { - throw new BlobNotFoundError() - } - throw err - } - } - - async put(fileId: string, stream: Readable) { - const filename = path.join(this.tempDir, fileId) - try { - await fs.writeFile(filename, stream, { flag: 'wx' }) - } catch (err) { - // Do not overwrite existing file, just ignore the error - if (isErrnoException(err) && err.code === 'EEXIST') return - throw err - } - } - - async clear(fileId: string) { - const filename = path.join(this.tempDir, fileId) - await fs.rm(filename, { force: true }) - } - - async clearAll() { - await fs.rm(this.tempDir, { recursive: true, force: true }) - } -} diff --git a/packages/pds/src/image/uri.ts b/packages/pds/src/image/uri.ts deleted file mode 100644 index 227a4ebb94b..00000000000 --- a/packages/pds/src/image/uri.ts +++ /dev/null @@ -1,202 +0,0 @@ -import { createHmac } from 'crypto' -import * as uint8arrays from 'uint8arrays' -import { CID } from 'multiformats/cid' -import { Options } from './util' - -// @NOTE if there are any additions here, ensure to include them on ImageUriBuilder.commonSignedUris -type CommonSignedUris = 'avatar' | 'banner' | 'feed_thumbnail' | 'feed_fullsize' - -export class ImageUriBuilder { - public endpoint: string - private salt: Uint8Array - private key: Uint8Array - - constructor( - endpoint: string, - salt: Uint8Array | string, - key: Uint8Array | string, - ) { - this.endpoint = endpoint - this.salt = - typeof salt === 'string' ? uint8arrays.fromString(salt, 'hex') : salt - this.key = - typeof key === 'string' ? uint8arrays.fromString(key, 'hex') : key - } - - getSignedPath(opts: Options & { cid: CID }): string { - const path = ImageUriBuilder.getPath(opts) - const saltedPath = uint8arrays.concat([ - this.salt, - uint8arrays.fromString(path), - ]) - const sig = hmac(this.key, saltedPath).toString('base64url') - return `/${sig}${path}` - } - - getSignedUri(opts: Options & { cid: CID }): string { - const path = this.getSignedPath(opts) - return this.endpoint + path - } - - static commonSignedUris: CommonSignedUris[] = [ - 'avatar', - 'banner', - 'feed_thumbnail', - 'feed_fullsize', - ] - - getCommonSignedUri(id: CommonSignedUris, cid: string | CID): string { - if (id === 'avatar') { - return this.getSignedUri({ - cid: typeof cid === 'string' ? CID.parse(cid) : cid, - format: 'jpeg', - fit: 'cover', - height: 1000, - width: 1000, - min: true, - }) - } else if (id === 'banner') { - return this.getSignedUri({ - cid: typeof cid === 'string' ? CID.parse(cid) : cid, - format: 'jpeg', - fit: 'cover', - height: 1000, - width: 3000, - min: true, - }) - } else if (id === 'feed_fullsize') { - return this.getSignedUri({ - cid: typeof cid === 'string' ? CID.parse(cid) : cid, - format: 'jpeg', - fit: 'inside', - height: 2000, - width: 2000, - min: true, - }) - } else if (id === 'feed_thumbnail') { - return this.getSignedUri({ - cid: typeof cid === 'string' ? CID.parse(cid) : cid, - format: 'jpeg', - fit: 'inside', - height: 1000, - width: 1000, - min: true, - }) - } else { - const exhaustiveCheck: never = id - throw new Error( - `Unrecognized requested common uri type: ${exhaustiveCheck}`, - ) - } - } - - getVerifiedOptions(path: string): Options & { cid: CID; signature: string } { - if (path.at(0) !== '/') { - throw new BadPathError('Invalid path: does not start with a slash') - } - const pathParts = path.split('/') // ['', sig, 'rs:fill:...', ...] - const [sig] = pathParts.splice(1, 1) // ['', 'rs:fill:...', ...] - const unsignedPath = pathParts.join('/') - if (!sig || sig.includes(':')) { - throw new BadPathError('Invalid path: missing signature') - } - const saltedPath = uint8arrays.concat([ - this.salt, - uint8arrays.fromString(unsignedPath), - ]) - const validSig = hmac(this.key, saltedPath).toString('base64url') - if (sig !== validSig) { - throw new BadPathError('Invalid path: bad signature') - } - const options = ImageUriBuilder.getOptions(unsignedPath) - return { - signature: validSig, - ...options, - } - } - - static getPath(opts: Options & { cid: CID }) { - const fit = opts.fit === 'inside' ? 'fit' : 'fill' // fit default is 'cover' - const enlarge = opts.min === true ? 1 : 0 // min default is false - const resize = `rs:${fit}:${opts.width}:${opts.height}:${enlarge}:0` // final ':0' is for interop with imgproxy - const minWidth = - opts.min && typeof opts.min === 'object' ? `mw:${opts.min.width}` : null - const minHeight = - opts.min && typeof opts.min === 'object' ? `mh:${opts.min.height}` : null - const quality = opts.quality ? `q:${opts.quality}` : null - return ( - `/` + - [resize, minWidth, minHeight, quality].filter(Boolean).join('/') + - `/plain/${opts.cid.toString()}@${opts.format}` - ) - } - - static getOptions(path: string): Options & { cid: CID } { - if (path.at(0) !== '/') { - throw new BadPathError('Invalid path: does not start with a slash') - } - const parts = path.split('/') - if (parts.at(-2) !== 'plain') { - throw new BadPathError('Invalid path') - } - const cidAndFormat = parts.at(-1) - const [cid, format, ...others] = cidAndFormat?.split('@') ?? [] - if (!cid || (format !== 'png' && format !== 'jpeg') || others.length) { - throw new BadPathError('Invalid path: bad cid/format part') - } - const resizePart = parts.find((part) => part.startsWith('rs:')) - const qualityPart = parts.find((part) => part.startsWith('q:')) - const minWidthPart = parts.find((part) => part.startsWith('mw:')) - const minHeightPart = parts.find((part) => part.startsWith('mh:')) - const [, fit, width, height, enlarge] = resizePart?.split(':') ?? [] - const [, quality] = qualityPart?.split(':') ?? [] - const [, minWidth] = minWidthPart?.split(':') ?? [] - const [, minHeight] = minHeightPart?.split(':') ?? [] - if (fit !== 'fill' && fit !== 'fit') { - throw new BadPathError('Invalid path: bad resize fit param') - } - if (isNaN(toInt(width)) || isNaN(toInt(height))) { - throw new BadPathError('Invalid path: bad resize height/width param') - } - if (enlarge !== '0' && enlarge !== '1') { - throw new BadPathError('Invalid path: bad resize enlarge param') - } - if (quality && isNaN(toInt(quality))) { - throw new BadPathError('Invalid path: bad quality param') - } - if ( - (!minWidth && minHeight) || - (minWidth && !minHeight) || - (minWidth && isNaN(toInt(minWidth))) || - (minHeight && isNaN(toInt(minHeight))) || - (enlarge === '1' && (minHeight || minHeight)) - ) { - throw new BadPathError('Invalid path: bad min width/height param') - } - return { - cid: CID.parse(cid), - format, - height: toInt(height), - width: toInt(width), - fit: fit === 'fill' ? 'cover' : 'inside', - quality: quality ? toInt(quality) : undefined, - min: - minWidth && minHeight - ? { width: toInt(minWidth), height: toInt(minHeight) } - : enlarge === '1', - } - } -} - -export class BadPathError extends Error {} - -function toInt(str: string) { - if (!/^\d+$/.test(str)) { - return NaN // String must be all numeric - } - return parseInt(str, 10) -} - -function hmac(key: Uint8Array, message: Uint8Array) { - return createHmac('sha256', key).update(message).digest() -} diff --git a/packages/pds/src/index.ts b/packages/pds/src/index.ts index 813b23209cc..36f15adae8e 100644 --- a/packages/pds/src/index.ts +++ b/packages/pds/src/index.ts @@ -36,16 +36,10 @@ import { ServerMailer } from './mailer' import { ModerationMailer } from './mailer/moderation' import { createServer } from './lexicon' import { MessageDispatcher } from './event-stream/message-queue' -import { ImageUriBuilder } from './image/uri' -import { BlobDiskCache, ImageProcessingServer } from './image/server' import { createServices } from './services' import { createHttpTerminator, HttpTerminator } from 'http-terminator' import AppContext from './context' import { Sequencer, SequencerLeader } from './sequencer' -import { - ImageInvalidator, - ImageProcessingServerInvalidator, -} from './image/invalidator' import { Labeler, HiveLabeler, KeywordLabeler } from './labeler' import { BackgroundQueue } from './event-stream/background-queue' import DidSqlCache from './did-cache' @@ -78,13 +72,11 @@ export class PDS { static create(opts: { db: Database blobstore: BlobStore - imgInvalidator?: ImageInvalidator repoSigningKey: crypto.Keypair plcRotationKey: crypto.Keypair config: ServerConfig }): PDS { const { db, blobstore, repoSigningKey, plcRotationKey, config } = opts - let maybeImgInvalidator = opts.imgInvalidator const auth = new ServerAuth({ jwtSecret: config.jwtSecret, adminPass: config.adminPassword, @@ -131,35 +123,6 @@ export class PDS { app.use(loggerMiddleware) app.use(compression()) - let imgUriEndpoint = config.imgUriEndpoint - if (!imgUriEndpoint) { - const imgProcessingCache = new BlobDiskCache(config.blobCacheLocation) - const imgProcessingServer = new ImageProcessingServer( - config.imgUriSalt, - config.imgUriKey, - blobstore, - imgProcessingCache, - ) - maybeImgInvalidator ??= new ImageProcessingServerInvalidator( - imgProcessingCache, - ) - app.use('/image', imgProcessingServer.app) - imgUriEndpoint = `${config.publicUrl}/image` - } - - let imgInvalidator: ImageInvalidator - if (maybeImgInvalidator) { - imgInvalidator = maybeImgInvalidator - } else { - throw new Error('Missing PDS image invalidator') - } - - const imgUriBuilder = new ImageUriBuilder( - imgUriEndpoint, - config.imgUriSalt, - config.imgUriKey, - ) - const backgroundQueue = new BackgroundQueue(db) const crawlers = new Crawlers( config.hostname, @@ -193,8 +156,6 @@ export class PDS { repoSigningKey, messageDispatcher, blobstore, - imgUriBuilder, - imgInvalidator, labeler, labelCache, appviewAgent, @@ -233,7 +194,6 @@ export class PDS { services, mailer, moderationMailer, - imgUriBuilder, backgroundQueue, appviewAgent, crawlers, diff --git a/packages/pds/src/services/index.ts b/packages/pds/src/services/index.ts index 757808df198..a60451785ec 100644 --- a/packages/pds/src/services/index.ts +++ b/packages/pds/src/services/index.ts @@ -3,8 +3,6 @@ import * as crypto from '@atproto/crypto' import { BlobStore } from '@atproto/repo' import Database from '../db' import { MessageDispatcher } from '../event-stream/message-queue' -import { ImageUriBuilder } from '../image/uri' -import { ImageInvalidator } from '../image/invalidator' import { AccountService } from './account' import { AuthService } from './auth' import { RecordService } from './record' @@ -22,8 +20,6 @@ export function createServices(resources: { repoSigningKey: crypto.Keypair messageDispatcher: MessageDispatcher blobstore: BlobStore - imgUriBuilder: ImageUriBuilder - imgInvalidator: ImageInvalidator labeler: Labeler labelCache: LabelCache appviewAgent?: AtpAgent @@ -36,8 +32,6 @@ export function createServices(resources: { repoSigningKey, messageDispatcher, blobstore, - imgUriBuilder, - imgInvalidator, labeler, labelCache, appviewAgent, @@ -64,12 +58,7 @@ export function createServices(resources: { appviewDid, appviewCdnUrlPattern, ), - moderation: ModerationService.creator( - messageDispatcher, - blobstore, - imgUriBuilder, - imgInvalidator, - ), + moderation: ModerationService.creator(messageDispatcher, blobstore), appView: { indexing: IndexingService.creator(backgroundQueue), label: LabelService.creator(labelCache), diff --git a/packages/pds/src/services/moderation/index.ts b/packages/pds/src/services/moderation/index.ts index 0a5ba6d02b0..6104ffe728b 100644 --- a/packages/pds/src/services/moderation/index.ts +++ b/packages/pds/src/services/moderation/index.ts @@ -9,8 +9,6 @@ import { ModerationAction, ModerationReport } from '../../db/tables/moderation' import { RecordService } from '../record' import { ModerationViews } from './views' import SqlRepoStorage from '../../sql-repo-storage' -import { ImageInvalidator } from '../../image/invalidator' -import { ImageUriBuilder } from '../../image/uri' import { TAKEDOWN } from '../../lexicon/types/com/atproto/admin/defs' import { addHoursToDate } from '../../util/date' @@ -19,24 +17,11 @@ export class ModerationService { public db: Database, public messageDispatcher: MessageQueue, public blobstore: BlobStore, - public imgUriBuilder: ImageUriBuilder, - public imgInvalidator: ImageInvalidator, ) {} - static creator( - messageDispatcher: MessageQueue, - blobstore: BlobStore, - imgUriBuilder: ImageUriBuilder, - imgInvalidator: ImageInvalidator, - ) { + static creator(messageDispatcher: MessageQueue, blobstore: BlobStore) { return (db: Database) => - new ModerationService( - db, - messageDispatcher, - blobstore, - imgUriBuilder, - imgInvalidator, - ) + new ModerationService(db, messageDispatcher, blobstore) } views = new ModerationViews(this.db, this.messageDispatcher) @@ -497,14 +482,7 @@ export class ModerationService { .where('takedownId', 'is', null) .executeTakeFirst() await Promise.all( - info.blobCids.map(async (cid) => { - await this.blobstore.quarantine(cid) - const paths = ImageUriBuilder.commonSignedUris.map((id) => { - const uri = this.imgUriBuilder.getCommonSignedUri(id, cid) - return uri.replace(this.imgUriBuilder.endpoint, '') - }) - await this.imgInvalidator.invalidate(cid.toString(), paths) - }), + info.blobCids.map((cid) => this.blobstore.quarantine(cid)), ) } } diff --git a/packages/pds/tests/image/server.test.ts b/packages/pds/tests/image/server.test.ts deleted file mode 100644 index 76dc7f57978..00000000000 --- a/packages/pds/tests/image/server.test.ts +++ /dev/null @@ -1,127 +0,0 @@ -import * as http from 'http' -import os from 'os' -import path from 'path' -import fs from 'fs' -import { AddressInfo } from 'net' -import axios, { AxiosInstance } from 'axios' -import { getInfo } from '../../src/image/sharp' -import { BlobDiskCache, ImageProcessingServer } from '../../src/image/server' -import { DiskBlobStore } from '../../src' -import { cidForCbor } from '@atproto/common' -import { CID } from 'multiformats/cid' - -describe('image processing server', () => { - let server: ImageProcessingServer - let httpServer: http.Server - let client: AxiosInstance - - let fileCid: CID - - beforeAll(async () => { - const salt = '9dd04221f5755bce5f55f47464c27e1e' - const key = - 'f23ecd142835025f42c3db2cf25dd813956c178392760256211f9d315f8ab4d8' - const storage = await DiskBlobStore.create( - path.join(os.tmpdir(), 'img-processing-tests'), - ) - // this CID isn't accurate for the data, but it works for the sake of the test - fileCid = await cidForCbor('key-landscape-small') - await storage.putPermanent( - fileCid, - fs.createReadStream('tests/image/fixtures/key-landscape-small.jpg'), - ) - const cache = new BlobDiskCache() - server = new ImageProcessingServer(salt, key, storage, cache) - httpServer = server.app.listen() - const { port } = httpServer.address() as AddressInfo - client = axios.create({ - baseURL: `http://localhost:${port}`, - validateStatus: () => true, - }) - }) - - afterAll(async () => { - if (httpServer) httpServer.close() - if (server) await server.cache.clearAll() - }) - - it('processes image from storage.', async () => { - const res = await client.get( - server.uriBuilder.getSignedPath({ - cid: fileCid, - format: 'jpeg', - fit: 'cover', - width: 500, - height: 500, - min: true, - }), - { responseType: 'stream' }, - ) - - const info = await getInfo(res.data) - expect(info).toEqual( - expect.objectContaining({ - height: 500, - width: 500, - size: 67221, - }), - ) - expect(res.headers).toEqual( - expect.objectContaining({ - 'content-type': 'image/jpeg', - 'cache-control': 'public, max-age=31536000', - 'content-length': '67221', - }), - ) - }) - - it('caches results.', async () => { - const path = server.uriBuilder.getSignedPath({ - cid: fileCid, - format: 'jpeg', - width: 25, // Special number for this test - height: 25, - }) - const res1 = await client.get(path, { responseType: 'arraybuffer' }) - expect(res1.headers['x-cache']).toEqual('miss') - const res2 = await client.get(path, { responseType: 'arraybuffer' }) - expect(res2.headers['x-cache']).toEqual('hit') - const res3 = await client.get(path, { responseType: 'arraybuffer' }) - expect(res3.headers['x-cache']).toEqual('hit') - expect(Buffer.compare(res1.data, res2.data)).toEqual(0) - expect(Buffer.compare(res1.data, res3.data)).toEqual(0) - }) - - it('errors on bad signature.', async () => { - const path = server.uriBuilder.getSignedPath({ - cid: fileCid, - format: 'jpeg', - fit: 'cover', - width: 500, - height: 500, - min: true, - }) - expect(path).toEqual( - `/G37yf764s6331dxOWiaOYEiLdg8OJxeE-RNxPDKB9Ck/rs:fill:500:500:1:0/plain/${fileCid.toString()}@jpeg`, - ) - const res = await client.get(path.replace('/G', '/bad_'), {}) - expect(res.status).toEqual(400) - expect(res.data).toEqual({ message: 'Invalid path: bad signature' }) - }) - - it('errors on missing file.', async () => { - const missingCid = await cidForCbor('missing-file') - const res = await client.get( - server.uriBuilder.getSignedPath({ - cid: missingCid, - format: 'jpeg', - fit: 'cover', - width: 500, - height: 500, - min: true, - }), - ) - expect(res.status).toEqual(404) - expect(res.data).toEqual({ message: 'Image not found' }) - }) -}) diff --git a/packages/pds/tests/image/uri.test.ts b/packages/pds/tests/image/uri.test.ts deleted file mode 100644 index ddf5468fe51..00000000000 --- a/packages/pds/tests/image/uri.test.ts +++ /dev/null @@ -1,204 +0,0 @@ -import { cidForCbor } from '@atproto/common' -import { CID } from 'multiformats/cid' -import { ImageUriBuilder, BadPathError } from '../../src/image/uri' - -describe('image uri builder', () => { - let uriBuilder: ImageUriBuilder - let cid: CID - - beforeAll(async () => { - const endpoint = 'https://example.com' - const salt = '9dd04221f5755bce5f55f47464c27e1e' - const key = - 'f23ecd142835025f42c3db2cf25dd813956c178392760256211f9d315f8ab4d8' - uriBuilder = new ImageUriBuilder(endpoint, salt, key) - cid = await cidForCbor('test cid') - }) - - it('signs and verifies uri options.', () => { - const path = uriBuilder.getSignedPath({ - cid, - format: 'png', - height: 200, - width: 300, - }) - expect(path).toEqual( - `/8Lpp5Y4ZQFkwTxDDgc1hz8haG6-lUBHsGsyNYoDEaXc/rs:fill:300:200:0:0/plain/${cid.toString()}@png`, - ) - expect(uriBuilder.getVerifiedOptions(path)).toEqual({ - signature: '8Lpp5Y4ZQFkwTxDDgc1hz8haG6-lUBHsGsyNYoDEaXc', - cid, - format: 'png', - fit: 'cover', - height: 200, - width: 300, - min: false, - }) - }) - - it('errors on bad signature.', () => { - const tryGetVerifiedOptions = (path) => () => - uriBuilder.getVerifiedOptions(path) - - tryGetVerifiedOptions( - // Confirm this is a good signed uri - `/BtHM_4IOak5MOc2gOPDxbfS4_HG6VPcry2OAV03L29g/rs:fill:300:200:0:0/plain/${cid.toString()}@png`, - ) - - expect( - tryGetVerifiedOptions( - // Tamper with signature - `/DtHM_4IOak5MOc2gOPDxbfS4_HG6VPcry2OAV03L29g/rs:fill:300:200:0:0/plain/${cid.toString()}@png`, - ), - ).toThrow(new BadPathError('Invalid path: bad signature')) - - expect( - tryGetVerifiedOptions( - // Tamper with params - `/DtHM_4IOak5MOc2gOPDxbfS4_HG6VPcry2OAV03L29g/rs:fill:300:200:0:0/plain/${cid.toString()}@jpeg`, - ), - ).toThrow(new BadPathError('Invalid path: bad signature')) - - expect( - tryGetVerifiedOptions( - // Missing signature - `/rs:fill:300:200:0:0/plain/${cid.toString()}@jpeg`, - ), - ).toThrow(new BadPathError('Invalid path: missing signature')) - }) - - it('supports basic options.', () => { - const path = ImageUriBuilder.getPath({ - cid, - format: 'png', - height: 200, - width: 300, - }) - expect(path).toEqual(`/rs:fill:300:200:0:0/plain/${cid.toString()}@png`) - expect(ImageUriBuilder.getOptions(path)).toEqual({ - cid, - format: 'png', - fit: 'cover', - height: 200, - width: 300, - min: false, - }) - }) - - it('supports fit option.', () => { - const path = ImageUriBuilder.getPath({ - cid, - format: 'png', - fit: 'inside', - height: 200, - width: 300, - }) - expect(path).toEqual(`/rs:fit:300:200:0:0/plain/${cid.toString()}@png`) - expect(ImageUriBuilder.getOptions(path)).toEqual({ - cid, - format: 'png', - fit: 'inside', - height: 200, - width: 300, - min: false, - }) - }) - - it('supports min=true option.', () => { - const path = ImageUriBuilder.getPath({ - cid, - format: 'png', - height: 200, - width: 300, - min: true, - }) - expect(path).toEqual(`/rs:fill:300:200:1:0/plain/${cid.toString()}@png`) - expect(ImageUriBuilder.getOptions(path)).toEqual({ - cid, - format: 'png', - fit: 'cover', - height: 200, - width: 300, - min: true, - }) - }) - - it('supports min={height,width} option.', () => { - const path = ImageUriBuilder.getPath({ - cid, - format: 'jpeg', - height: 200, - width: 300, - min: { height: 50, width: 100 }, - }) - expect(path).toEqual( - `/rs:fill:300:200:0:0/mw:100/mh:50/plain/${cid.toString()}@jpeg`, - ) - expect(ImageUriBuilder.getOptions(path)).toEqual({ - cid, - format: 'jpeg', - fit: 'cover', - height: 200, - width: 300, - min: { height: 50, width: 100 }, - }) - }) - - it('errors on bad cid/format part.', () => { - expect( - tryGetOptions(`/rs:fill:300:200:1:0/plain/${cid.toString()}@mp4`), - ).toThrow(new BadPathError('Invalid path: bad cid/format part')) - expect(tryGetOptions(`/rs:fill:300:200:1:0/plain/@jpg`)).toThrow( - new BadPathError('Invalid path: bad cid/format part'), - ) - expect( - tryGetOptions(`/rs:fill:300:200:1:0/plain/${cid.toString()}@`), - ).toThrow(new BadPathError('Invalid path: bad cid/format part')) - expect( - tryGetOptions(`/rs:fill:300:200:1:0/plain/${cid.toString()}@`), - ).toThrow(new BadPathError('Invalid path: bad cid/format part')) - expect( - tryGetOptions(`/rs:fill:300:200:1:0/plain/${cid.toString()}@x@jpeg`), - ).toThrow(new BadPathError('Invalid path: bad cid/format part')) - }) - - it('errors on mismatching min settings.', () => { - expect( - tryGetOptions( - `/rs:fill:300:200:1:0/mw:100/mh:50/plain/${cid.toString()}@jpeg`, - ), - ).toThrow(new BadPathError('Invalid path: bad min width/height param')) - expect( - tryGetOptions(`/rs:fill:300:200:0:0/mw:100/plain/${cid.toString()}@jpeg`), - ).toThrow(new BadPathError('Invalid path: bad min width/height param')) - }) - - it('errors on bad fit setting.', () => { - expect( - tryGetOptions(`/rs:blah:300:200:1:0/plain/${cid.toString()}@jpeg`), - ).toThrow(new BadPathError('Invalid path: bad resize fit param')) - }) - - it('errors on bad dimension settings.', () => { - expect( - tryGetOptions(`/rs:fill:30x:200:1:0/plain/${cid.toString()}@jpeg`), - ).toThrow(new BadPathError('Invalid path: bad resize height/width param')) - expect( - tryGetOptions(`/rs:fill:300:20x:1:0/plain/${cid.toString()}@jpeg`), - ).toThrow(new BadPathError('Invalid path: bad resize height/width param')) - expect( - tryGetOptions( - `/rs:fill:300:200:1:0/mw:10x/mh:50/plain/${cid.toString()}@jpeg`, - ), - ).toThrow(new BadPathError('Invalid path: bad min width/height param')) - expect( - tryGetOptions( - `/rs:fill:300:200:1:0/mw:100/mh:5x/plain/${cid.toString()}@jpeg`, - ), - ).toThrow(new BadPathError('Invalid path: bad min width/height param')) - }) - - function tryGetOptions(path: string) { - return () => ImageUriBuilder.getOptions(path) - } -}) diff --git a/packages/pds/tests/moderation.test.ts b/packages/pds/tests/moderation.test.ts index 0861d852aca..34e52a156a0 100644 --- a/packages/pds/tests/moderation.test.ts +++ b/packages/pds/tests/moderation.test.ts @@ -1130,18 +1130,11 @@ describe('moderation', () => { describe('blob takedown', () => { let post: { ref: RecordRef; images: ImageRef[] } let blob: ImageRef - let imageUri: string let actionId: number + beforeAll(async () => { post = sc.posts[sc.dids.carol][0] blob = post.images[1] - imageUri = server.ctx.imgUriBuilder - .getCommonSignedUri('feed_thumbnail', blob.image.ref.toString()) - .replace(server.ctx.cfg.publicUrl, server.url) - // Warm image server cache - await fetch(imageUri) - const cached = await fetch(imageUri) - expect(cached.headers.get('x-cache')).toEqual('hit') const takeAction = await agent.api.com.atproto.admin.takeModerationAction( { action: TAKEDOWN, @@ -1178,12 +1171,6 @@ describe('moderation', () => { await expect(referenceBlob).rejects.toThrow('Could not find blob:') }) - it('prevents image blob from being served, even when cached.', async () => { - const fetchImage = await fetch(imageUri) - expect(fetchImage.status).toEqual(404) - expect(await fetchImage.json()).toEqual({ message: 'Image not found' }) - }) - it('restores blob when action is reversed.', async () => { await agent.api.com.atproto.admin.reverseModerationAction( { @@ -1200,12 +1187,6 @@ describe('moderation', () => { // Can post and reference blob const post = await sc.post(sc.dids.alice, 'pic', [], [blob]) expect(post.images[0].image.ref.equals(blob.image.ref)).toBeTruthy() - - // Can fetch through image server - const fetchImage = await fetch(imageUri) - expect(fetchImage.status).toEqual(200) - const size = Number(fetchImage.headers.get('content-length')) - expect(size).toBeGreaterThan(9000) }) }) }) From 0fd089a8f400d4c6b0c068a638cfab3ba66e86bf Mon Sep 17 00:00:00 2001 From: dholms Date: Thu, 21 Sep 2023 15:05:22 -0500 Subject: [PATCH 12/31] fix dev envs --- packages/dev-env/src/pds.ts | 3 --- packages/pds/tests/_util.ts | 3 --- 2 files changed, 6 deletions(-) diff --git a/packages/dev-env/src/pds.ts b/packages/dev-env/src/pds.ts index 5a137e59e6f..e94130f41ff 100644 --- a/packages/dev-env/src/pds.ts +++ b/packages/dev-env/src/pds.ts @@ -56,9 +56,6 @@ export class TestPds { appUrlPasswordReset: 'app://forgot-password', emailNoReplyAddress: 'noreply@blueskyweb.xyz', publicUrl: 'https://pds.public.url', - imgUriSalt: '9dd04221f5755bce5f55f47464c27e1e', - imgUriKey: - 'f23ecd142835025f42c3db2cf25dd813956c178392760256211f9d315f8ab4d8', dbPostgresUrl: cfg.dbPostgresUrl, maxSubscriptionBuffer: 200, repoBackfillLimitMs: 1000 * 60 * 60, // 1hr diff --git a/packages/pds/tests/_util.ts b/packages/pds/tests/_util.ts index 67aadaeaf2c..16061b8af51 100644 --- a/packages/pds/tests/_util.ts +++ b/packages/pds/tests/_util.ts @@ -95,9 +95,6 @@ export const runTestServer = async ( appUrlPasswordReset: 'app://forgot-password', emailNoReplyAddress: 'noreply@blueskyweb.xyz', publicUrl: 'https://pds.public.url', - imgUriSalt: '9dd04221f5755bce5f55f47464c27e1e', - imgUriKey: - 'f23ecd142835025f42c3db2cf25dd813956c178392760256211f9d315f8ab4d8', dbPostgresUrl: process.env.DB_POSTGRES_URL, blobstoreLocation: `${blobstoreLoc}/blobs`, blobstoreTmp: `${blobstoreLoc}/tmp`, From acd5c20140997c402bcad1dd05c7d1b301b989be Mon Sep 17 00:00:00 2001 From: dholms Date: Thu, 21 Sep 2023 18:22:43 -0500 Subject: [PATCH 13/31] build branch --- .github/workflows/build-and-push-pds-aws.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-and-push-pds-aws.yaml b/.github/workflows/build-and-push-pds-aws.yaml index 097f782d88e..9c5a4cbad6b 100644 --- a/.github/workflows/build-and-push-pds-aws.yaml +++ b/.github/workflows/build-and-push-pds-aws.yaml @@ -3,6 +3,7 @@ on: push: branches: - main + - disable-pds-appview-routes env: REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }} USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }} From 9ce9a49a0cac3dfd674c19792984a7758a4fb3a9 Mon Sep 17 00:00:00 2001 From: dholms Date: Thu, 21 Sep 2023 21:03:32 -0500 Subject: [PATCH 14/31] wip - removing labeler --- packages/dev-env/src/pds.ts | 6 - packages/dev-env/src/types.ts | 2 - .../pds/src/app-view/services/label/index.ts | 171 ---------------- packages/pds/src/config.ts | 32 --- packages/pds/src/context.ts | 12 -- packages/pds/src/index.ts | 29 --- packages/pds/src/label-cache.ts | 90 --------- packages/pds/src/labeler/base.ts | 72 ------- packages/pds/src/labeler/hive.ts | 191 ------------------ packages/pds/src/labeler/index.ts | 3 - packages/pds/src/labeler/keyword.ts | 29 --- packages/pds/src/labeler/util.ts | 109 ---------- packages/pds/src/services/index.ts | 14 -- packages/pds/src/services/moderation/views.ts | 1 - packages/pds/src/services/repo/index.ts | 22 +- packages/pds/tests/_util.ts | 7 - 16 files changed, 1 insertion(+), 789 deletions(-) delete mode 100644 packages/pds/src/app-view/services/label/index.ts delete mode 100644 packages/pds/src/label-cache.ts delete mode 100644 packages/pds/src/labeler/base.ts delete mode 100644 packages/pds/src/labeler/hive.ts delete mode 100644 packages/pds/src/labeler/index.ts delete mode 100644 packages/pds/src/labeler/keyword.ts delete mode 100644 packages/pds/src/labeler/util.ts diff --git a/packages/dev-env/src/pds.ts b/packages/dev-env/src/pds.ts index f717b2740a5..2cdcadfe70c 100644 --- a/packages/dev-env/src/pds.ts +++ b/packages/dev-env/src/pds.ts @@ -59,9 +59,6 @@ export class TestPds { maxSubscriptionBuffer: 200, repoBackfillLimitMs: 1000 * 60 * 60, // 1hr sequencerLeaderLockId: uniqueLockId(), - labelerDid: 'did:example:labeler', - labelerKeywords: { label_me: 'test-label', label_me_2: 'test-label-2' }, - feedGenDid: 'did:example:feedGen', dbTxLockNonce: await randomStr(32, 'base32'), bskyAppViewEndpoint: cfg.bskyAppViewEndpoint ?? 'http://fake_address', bskyAppViewDid: cfg.bskyAppViewDid ?? 'did:example:fake', @@ -89,8 +86,6 @@ export class TestPds { await server.start() - // we refresh label cache by hand in `processAll` instead of on a timer - if (!cfg.enableLabelsCache) server.ctx.labelCache.stop() return new TestPds(url, port, server) } @@ -123,7 +118,6 @@ export class TestPds { async processAll() { await this.ctx.backgroundQueue.processAll() - await this.ctx.labelCache.fullRefresh() } async close() { diff --git a/packages/dev-env/src/types.ts b/packages/dev-env/src/types.ts index d87e78a679a..a1642f4751c 100644 --- a/packages/dev-env/src/types.ts +++ b/packages/dev-env/src/types.ts @@ -10,8 +10,6 @@ export type PlcConfig = { export type PdsConfig = Partial & { plcUrl: string migration?: string - enableInProcessAppView?: boolean - enableLabelsCache?: boolean } export type BskyConfig = Partial & { diff --git a/packages/pds/src/app-view/services/label/index.ts b/packages/pds/src/app-view/services/label/index.ts deleted file mode 100644 index 24ae99034c1..00000000000 --- a/packages/pds/src/app-view/services/label/index.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { sql } from 'kysely' -import { AtUri } from '@atproto/syntax' -import { toSimplifiedISOSafe } from '@atproto/common' -import Database from '../../../db' -import { - Label, - isSelfLabels, -} from '../../../lexicon/types/com/atproto/label/defs' -import { ids } from '../../../lexicon/lexicons' -import { LabelCache } from '../../../label-cache' - -export type Labels = Record - -export class LabelService { - constructor(public db: Database, public cache: LabelCache) {} - - static creator(cache: LabelCache) { - return (db: Database) => new LabelService(db, cache) - } - - async formatAndCreate( - src: string, - uri: string, - cid: string | null, - labels: { create?: string[]; negate?: string[] }, - ) { - const { create = [], negate = [] } = labels - const toCreate = create.map((val) => ({ - src, - uri, - cid: cid ?? undefined, - val, - neg: false, - cts: new Date().toISOString(), - })) - const toNegate = negate.map((val) => ({ - src, - uri, - cid: cid ?? undefined, - val, - neg: true, - cts: new Date().toISOString(), - })) - await this.createLabels([...toCreate, ...toNegate]) - } - - async createLabels(labels: Label[]) { - if (labels.length < 1) return - const dbVals = labels.map((l) => ({ - ...l, - cid: l.cid ?? '', - neg: (l.neg ? 1 : 0) as 1 | 0, - })) - const { ref } = this.db.db.dynamic - const excluded = (col: string) => ref(`excluded.${col}`) - await this.db.db - .insertInto('label') - .values(dbVals) - .onConflict((oc) => - oc.columns(['src', 'uri', 'cid', 'val']).doUpdateSet({ - neg: sql`${excluded('neg')}`, - cts: sql`${excluded('cts')}`, - }), - ) - .execute() - } - - async getLabelsForUris( - subjects: string[], - opts?: { - includeNeg?: boolean - skipCache?: boolean - }, - ): Promise { - if (subjects.length < 1) return {} - const res = opts?.skipCache - ? await this.db.db - .selectFrom('label') - .where('label.uri', 'in', subjects) - .if(!opts?.includeNeg, (qb) => qb.where('neg', '=', 0)) - .selectAll() - .execute() - : this.cache.forSubjects(subjects, opts?.includeNeg) - return res.reduce((acc, cur) => { - acc[cur.uri] ??= [] - acc[cur.uri].push({ - ...cur, - cid: cur.cid === '' ? undefined : cur.cid, - neg: cur.neg === 1, // @TODO update in appview - }) - return acc - }, {} as Labels) - } - - // gets labels for any record. when did is present, combine labels for both did & profile record. - async getLabelsForSubjects( - subjects: string[], - opts?: { - includeNeg?: boolean - skipCache?: boolean - }, - ): Promise { - if (subjects.length < 1) return {} - const expandedSubjects = subjects.flatMap((subject) => { - if (subject.startsWith('did:')) { - return [ - subject, - AtUri.make(subject, ids.AppBskyActorProfile, 'self').toString(), - ] - } - return subject - }) - const labels = await this.getLabelsForUris(expandedSubjects, opts) - return Object.keys(labels).reduce((acc, cur) => { - const uri = cur.startsWith('at://') ? new AtUri(cur) : null - if ( - uri && - uri.collection === ids.AppBskyActorProfile && - uri.rkey === 'self' - ) { - // combine labels for profile + did - const did = uri.hostname - acc[did] ??= [] - acc[did].push(...labels[cur]) - } - acc[cur] ??= [] - acc[cur].push(...labels[cur]) - return acc - }, {} as Labels) - } - - async getLabels( - subject: string, - opts?: { - includeNeg?: boolean - skipCache?: boolean - }, - ): Promise { - const labels = await this.getLabelsForUris([subject], opts) - return labels[subject] ?? [] - } - - async getLabelsForProfile( - did: string, - opts?: { - includeNeg?: boolean - skipCache?: boolean - }, - ): Promise { - const labels = await this.getLabelsForSubjects([did], opts) - return labels[did] ?? [] - } -} - -export function getSelfLabels(details: { - uri: string | null - cid: string | null - record: Record | null -}): Label[] { - const { uri, cid, record } = details - if (!uri || !cid || !record) return [] - if (!isSelfLabels(record.labels)) return [] - const src = new AtUri(uri).host // record creator - const cts = - typeof record.createdAt === 'string' - ? toSimplifiedISOSafe(record.createdAt) - : new Date(0).toISOString() - return record.labels.values.map(({ val }) => { - return { src, uri, cid, val, cts, neg: false } - }) -} diff --git a/packages/pds/src/config.ts b/packages/pds/src/config.ts index 816aa511873..f8d7b04d8fa 100644 --- a/packages/pds/src/config.ts +++ b/packages/pds/src/config.ts @@ -50,12 +50,6 @@ export interface ServerConfigValues { moderationEmailAddress?: string moderationEmailSmtpUrl?: string - hiveApiKey?: string - labelerDid: string - labelerKeywords: Record - - feedGenDid?: string - maxSubscriptionBuffer: number repoBackfillLimitMs: number sequencerLeaderLockId?: number @@ -179,12 +173,6 @@ export class ServerConfig { const moderationEmailSmtpUrl = process.env.MODERATION_EMAIL_SMTP_URL || undefined - const hiveApiKey = process.env.HIVE_API_KEY || undefined - const labelerDid = process.env.LABELER_DID || 'did:example:labeler' - const labelerKeywords = {} - - const feedGenDid = process.env.FEED_GEN_DID - const dbPostgresUrl = process.env.DB_POSTGRES_URL const dbPostgresSchema = process.env.DB_POSTGRES_SCHEMA @@ -271,10 +259,6 @@ export class ServerConfig { emailNoReplyAddress, moderationEmailAddress, moderationEmailSmtpUrl, - hiveApiKey, - labelerDid, - labelerKeywords, - feedGenDid, maxSubscriptionBuffer, repoBackfillLimitMs, sequencerLeaderLockId, @@ -467,22 +451,6 @@ export class ServerConfig { return this.cfg.moderationEmailSmtpUrl } - get hiveApiKey() { - return this.cfg.hiveApiKey - } - - get labelerDid() { - return this.cfg.labelerDid - } - - get labelerKeywords() { - return this.cfg.labelerKeywords - } - - get feedGenDid() { - return this.cfg.feedGenDid - } - get maxSubscriptionBuffer() { return this.cfg.maxSubscriptionBuffer } diff --git a/packages/pds/src/context.ts b/packages/pds/src/context.ts index d06134fddc6..791100c492b 100644 --- a/packages/pds/src/context.ts +++ b/packages/pds/src/context.ts @@ -12,11 +12,9 @@ import { ModerationMailer } from './mailer/moderation' import { BlobStore } from '@atproto/repo' import { Services } from './services' import { Sequencer, SequencerLeader } from './sequencer' -import { Labeler } from './labeler' import { BackgroundQueue } from './background' import DidSqlCache from './did-cache' import { Crawlers } from './crawlers' -import { LabelCache } from './label-cache' import { RuntimeFlags } from './runtime-flags' export class AppContext { @@ -36,8 +34,6 @@ export class AppContext { services: Services sequencer: Sequencer sequencerLeader: SequencerLeader | null - labeler: Labeler - labelCache: LabelCache runtimeFlags: RuntimeFlags backgroundQueue: BackgroundQueue appviewAgent: AtpAgent @@ -121,14 +117,6 @@ export class AppContext { return this.opts.sequencerLeader } - get labeler(): Labeler { - return this.opts.labeler - } - - get labelCache(): LabelCache { - return this.opts.labelCache - } - get runtimeFlags(): RuntimeFlags { return this.opts.runtimeFlags } diff --git a/packages/pds/src/index.ts b/packages/pds/src/index.ts index f70cc82eafe..fb08ef375e9 100644 --- a/packages/pds/src/index.ts +++ b/packages/pds/src/index.ts @@ -38,11 +38,9 @@ import { createServices } from './services' import { createHttpTerminator, HttpTerminator } from 'http-terminator' import AppContext from './context' import { Sequencer, SequencerLeader } from './sequencer' -import { Labeler, HiveLabeler, KeywordLabeler } from './labeler' import { BackgroundQueue } from './background' import DidSqlCache from './did-cache' import { Crawlers } from './crawlers' -import { LabelCache } from './label-cache' import { getRedisClient } from './redis' import { RuntimeFlags } from './runtime-flags' @@ -126,34 +124,11 @@ export class PDS { config.crawlersToNotify ?? [], ) - let labeler: Labeler - if (config.hiveApiKey) { - labeler = new HiveLabeler({ - db, - blobstore, - backgroundQueue, - labelerDid: config.labelerDid, - hiveApiKey: config.hiveApiKey, - keywords: config.labelerKeywords, - }) - } else { - labeler = new KeywordLabeler({ - db, - blobstore, - backgroundQueue, - labelerDid: config.labelerDid, - keywords: config.labelerKeywords, - }) - } - - const labelCache = new LabelCache(db) const appviewAgent = new AtpAgent({ service: config.bskyAppViewEndpoint }) const services = createServices({ repoSigningKey, blobstore, - labeler, - labelCache, appviewAgent, appviewDid: config.bskyAppViewDid, appviewCdnUrlPattern: config.bskyAppViewCdnUrlPattern, @@ -183,8 +158,6 @@ export class PDS { auth, sequencer, sequencerLeader, - labeler, - labelCache, runtimeFlags, services, mailer, @@ -291,7 +264,6 @@ export class PDS { this.ctx.sequencerLeader?.run() await this.ctx.sequencer.start() await this.ctx.db.startListeningToChannels() - this.ctx.labelCache.start() await this.ctx.runtimeFlags.start() const server = this.app.listen(this.ctx.cfg.port) this.server = server @@ -303,7 +275,6 @@ export class PDS { async destroy(): Promise { await this.ctx.runtimeFlags.destroy() - this.ctx.labelCache.stop() await this.ctx.sequencerLeader?.destroy() await this.terminator?.terminate() await this.ctx.backgroundQueue.destroy() diff --git a/packages/pds/src/label-cache.ts b/packages/pds/src/label-cache.ts deleted file mode 100644 index e4f23daa599..00000000000 --- a/packages/pds/src/label-cache.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { wait } from '@atproto/common' -import Database from './db' -import { Label } from './db/tables/label' -import { labelerLogger as log } from './logger' - -export class LabelCache { - bySubject: Record = {} - latestLabel = '' - refreshes = 0 - - destroyed = false - - constructor(public db: Database) {} - - start() { - this.poll() - } - - async fullRefresh() { - const allLabels = await this.db.db.selectFrom('label').selectAll().execute() - this.wipeCache() - this.processLabels(allLabels) - } - - async partialRefresh() { - const labels = await this.db.db - .selectFrom('label') - .selectAll() - .where('cts', '>', this.latestLabel) - .execute() - this.processLabels(labels) - } - - async poll() { - try { - if (this.destroyed) return - if (this.refreshes >= 120) { - await this.fullRefresh() - this.refreshes = 0 - } else { - await this.partialRefresh() - this.refreshes++ - } - } catch (err) { - log.error( - { err, latestLabel: this.latestLabel, refreshes: this.refreshes }, - 'label cache failed to refresh', - ) - } - await wait(500) - this.poll() - } - - processLabels(labels: Label[]) { - for (const label of labels) { - if (label.cts > this.latestLabel) { - this.latestLabel = label.cts - } - this.bySubject[label.uri] ??= [] - this.bySubject[label.uri].push(label) - } - } - - wipeCache() { - this.bySubject = {} - } - - stop() { - this.destroyed = true - } - - forSubject(subject: string, includeNeg = false): Label[] { - const labels = this.bySubject[subject] ?? [] - return includeNeg ? labels : labels.filter((l) => l.neg === 0) - } - - forSubjects(subjects: string[], includeNeg?: boolean): Label[] { - let labels: Label[] = [] - const alreadyAdded = new Set() - for (const subject of subjects) { - if (alreadyAdded.has(subject)) { - continue - } - const subLabels = this.forSubject(subject, includeNeg) - labels = [...labels, ...subLabels] - alreadyAdded.add(subject) - } - return labels - } -} diff --git a/packages/pds/src/labeler/base.ts b/packages/pds/src/labeler/base.ts deleted file mode 100644 index b3efefad327..00000000000 --- a/packages/pds/src/labeler/base.ts +++ /dev/null @@ -1,72 +0,0 @@ -import Database from '../db' -import { BlobStore, cidForRecord } from '@atproto/repo' -import { dedupe, getFieldsFromRecord } from './util' -import { AtUri } from '@atproto/syntax' -import { labelerLogger as log } from '../logger' -import { BackgroundQueue } from '../background' -import { CID } from 'multiformats/cid' - -export abstract class Labeler { - public db: Database - public blobstore: BlobStore - public labelerDid: string - public backgroundQueue: BackgroundQueue - constructor(opts: { - db: Database - blobstore: BlobStore - labelerDid: string - backgroundQueue: BackgroundQueue - }) { - this.db = opts.db - this.blobstore = opts.blobstore - this.labelerDid = opts.labelerDid - this.backgroundQueue = opts.backgroundQueue - } - - processRecord(uri: AtUri, obj: unknown) { - this.backgroundQueue.add(() => - this.createAndStoreLabels(uri, obj).catch((err) => { - log.error( - { err, uri: uri.toString(), record: obj }, - 'failed to label record', - ) - }), - ) - } - - async createAndStoreLabels(uri: AtUri, obj: unknown): Promise { - const labels = await this.labelRecord(obj) - if (labels.length < 1) return - const cid = await cidForRecord(obj) - const rows = labels.map((val) => ({ - src: this.labelerDid, - uri: uri.toString(), - cid: cid.toString(), - val, - neg: 0 as const, - cts: new Date().toISOString(), - })) - - await this.db.db - .insertInto('label') - .values(rows) - .onConflict((oc) => oc.doNothing()) - .execute() - } - - async labelRecord(obj: unknown): Promise { - const { text, imgs } = getFieldsFromRecord(obj) - const txtLabels = await this.labelText(text.join(' ')) - const imgLabels = await Promise.all( - imgs.map(async (cid) => this.labelImg(cid)), - ) - return dedupe([...txtLabels, ...imgLabels.flat()]) - } - - abstract labelText(text: string): Promise - abstract labelImg(cid: CID): Promise - - async processAll() { - await this.backgroundQueue.processAll() - } -} diff --git a/packages/pds/src/labeler/hive.ts b/packages/pds/src/labeler/hive.ts deleted file mode 100644 index b14b84f3479..00000000000 --- a/packages/pds/src/labeler/hive.ts +++ /dev/null @@ -1,191 +0,0 @@ -import stream from 'stream' -import axios from 'axios' -import FormData from 'form-data' -import { Labeler } from './base' -import Database from '../db' -import { BlobStore } from '@atproto/repo' -import { keywordLabeling } from './util' -import { BackgroundQueue } from '../background' -import { CID } from 'multiformats/cid' - -const HIVE_ENDPOINT = 'https://api.thehive.ai/api/v2/task/sync' - -export class HiveLabeler extends Labeler { - hiveApiKey: string - keywords: Record - - constructor(opts: { - db: Database - blobstore: BlobStore - backgroundQueue: BackgroundQueue - labelerDid: string - hiveApiKey: string - keywords: Record - }) { - const { db, blobstore, backgroundQueue, labelerDid, hiveApiKey, keywords } = - opts - super({ db, blobstore, backgroundQueue, labelerDid }) - this.hiveApiKey = hiveApiKey - this.keywords = keywords - } - - async labelText(text: string): Promise { - return keywordLabeling(this.keywords, text) - } - - async labelImg(cid: CID): Promise { - const stream = await this.blobstore.getStream(cid) - return labelBlob(stream, this.hiveApiKey) - } -} - -export const labelBlob = async ( - blob: stream.Readable, - hiveApiKey: string, -): Promise => { - const classes = await makeHiveReq(blob, hiveApiKey) - return summarizeLabels(classes) -} - -export const makeHiveReq = async ( - blob: stream.Readable, - hiveApiKey: string, -): Promise => { - const form = new FormData() - form.append('media', blob) - const res = await axios.post(HIVE_ENDPOINT, form, { - headers: { - 'Content-Type': 'multipart/form-data', - authorization: `token ${hiveApiKey}`, - accept: 'application/json', - }, - }) - return respToClasses(res.data) -} - -export const respToClasses = (res: HiveResp): HiveRespClass[] => { - const classes: HiveRespClass[] = [] - for (const status of res.status) { - for (const out of status.response.output) { - for (const cls of out.classes) { - classes.push(cls) - } - } - } - return classes -} - -// Matches only one (or none) of: porn, sexual, nudity -// -// porn: sexual and nudity. including both explicit activity or full-frontal and suggestive/intent -// sexual: sexually suggestive, not explicit; may include some forms of nudity -// nudity: non-sexual nudity (eg, artistic, possibly some photographic) -// -// hive docs/definitions: https://docs.thehive.ai/docs/sexual-content -export const sexualLabels = (classes: HiveRespClass[]): string[] => { - const scores = {} - - for (const cls of classes) { - scores[cls.class] = cls.score - } - - // first check if porn... - for (const pornClass of [ - 'yes_sexual_activity', - 'animal_genitalia_and_human', - 'yes_realistic_nsfw', - ]) { - if (scores[pornClass] >= 0.9) { - return ['porn'] - } - } - if (scores['general_nsfw'] >= 0.9) { - // special case for some anime examples - if (scores['animated_animal_genitalia'] >= 0.5) { - return ['porn'] - } - // special case for some pornographic/explicit classic drawings - if (scores['yes_undressed'] >= 0.9 && scores['yes_sexual_activity'] > 0.9) { - return ['porn'] - } - } - - // then check for sexual suggestive (which may include nudity)... - for (const sexualClass of ['yes_sexual_intent', 'yes_sex_toy']) { - if (scores[sexualClass] >= 0.9) { - return ['sexual'] - } - } - if (scores['yes_undressed'] >= 0.9) { - // special case for bondage examples - if (scores['yes_sex_toy'] > 0.75) { - return ['sexual'] - } - } - - // then non-sexual nudity... - for (const nudityClass of [ - 'yes_male_nudity', - 'yes_female_nudity', - 'yes_undressed', - ]) { - if (scores[nudityClass] >= 0.9) { - return ['nudity'] - } - } - - // then finally flag remaining "underwear" images in to sexually suggestive - // (after non-sexual content already labeled above) - for (const nudityClass of ['yes_male_underwear', 'yes_female_underwear']) { - if (scores[nudityClass] >= 0.9) { - return ['sexual'] - } - } - - return [] -} - -// gore and violence: https://docs.thehive.ai/docs/class-descriptions-violence-gore -const labelForClass = { - very_bloody: 'gore', - human_corpse: 'corpse', - hanging: 'corpse', -} -const labelForClassLessSensitive = { - yes_self_harm: 'self-harm', -} - -export const summarizeLabels = (classes: HiveRespClass[]): string[] => { - const labels: string[] = sexualLabels(classes) - for (const cls of classes) { - if (labelForClass[cls.class] && cls.score >= 0.9) { - labels.push(labelForClass[cls.class]) - } - } - for (const cls of classes) { - if (labelForClassLessSensitive[cls.class] && cls.score >= 0.96) { - labels.push(labelForClassLessSensitive[cls.class]) - } - } - return labels -} - -type HiveResp = { - status: HiveRespStatus[] -} - -type HiveRespStatus = { - response: { - output: HiveRespOutput[] - } -} - -type HiveRespOutput = { - time: number - classes: HiveRespClass[] -} - -type HiveRespClass = { - class: string - score: number -} diff --git a/packages/pds/src/labeler/index.ts b/packages/pds/src/labeler/index.ts deleted file mode 100644 index cd6d2a64345..00000000000 --- a/packages/pds/src/labeler/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './base' -export * from './hive' -export * from './keyword' diff --git a/packages/pds/src/labeler/keyword.ts b/packages/pds/src/labeler/keyword.ts deleted file mode 100644 index 0a6bf9ca3d2..00000000000 --- a/packages/pds/src/labeler/keyword.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { BlobStore } from '@atproto/repo' -import Database from '../db' -import { Labeler } from './base' -import { keywordLabeling } from './util' -import { BackgroundQueue } from '../background' - -export class KeywordLabeler extends Labeler { - keywords: Record - - constructor(opts: { - db: Database - blobstore: BlobStore - backgroundQueue: BackgroundQueue - labelerDid: string - keywords: Record - }) { - const { db, blobstore, backgroundQueue, labelerDid, keywords } = opts - super({ db, blobstore, backgroundQueue, labelerDid }) - this.keywords = keywords - } - - async labelText(text: string): Promise { - return keywordLabeling(this.keywords, text) - } - - async labelImg(): Promise { - return [] - } -} diff --git a/packages/pds/src/labeler/util.ts b/packages/pds/src/labeler/util.ts deleted file mode 100644 index 4175886a542..00000000000 --- a/packages/pds/src/labeler/util.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { CID } from 'multiformats/cid' -import * as lex from '../lexicon/lexicons' -import { Record as PostRecord } from '../lexicon/types/app/bsky/feed/post' -import { Record as ProfileRecord } from '../lexicon/types/app/bsky/actor/profile' -import { isMain as isEmbedImage } from '../lexicon/types/app/bsky/embed/images' -import { isMain as isEmbedExternal } from '../lexicon/types/app/bsky/embed/external' -import { isMain as isEmbedRecordWithMedia } from '../lexicon/types/app/bsky/embed/recordWithMedia' - -type RecordFields = { - text: string[] - imgs: CID[] -} - -export const getFieldsFromRecord = (record: unknown): RecordFields => { - if (isPost(record)) { - return getFieldsFromPost(record) - // @TODO add back in profile labeling - // } else if (isProfile(record)) { - // return getFieldsFromProfile(record) - } else { - return { text: [], imgs: [] } - } -} - -export const getFieldsFromPost = (record: PostRecord): RecordFields => { - const text: string[] = [] - const imgs: CID[] = [] - text.push(record.text) - const embeds = separateEmbeds(record.embed) - for (const embed of embeds) { - if (isEmbedImage(embed)) { - for (const img of embed.images) { - imgs.push(img.image.ref) - text.push(img.alt) - } - } else if (isEmbedExternal(embed)) { - if (embed.external.thumb) { - imgs.push(embed.external.thumb.ref) - } - text.push(embed.external.title) - text.push(embed.external.description) - } - } - return { text, imgs } -} - -export const getFieldsFromProfile = (record: ProfileRecord): RecordFields => { - const text: string[] = [] - const imgs: CID[] = [] - if (record.displayName) { - text.push(record.displayName) - } - if (record.description) { - text.push(record.description) - } - if (record.avatar) { - imgs.push(record.avatar.ref) - } - if (record.banner) { - imgs.push(record.banner.ref) - } - return { text, imgs } -} - -export const dedupe = (str: string[]): string[] => { - const set = new Set(str) - return [...set] -} - -export const isPost = (obj: unknown): obj is PostRecord => { - return isRecordType(obj, 'app.bsky.feed.post') -} - -export const isProfile = (obj: unknown): obj is ProfileRecord => { - return isRecordType(obj, 'app.bsky.actor.profile') -} - -export const isRecordType = (obj: unknown, lexId: string): boolean => { - try { - lex.lexicons.assertValidRecord(lexId, obj) - return true - } catch { - return false - } -} - -export const keywordLabeling = ( - keywords: Record, - text: string, -): string[] => { - const lowerText = text.toLowerCase() - const labels: string[] = [] - for (const word of Object.keys(keywords)) { - if (lowerText.includes(word)) { - labels.push(keywords[word]) - } - } - return labels -} - -const separateEmbeds = (embed: PostRecord['embed']) => { - if (!embed) { - return [] - } - if (isEmbedRecordWithMedia(embed)) { - return [{ $type: lex.ids.AppBskyEmbedRecord, ...embed.record }, embed.media] - } - return [embed] -} diff --git a/packages/pds/src/services/index.ts b/packages/pds/src/services/index.ts index fc335827107..3dd376a8dd6 100644 --- a/packages/pds/src/services/index.ts +++ b/packages/pds/src/services/index.ts @@ -7,18 +7,13 @@ import { AuthService } from './auth' import { RecordService } from './record' import { RepoService } from './repo' import { ModerationService } from './moderation' -import { Labeler } from '../labeler' -import { LabelService } from '../app-view/services/label' import { BackgroundQueue } from '../background' import { Crawlers } from '../crawlers' -import { LabelCache } from '../label-cache' import { LocalService } from './local' export function createServices(resources: { repoSigningKey: crypto.Keypair blobstore: BlobStore - labeler: Labeler - labelCache: LabelCache appviewAgent?: AtpAgent appviewDid?: string appviewCdnUrlPattern?: string @@ -28,8 +23,6 @@ export function createServices(resources: { const { repoSigningKey, blobstore, - labeler, - labelCache, appviewAgent, appviewDid, appviewCdnUrlPattern, @@ -45,7 +38,6 @@ export function createServices(resources: { blobstore, backgroundQueue, crawlers, - labeler, ), local: LocalService.creator( repoSigningKey, @@ -54,9 +46,6 @@ export function createServices(resources: { appviewCdnUrlPattern, ), moderation: ModerationService.creator(blobstore), - appView: { - label: LabelService.creator(labelCache), - }, } } @@ -67,9 +56,6 @@ export type Services = { repo: FromDb local: FromDb moderation: FromDb - appView: { - label: FromDb - } } type FromDb = (db: Database) => T diff --git a/packages/pds/src/services/moderation/views.ts b/packages/pds/src/services/moderation/views.ts index 8f0fec04ac7..2aaf01c52fe 100644 --- a/packages/pds/src/services/moderation/views.ts +++ b/packages/pds/src/services/moderation/views.ts @@ -21,7 +21,6 @@ import { ModerationAction } from '../../db/tables/moderation' import { AccountService } from '../account' import { RecordService } from '../record' import { ModerationReportRowWithHandle } from '.' -import { getSelfLabels } from '../../app-view/services/label' export class ModerationViews { constructor(private db: Database) {} diff --git a/packages/pds/src/services/repo/index.ts b/packages/pds/src/services/repo/index.ts index 568707e7ce3..406635b736d 100644 --- a/packages/pds/src/services/repo/index.ts +++ b/packages/pds/src/services/repo/index.ts @@ -15,7 +15,6 @@ import { RepoBlobs } from './blobs' import { createWriteToOp, writeToOp } from '../../repo' import { RecordService } from '../record' import * as sequencer from '../../sequencer' -import { Labeler } from '../../labeler' import { wait } from '@atproto/common' import { BackgroundQueue } from '../../background' import { Crawlers } from '../../crawlers' @@ -29,7 +28,6 @@ export class RepoService { public blobstore: BlobStore, public backgroundQueue: BackgroundQueue, public crawlers: Crawlers, - public labeler: Labeler, ) { this.blobs = new RepoBlobs(db, blobstore, backgroundQueue) } @@ -39,17 +37,9 @@ export class RepoService { blobstore: BlobStore, backgroundQueue: BackgroundQueue, crawlers: Crawlers, - labeler: Labeler, ) { return (db: Database) => - new RepoService( - db, - keypair, - blobstore, - backgroundQueue, - crawlers, - labeler, - ) + new RepoService(db, keypair, blobstore, backgroundQueue, crawlers) } services = { @@ -67,7 +57,6 @@ export class RepoService { this.blobstore, this.backgroundQueue, this.crawlers, - this.labeler, ) return fn(srvc) }) @@ -289,15 +278,6 @@ export class RepoService { this.backgroundQueue.add(async () => { await this.crawlers.notifyOfUpdate() }) - writes.forEach((write) => { - if ( - write.action === WriteOpAction.Create || - write.action === WriteOpAction.Update - ) { - // @TODO move to appview - this.labeler.processRecord(write.uri, write.record) - } - }) }) const seqEvt = await sequencer.formatSeqCommit(did, commitData, writes) diff --git a/packages/pds/tests/_util.ts b/packages/pds/tests/_util.ts index 16061b8af51..b8135836c15 100644 --- a/packages/pds/tests/_util.ts +++ b/packages/pds/tests/_util.ts @@ -98,9 +98,6 @@ export const runTestServer = async ( dbPostgresUrl: process.env.DB_POSTGRES_URL, blobstoreLocation: `${blobstoreLoc}/blobs`, blobstoreTmp: `${blobstoreLoc}/tmp`, - labelerDid: 'did:example:labeler', - labelerKeywords: { label_me: 'test-label', label_me_2: 'test-label-2' }, - feedGenDid: 'did:example:feedGen', maxSubscriptionBuffer: 200, repoBackfillLimitMs: HOUR, sequencerLeaderLockId: uniqueLockId(), @@ -153,9 +150,6 @@ export const runTestServer = async ( const pdsServer = await pds.start() const pdsPort = (pdsServer.address() as AddressInfo).port - // we refresh label cache by hand in `processAll` instead of on a timer - pds.ctx.labelCache.stop() - return { url: `http://localhost:${pdsPort}`, ctx: pds.ctx, @@ -165,7 +159,6 @@ export const runTestServer = async ( }, processAll: async () => { await pds.ctx.backgroundQueue.processAll() - await pds.ctx.labelCache.fullRefresh() }, } } From ec422b0dd002472d8cf8155f66f6b84b131687ed Mon Sep 17 00:00:00 2001 From: dholms Date: Thu, 21 Sep 2023 21:04:39 -0500 Subject: [PATCH 15/31] fix service file --- services/pds/index.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/services/pds/index.js b/services/pds/index.js index 5c4d8e924a7..b65505152fb 100644 --- a/services/pds/index.js +++ b/services/pds/index.js @@ -15,14 +15,12 @@ const path = require('path') const { KmsKeypair, S3BlobStore, - CloudfrontInvalidator, } = require('@atproto/aws') const { Database, ServerConfig, PDS, ViewMaintainer, - makeAlgos, PeriodicModerationActionReversal, } = require('@atproto/pds') const { Secp256k1Keypair } = require('@atproto/crypto') @@ -68,19 +66,12 @@ const main = async () => { password: env.smtpPassword, }), }) - const cfInvalidator = new CloudfrontInvalidator({ - distributionId: env.cfDistributionId, - pathPrefix: cfg.imgUriEndpoint && new URL(cfg.imgUriEndpoint).pathname, - }) - const algos = env.feedPublisherDid ? makeAlgos(env.feedPublisherDid) : {} const pds = PDS.create({ db, blobstore: s3Blobstore, repoSigningKey, plcRotationKey, config: cfg, - imgInvalidator: cfInvalidator, - algos, }) const viewMaintainer = new ViewMaintainer(migrateDb) const viewMaintainerRunning = viewMaintainer.run() @@ -151,8 +142,6 @@ const getEnv = () => ({ smtpUsername: process.env.SMTP_USERNAME, smtpPassword: process.env.SMTP_PASSWORD, s3Bucket: process.env.S3_BUCKET_NAME, - cfDistributionId: process.env.CF_DISTRIBUTION_ID, - feedPublisherDid: process.env.FEED_PUBLISHER_DID, }) const maintainXrpcResource = (span, req) => { From 182064b0ceab34f8a6d70a6752f7b997e14a4cbe Mon Sep 17 00:00:00 2001 From: dholms Date: Thu, 21 Sep 2023 21:47:38 -0500 Subject: [PATCH 16/31] remove kysely tables --- .../atproto/admin/reverseModerationAction.ts | 31 ------ .../com/atproto/admin/takeModerationAction.ts | 21 ---- .../src/app-view/api/app/bsky/unspecced.ts | 14 +-- packages/pds/src/app-view/db/index.ts | 38 -------- .../pds/src/app-view/db/tables/actor-block.ts | 11 --- packages/pds/src/app-view/db/tables/algo.ts | 12 --- .../app-view/db/tables/duplicate-record.ts | 12 --- .../src/app-view/db/tables/feed-generator.ts | 18 ---- .../pds/src/app-view/db/tables/feed-item.ts | 12 --- packages/pds/src/app-view/db/tables/follow.ts | 11 --- packages/pds/src/app-view/db/tables/like.ts | 13 --- .../pds/src/app-view/db/tables/list-item.ts | 13 --- packages/pds/src/app-view/db/tables/list.ts | 16 ---- .../pds/src/app-view/db/tables/post-agg.ts | 14 --- .../pds/src/app-view/db/tables/post-embed.ts | 30 ------ packages/pds/src/app-view/db/tables/post.ts | 18 ---- .../pds/src/app-view/db/tables/profile-agg.ts | 14 --- .../pds/src/app-view/db/tables/profile.ts | 13 --- packages/pds/src/app-view/db/tables/repost.ts | 13 --- .../src/app-view/db/tables/subscription.ts | 9 -- .../app-view/db/tables/suggested-follow.ts | 10 -- .../pds/src/app-view/db/tables/view-param.ts | 12 --- packages/pds/src/db/database-schema.ts | 4 +- .../db/periodic-moderation-action-reversal.ts | 25 ----- packages/pds/src/services/account/index.ts | 28 +----- packages/pds/src/services/moderation/views.ts | 39 ++------ packages/pds/src/services/util/search.ts | 96 +------------------ 27 files changed, 17 insertions(+), 530 deletions(-) delete mode 100644 packages/pds/src/app-view/db/index.ts delete mode 100644 packages/pds/src/app-view/db/tables/actor-block.ts delete mode 100644 packages/pds/src/app-view/db/tables/algo.ts delete mode 100644 packages/pds/src/app-view/db/tables/duplicate-record.ts delete mode 100644 packages/pds/src/app-view/db/tables/feed-generator.ts delete mode 100644 packages/pds/src/app-view/db/tables/feed-item.ts delete mode 100644 packages/pds/src/app-view/db/tables/follow.ts delete mode 100644 packages/pds/src/app-view/db/tables/like.ts delete mode 100644 packages/pds/src/app-view/db/tables/list-item.ts delete mode 100644 packages/pds/src/app-view/db/tables/list.ts delete mode 100644 packages/pds/src/app-view/db/tables/post-agg.ts delete mode 100644 packages/pds/src/app-view/db/tables/post-embed.ts delete mode 100644 packages/pds/src/app-view/db/tables/post.ts delete mode 100644 packages/pds/src/app-view/db/tables/profile-agg.ts delete mode 100644 packages/pds/src/app-view/db/tables/profile.ts delete mode 100644 packages/pds/src/app-view/db/tables/repost.ts delete mode 100644 packages/pds/src/app-view/db/tables/subscription.ts delete mode 100644 packages/pds/src/app-view/db/tables/suggested-follow.ts delete mode 100644 packages/pds/src/app-view/db/tables/view-param.ts diff --git a/packages/pds/src/api/com/atproto/admin/reverseModerationAction.ts b/packages/pds/src/api/com/atproto/admin/reverseModerationAction.ts index d652b501166..b0d1e149790 100644 --- a/packages/pds/src/api/com/atproto/admin/reverseModerationAction.ts +++ b/packages/pds/src/api/com/atproto/admin/reverseModerationAction.ts @@ -26,7 +26,6 @@ export default function (server: Server, ctx: AppContext) { const transact = db.transaction(async (dbTxn) => { const moderationTxn = services.moderation(dbTxn) - const labelTxn = services.appView.label(dbTxn) // reverse takedowns if (result.action === TAKEDOWN && isRepoRef(result.subject)) { await moderationTxn.reverseTakedownRepo({ @@ -38,18 +37,6 @@ export default function (server: Server, ctx: AppContext) { uri: new AtUri(result.subject.uri), }) } - // invert label creation & negations - const reverseLabels = (uri: string, cid: string | null) => - labelTxn.formatAndCreate(ctx.cfg.labelerDid, uri, cid, { - create: result.negateLabelVals, - negate: result.createLabelVals, - }) - if (isRepoRef(result.subject)) { - await reverseLabels(result.subject.did, null) - } - if (isStrongRef(result.subject)) { - await reverseLabels(result.subject.uri, result.subject.cid) - } }) try { @@ -72,7 +59,6 @@ export default function (server: Server, ctx: AppContext) { const moderationAction = await db.transaction(async (dbTxn) => { const moderationTxn = services.moderation(dbTxn) - const labelTxn = services.appView.label(dbTxn) const now = new Date() const existing = await moderationTxn.getAction(id) @@ -114,23 +100,6 @@ export default function (server: Server, ctx: AppContext) { reason, }) - // invert creates & negates - const { createLabelVals, negateLabelVals } = result - const negate = - createLabelVals && createLabelVals.length > 0 - ? createLabelVals.split(' ') - : undefined - const create = - negateLabelVals && negateLabelVals.length > 0 - ? negateLabelVals.split(' ') - : undefined - await labelTxn.formatAndCreate( - ctx.cfg.labelerDid, - result.subjectUri ?? result.subjectDid, - result.subjectCid, - { create, negate }, - ) - return result }) diff --git a/packages/pds/src/api/com/atproto/admin/takeModerationAction.ts b/packages/pds/src/api/com/atproto/admin/takeModerationAction.ts index 8a81245a9e8..e9fb9b7e043 100644 --- a/packages/pds/src/api/com/atproto/admin/takeModerationAction.ts +++ b/packages/pds/src/api/com/atproto/admin/takeModerationAction.ts @@ -29,7 +29,6 @@ export default function (server: Server, ctx: AppContext) { const transact = db.transaction(async (dbTxn) => { const authTxn = services.auth(dbTxn) const moderationTxn = services.moderation(dbTxn) - const labelTxn = services.appView.label(dbTxn) // perform takedowns if (result.action === TAKEDOWN && isRepoRef(result.subject)) { await authTxn.revokeRefreshTokensByDid(result.subject.did) @@ -45,18 +44,6 @@ export default function (server: Server, ctx: AppContext) { blobCids: result.subjectBlobCids.map((cid) => CID.parse(cid)), }) } - // apply label creation & negations - const applyLabels = (uri: string, cid: string | null) => - labelTxn.formatAndCreate(ctx.cfg.labelerDid, uri, cid, { - create: result.createLabelVals, - negate: result.negateLabelVals, - }) - if (isRepoRef(result.subject)) { - await applyLabels(result.subject.did, null) - } - if (isStrongRef(result.subject)) { - await applyLabels(result.subject.uri, result.subject.cid) - } }) try { @@ -113,7 +100,6 @@ export default function (server: Server, ctx: AppContext) { const moderationAction = await db.transaction(async (dbTxn) => { const authTxn = services.auth(dbTxn) const moderationTxn = services.moderation(dbTxn) - const labelTxn = services.appView.label(dbTxn) const result = await moderationTxn.logAction({ action: getAction(action), @@ -150,13 +136,6 @@ export default function (server: Server, ctx: AppContext) { }) } - await labelTxn.formatAndCreate( - ctx.cfg.labelerDid, - result.subjectUri ?? result.subjectDid, - result.subjectCid, - { create: createLabelVals, negate: negateLabelVals }, - ) - return result }) diff --git a/packages/pds/src/app-view/api/app/bsky/unspecced.ts b/packages/pds/src/app-view/api/app/bsky/unspecced.ts index 222b58ffde5..809e4482f8a 100644 --- a/packages/pds/src/app-view/api/app/bsky/unspecced.ts +++ b/packages/pds/src/app-view/api/app/bsky/unspecced.ts @@ -1,4 +1,4 @@ -import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server' +import { InvalidRequestError } from '@atproto/xrpc-server' import { Server } from '../../../../lexicon' import { GenericKeyset } from '../../../../db/pagination' import AppContext from '../../../../context' @@ -30,18 +30,6 @@ export default function (server: Server, ctx: AppContext) { } }, }) - - server.app.bsky.unspecced.applyLabels({ - auth: ctx.roleVerifier, - handler: async ({ auth, input }) => { - if (!auth.credentials.admin) { - throw new AuthRequiredError('Insufficient privileges') - } - const { services, db } = ctx - const { labels } = input.body - await services.appView.label(db).createLabels(labels) - }, - }) } type Result = { likeCount: number; cid: string } diff --git a/packages/pds/src/app-view/db/index.ts b/packages/pds/src/app-view/db/index.ts deleted file mode 100644 index 21edd709a7a..00000000000 --- a/packages/pds/src/app-view/db/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -import * as duplicateRecords from './tables/duplicate-record' -import * as profile from './tables/profile' -import * as profileAgg from './tables/profile-agg' -import * as post from './tables/post' -import * as postAgg from './tables/post-agg' -import * as postEmbed from './tables/post-embed' -import * as repost from './tables/repost' -import * as feedItem from './tables/feed-item' -import * as follow from './tables/follow' -import * as list from './tables/list' -import * as listItem from './tables/list-item' -import * as actorBlock from './tables/actor-block' -import * as like from './tables/like' -import * as feedGenerator from './tables/feed-generator' -import * as subscription from './tables/subscription' -import * as algo from './tables/algo' -import * as viewParam from './tables/view-param' -import * as suggestedFollow from './tables/suggested-follow' - -// @NOTE app-view also shares did-handle, record, and repo-root tables w/ main pds -export type DatabaseSchemaType = duplicateRecords.PartialDB & - profile.PartialDB & - profileAgg.PartialDB & - post.PartialDB & - postAgg.PartialDB & - postEmbed.PartialDB & - repost.PartialDB & - feedItem.PartialDB & - follow.PartialDB & - list.PartialDB & - listItem.PartialDB & - actorBlock.PartialDB & - like.PartialDB & - feedGenerator.PartialDB & - subscription.PartialDB & - algo.PartialDB & - viewParam.PartialDB & - suggestedFollow.PartialDB diff --git a/packages/pds/src/app-view/db/tables/actor-block.ts b/packages/pds/src/app-view/db/tables/actor-block.ts deleted file mode 100644 index 8923e921071..00000000000 --- a/packages/pds/src/app-view/db/tables/actor-block.ts +++ /dev/null @@ -1,11 +0,0 @@ -export const tableName = 'actor_block' -export interface ActorBlock { - uri: string - cid: string - creator: string - subjectDid: string - createdAt: string - indexedAt: string -} - -export type PartialDB = { [tableName]: ActorBlock } diff --git a/packages/pds/src/app-view/db/tables/algo.ts b/packages/pds/src/app-view/db/tables/algo.ts deleted file mode 100644 index e38a64c4a69..00000000000 --- a/packages/pds/src/app-view/db/tables/algo.ts +++ /dev/null @@ -1,12 +0,0 @@ -// @NOTE postgres-only -export const whatsHotViewTableName = 'algo_whats_hot_view' - -export interface AlgoWhatsHotView { - uri: string - cid: string - score: number -} - -export type PartialDB = { - [whatsHotViewTableName]: AlgoWhatsHotView -} diff --git a/packages/pds/src/app-view/db/tables/duplicate-record.ts b/packages/pds/src/app-view/db/tables/duplicate-record.ts deleted file mode 100644 index 3cad0cd148b..00000000000 --- a/packages/pds/src/app-view/db/tables/duplicate-record.ts +++ /dev/null @@ -1,12 +0,0 @@ -export interface DuplicateRecord { - uri: string - cid: string - duplicateOf: string - indexedAt: string -} - -export const tableName = 'duplicate_record' - -export type PartialDB = { - [tableName]: DuplicateRecord -} diff --git a/packages/pds/src/app-view/db/tables/feed-generator.ts b/packages/pds/src/app-view/db/tables/feed-generator.ts deleted file mode 100644 index 0b33f05cb12..00000000000 --- a/packages/pds/src/app-view/db/tables/feed-generator.ts +++ /dev/null @@ -1,18 +0,0 @@ -export const tableName = 'feed_generator' - -export interface FeedGenerator { - uri: string - cid: string - creator: string - feedDid: string - displayName: string - description: string | null - descriptionFacets: string | null - avatarCid: string | null - createdAt: string - indexedAt: string -} - -export type PartialDB = { - [tableName]: FeedGenerator -} diff --git a/packages/pds/src/app-view/db/tables/feed-item.ts b/packages/pds/src/app-view/db/tables/feed-item.ts deleted file mode 100644 index 2fb7f867311..00000000000 --- a/packages/pds/src/app-view/db/tables/feed-item.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const tableName = 'feed_item' - -export interface FeedItem { - uri: string - cid: string - type: 'post' | 'repost' - postUri: string - originatorDid: string - sortAt: string -} - -export type PartialDB = { [tableName]: FeedItem } diff --git a/packages/pds/src/app-view/db/tables/follow.ts b/packages/pds/src/app-view/db/tables/follow.ts deleted file mode 100644 index b6ba34aea43..00000000000 --- a/packages/pds/src/app-view/db/tables/follow.ts +++ /dev/null @@ -1,11 +0,0 @@ -export const tableName = 'follow' -export interface Follow { - uri: string - cid: string - creator: string - subjectDid: string - createdAt: string - indexedAt: string -} - -export type PartialDB = { [tableName]: Follow } diff --git a/packages/pds/src/app-view/db/tables/like.ts b/packages/pds/src/app-view/db/tables/like.ts deleted file mode 100644 index 48edac21fde..00000000000 --- a/packages/pds/src/app-view/db/tables/like.ts +++ /dev/null @@ -1,13 +0,0 @@ -export interface Like { - uri: string - cid: string - creator: string - subject: string - subjectCid: string - createdAt: string - indexedAt: string -} - -const tableName = 'like' - -export type PartialDB = { [tableName]: Like } diff --git a/packages/pds/src/app-view/db/tables/list-item.ts b/packages/pds/src/app-view/db/tables/list-item.ts deleted file mode 100644 index 7e70e718169..00000000000 --- a/packages/pds/src/app-view/db/tables/list-item.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const tableName = 'list_item' - -export interface ListItem { - uri: string - cid: string - creator: string - subjectDid: string - listUri: string - createdAt: string - indexedAt: string -} - -export type PartialDB = { [tableName]: ListItem } diff --git a/packages/pds/src/app-view/db/tables/list.ts b/packages/pds/src/app-view/db/tables/list.ts deleted file mode 100644 index d2db17574d2..00000000000 --- a/packages/pds/src/app-view/db/tables/list.ts +++ /dev/null @@ -1,16 +0,0 @@ -export const tableName = 'list' - -export interface List { - uri: string - cid: string - creator: string - name: string - purpose: string - description: string | null - descriptionFacets: string | null - avatarCid: string | null - createdAt: string - indexedAt: string -} - -export type PartialDB = { [tableName]: List } diff --git a/packages/pds/src/app-view/db/tables/post-agg.ts b/packages/pds/src/app-view/db/tables/post-agg.ts deleted file mode 100644 index 5341347403d..00000000000 --- a/packages/pds/src/app-view/db/tables/post-agg.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Generated } from 'kysely' - -export const tableName = 'post_agg' - -export interface PostAgg { - uri: string - likeCount: Generated - replyCount: Generated - repostCount: Generated -} - -export type PartialDB = { - [tableName]: PostAgg -} diff --git a/packages/pds/src/app-view/db/tables/post-embed.ts b/packages/pds/src/app-view/db/tables/post-embed.ts deleted file mode 100644 index 69c44efe718..00000000000 --- a/packages/pds/src/app-view/db/tables/post-embed.ts +++ /dev/null @@ -1,30 +0,0 @@ -export const imageTableName = 'post_embed_image' -export const externalTableName = 'post_embed_external' -export const recordTableName = 'post_embed_record' - -export interface PostEmbedImage { - postUri: string - position: number - imageCid: string - alt: string -} - -export interface PostEmbedExternal { - postUri: string - uri: string - title: string - description: string - thumbCid: string | null -} - -export interface PostEmbedRecord { - postUri: string - embedUri: string - embedCid: string -} - -export type PartialDB = { - [imageTableName]: PostEmbedImage - [externalTableName]: PostEmbedExternal - [recordTableName]: PostEmbedRecord -} diff --git a/packages/pds/src/app-view/db/tables/post.ts b/packages/pds/src/app-view/db/tables/post.ts deleted file mode 100644 index ce7c2b7acbe..00000000000 --- a/packages/pds/src/app-view/db/tables/post.ts +++ /dev/null @@ -1,18 +0,0 @@ -export const tableName = 'post' - -export interface Post { - uri: string - cid: string - creator: string - text: string - replyRoot: string | null - replyRootCid: string | null - replyParent: string | null - replyParentCid: string | null - createdAt: string - indexedAt: string -} - -export type PartialDB = { - [tableName]: Post -} diff --git a/packages/pds/src/app-view/db/tables/profile-agg.ts b/packages/pds/src/app-view/db/tables/profile-agg.ts deleted file mode 100644 index 333a5136461..00000000000 --- a/packages/pds/src/app-view/db/tables/profile-agg.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Generated } from 'kysely' - -export const tableName = 'profile_agg' - -export interface ProfileAgg { - did: string - followersCount: Generated - followsCount: Generated - postsCount: Generated -} - -export type PartialDB = { - [tableName]: ProfileAgg -} diff --git a/packages/pds/src/app-view/db/tables/profile.ts b/packages/pds/src/app-view/db/tables/profile.ts deleted file mode 100644 index 6f8fac50aa5..00000000000 --- a/packages/pds/src/app-view/db/tables/profile.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const tableName = 'profile' - -export interface Profile { - uri: string - cid: string - creator: string - displayName: string | null - description: string | null - avatarCid: string | null - bannerCid: string | null - indexedAt: string -} -export type PartialDB = { [tableName]: Profile } diff --git a/packages/pds/src/app-view/db/tables/repost.ts b/packages/pds/src/app-view/db/tables/repost.ts deleted file mode 100644 index 8a0fadd6611..00000000000 --- a/packages/pds/src/app-view/db/tables/repost.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const tableName = 'repost' - -export interface Repost { - uri: string - cid: string - creator: string - subject: string - subjectCid: string - createdAt: string - indexedAt: string -} - -export type PartialDB = { [tableName]: Repost } diff --git a/packages/pds/src/app-view/db/tables/subscription.ts b/packages/pds/src/app-view/db/tables/subscription.ts deleted file mode 100644 index 62a49d0b29b..00000000000 --- a/packages/pds/src/app-view/db/tables/subscription.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const tableName = 'subscription' - -export interface Subscription { - service: string - method: string - state: string -} - -export type PartialDB = { [tableName]: Subscription } diff --git a/packages/pds/src/app-view/db/tables/suggested-follow.ts b/packages/pds/src/app-view/db/tables/suggested-follow.ts deleted file mode 100644 index 6ad10f43203..00000000000 --- a/packages/pds/src/app-view/db/tables/suggested-follow.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const tableName = 'suggested_follow' - -export interface SuggestedFollow { - did: string - order: number -} - -export type PartialDB = { - [tableName]: SuggestedFollow -} diff --git a/packages/pds/src/app-view/db/tables/view-param.ts b/packages/pds/src/app-view/db/tables/view-param.ts deleted file mode 100644 index 07e3b08f0cf..00000000000 --- a/packages/pds/src/app-view/db/tables/view-param.ts +++ /dev/null @@ -1,12 +0,0 @@ -// @NOTE postgres-only -export const tableName = 'view_param' - -// materialized views are difficult to change, -// so we parameterize them at runtime with contents of this table. -// its contents are set in migrations, available param names are static. -export interface ViewParam { - name: string - value: string -} - -export type PartialDB = { [tableName]: ViewParam } diff --git a/packages/pds/src/db/database-schema.ts b/packages/pds/src/db/database-schema.ts index f77df6b21ad..ed86d15939f 100644 --- a/packages/pds/src/db/database-schema.ts +++ b/packages/pds/src/db/database-schema.ts @@ -22,10 +22,8 @@ import * as label from './tables/label' import * as repoSeq from './tables/repo-seq' import * as appMigration from './tables/app-migration' import * as runtimeFlag from './tables/runtime-flag' -import * as appView from '../app-view/db' -export type DatabaseSchemaType = appView.DatabaseSchemaType & - runtimeFlag.PartialDB & +export type DatabaseSchemaType = runtimeFlag.PartialDB & appMigration.PartialDB & userAccount.PartialDB & userState.PartialDB & diff --git a/packages/pds/src/db/periodic-moderation-action-reversal.ts b/packages/pds/src/db/periodic-moderation-action-reversal.ts index 72730e61565..b3b631de71d 100644 --- a/packages/pds/src/db/periodic-moderation-action-reversal.ts +++ b/packages/pds/src/db/periodic-moderation-action-reversal.ts @@ -4,7 +4,6 @@ import { Leader } from './leader' import { dbLogger } from '../logger' import AppContext from '../context' import { ModerationActionRow } from '../services/moderation' -import { LabelService } from '../app-view/services/label' export const MODERATION_ACTION_REVERSAL_ID = 1011 @@ -14,39 +13,15 @@ export class PeriodicModerationActionReversal { constructor(private appContext: AppContext) {} - // invert label creation & negations - async reverseLabels(labelTxn: LabelService, actionRow: ModerationActionRow) { - let uri: string - let cid: string | null = null - - if (actionRow.subjectUri && actionRow.subjectCid) { - uri = actionRow.subjectUri - cid = actionRow.subjectCid - } else { - uri = actionRow.subjectDid - } - - await labelTxn.formatAndCreate(this.appContext.cfg.labelerDid, uri, cid, { - create: actionRow.negateLabelVals - ? actionRow.negateLabelVals.split(' ') - : undefined, - negate: actionRow.createLabelVals - ? actionRow.createLabelVals.split(' ') - : undefined, - }) - } - async revertAction(actionRow: ModerationActionRow) { return this.appContext.db.transaction(async (dbTxn) => { const moderationTxn = this.appContext.services.moderation(dbTxn) - const labelTxn = this.appContext.services.appView.label(dbTxn) await moderationTxn.revertAction({ id: actionRow.id, createdBy: actionRow.createdBy, createdAt: new Date(), reason: `[SCHEDULED_REVERSAL] Reverting action as originally scheduled`, }) - await this.reverseLabels(labelTxn, actionRow) }) } diff --git a/packages/pds/src/services/account/index.ts b/packages/pds/src/services/account/index.ts index 8173d9160af..bb89e092dc5 100644 --- a/packages/pds/src/services/account/index.ts +++ b/packages/pds/src/services/account/index.ts @@ -1,18 +1,17 @@ -import { WhereInterface, sql } from 'kysely' +import { sql } from 'kysely' import { dbLogger as log } from '../../logger' import Database from '../../db' import * as scrypt from '../../db/scrypt' import { UserAccountEntry } from '../../db/tables/user-account' import { DidHandle } from '../../db/tables/did-handle' import { RepoRoot } from '../../db/tables/repo-root' -import { DbRef, countAll, notSoftDeletedClause } from '../../db/util' +import { countAll, notSoftDeletedClause } from '../../db/util' import { getUserSearchQueryPg, getUserSearchQuerySqlite } from '../util/search' import { paginate, TimeCidKeyset } from '../../db/pagination' import * as sequencer from '../../sequencer' import { AppPassword } from '../../lexicon/types/com/atproto/server/createAppPassword' import { randomStr } from '@atproto/crypto' import { InvalidRequestError } from '@atproto/xrpc-server' -import { NotEmptyArray } from '@atproto/common' export class AccountService { constructor(public db: Database) {} @@ -355,27 +354,6 @@ export class AccountService { .execute() } - whereNotMuted>( - qb: W, - requester: string, - refs: NotEmptyArray, - ) { - const subjectRefs = sql.join(refs) - const actorMute = this.db.db - .selectFrom('mute') - .where('mutedByDid', '=', requester) - .where('did', 'in', sql`(${subjectRefs})`) - .select('did as muted') - const listMute = this.db.db - .selectFrom('list_item') - .innerJoin('list_mute', 'list_mute.listUri', 'list_item.listUri') - .where('list_mute.mutedByDid', '=', requester) - .whereRef('list_item.subjectDid', 'in', sql`(${subjectRefs})`) - .select('list_item.subjectDid as muted') - // Splitting the mute from list-mute checks seems to be more flexible for the query-planner and often quicker - return qb.whereNotExists(actorMute).whereNotExists(listMute) - } - async search(opts: { searchField?: 'did' | 'handle' term: string @@ -400,9 +378,7 @@ export class AccountService { .innerJoin('repo_root', 'repo_root.did', 'did_handle.did') .selectAll('did_handle') .selectAll('repo_root') - .select('results.distance as distance') : getUserSearchQuerySqlite(this.db, opts) - .leftJoin('profile', 'profile.creator', 'did_handle.did') // @TODO leaky, for getUserSearchQuerySqlite() .innerJoin('repo_root', 'repo_root.did', 'did_handle.did') .selectAll('did_handle') .selectAll('repo_root') diff --git a/packages/pds/src/services/moderation/views.ts b/packages/pds/src/services/moderation/views.ts index 2aaf01c52fe..e8d89620d73 100644 --- a/packages/pds/src/services/moderation/views.ts +++ b/packages/pds/src/services/moderation/views.ts @@ -16,11 +16,11 @@ import { BlobView, } from '../../lexicon/types/com/atproto/admin/defs' import { OutputSchema as ReportOutput } from '../../lexicon/types/com/atproto/moderation/createReport' -import { Label } from '../../lexicon/types/com/atproto/label/defs' import { ModerationAction } from '../../db/tables/moderation' import { AccountService } from '../account' import { RecordService } from '../record' import { ModerationReportRowWithHandle } from '.' +import { ids } from '../../lexicon/lexicons' export class ModerationViews { constructor(private db: Database) {} @@ -43,10 +43,15 @@ export class ModerationViews { await this.db.db .selectFrom('did_handle') .leftJoin('user_account', 'user_account.did', 'did_handle.did') - .leftJoin('profile', 'profile.creator', 'did_handle.did') + .leftJoin('record as profile_record', (join) => + join + .onRef('profile_record.did', '=', 'did_handle.did') + .on('profile_record.collection', '=', ids.AppBskyActorProfile) + .on('profile_record.rkey', '=', 'self'), + ) .leftJoin('ipld_block as profile_block', (join) => join - .onRef('profile_block.cid', '=', 'profile.cid') + .onRef('profile_block.cid', '=', 'profile_record.cid') .onRef('profile_block.creator', '=', 'did_handle.did'), ) .where( @@ -141,10 +146,9 @@ export class ModerationViews { .execute(), this.services.account(this.db).getAccountInviteCodes(repo.did), ]) - const [reports, actions, labels] = await Promise.all([ + const [reports, actions] = await Promise.all([ this.report(reportResults), this.action(actionResults), - this.labels(repo.did), ]) return { ...repo, @@ -154,7 +158,6 @@ export class ModerationViews { actions, }, invites: inviteCodes, - labels, } } @@ -268,17 +271,11 @@ export class ModerationViews { .selectAll() .execute(), ]) - const [reports, actions, blobs, labels] = await Promise.all([ + const [reports, actions, blobs] = await Promise.all([ this.report(reportResults), this.action(actionResults), this.blob(record.blobCids), - this.labels(record.uri), ]) - const selfLabels = getSelfLabels({ - uri: result.uri, - cid: result.cid, - record: result.value as Record, - }) return { ...record, blobs, @@ -287,7 +284,6 @@ export class ModerationViews { reports, actions, }, - labels: [...labels, ...selfLabels], } } @@ -607,21 +603,6 @@ export class ModerationViews { } }) } - - // @TODO: call into label service instead on AppView - async labels(subject: string, includeNeg?: boolean): Promise { - const res = await this.db.db - .selectFrom('label') - .where('label.uri', '=', subject) - .if(!includeNeg, (qb) => qb.where('neg', '=', 0)) - .selectAll() - .execute() - return res.map((l) => ({ - ...l, - cid: l.cid === '' ? undefined : l.cid, - neg: l.neg === 1, - })) - } } type RepoResult = DidHandle & RepoRoot diff --git a/packages/pds/src/services/util/search.ts b/packages/pds/src/services/util/search.ts index 007052e707c..e66818bb420 100644 --- a/packages/pds/src/services/util/search.ts +++ b/packages/pds/src/services/util/search.ts @@ -1,7 +1,7 @@ import { sql } from 'kysely' import { InvalidRequestError } from '@atproto/xrpc-server' import Database from '../../db' -import { notSoftDeletedClause, DbRef, AnyQb } from '../../db/util' +import { notSoftDeletedClause, DbRef } from '../../db/util' import { GenericKeyset, paginate } from '../../db/pagination' // @TODO utilized in both pds and app-view @@ -19,58 +19,13 @@ export const getUserSearchQueryPg = ( const { term, limit, cursor, includeSoftDeleted } = opts // Matching user accounts based on handle const distanceAccount = distance(term, ref('handle')) - let accountsQb = getMatchingAccountsQb(db, { term, includeSoftDeleted }) - accountsQb = paginate(accountsQb, { + const accountsQb = getMatchingAccountsQb(db, { term, includeSoftDeleted }) + return paginate(accountsQb, { limit, cursor, direction: 'asc', keyset: new SearchKeyset(distanceAccount, ref('handle')), }) - // Matching profiles based on display name - const distanceProfile = distance(term, ref('displayName')) - let profilesQb = getMatchingProfilesQb(db, { term, includeSoftDeleted }) - profilesQb = paginate( - profilesQb.innerJoin('did_handle', 'did_handle.did', 'profile.creator'), // for handle pagination - { - limit, - cursor, - direction: 'asc', - keyset: new SearchKeyset(distanceProfile, ref('handle')), - }, - ) - // Combine and paginate result set - return paginate(combineAccountsAndProfilesQb(db, accountsQb, profilesQb), { - limit, - cursor, - direction: 'asc', - keyset: new SearchKeyset(ref('distance'), ref('handle')), - }) -} - -// Takes maximal advantage of trigram index at the expense of ability to paginate. -export const getUserSearchQuerySimplePg = ( - db: Database, - opts: { - term: string - limit: number - }, -) => { - const { ref } = db.db.dynamic - const { term, limit } = opts - // Matching user accounts based on handle - const accountsQb = getMatchingAccountsQb(db, { term }) - .orderBy('distance', 'asc') - .limit(limit) - // Matching profiles based on display name - const profilesQb = getMatchingProfilesQb(db, { term }) - .orderBy('distance', 'asc') - .limit(limit) - // Combine and paginate result set - return paginate(combineAccountsAndProfilesQb(db, accountsQb, profilesQb), { - limit, - direction: 'asc', - keyset: new SearchKeyset(ref('distance'), ref('handle')), - }) } // Matching user accounts based on handle @@ -91,51 +46,6 @@ const getMatchingAccountsQb = ( .select(['did_handle.did as did', distanceAccount.as('distance')]) } -// Matching profiles based on display name -const getMatchingProfilesQb = ( - db: Database, - opts: { term: string; includeSoftDeleted?: boolean }, -) => { - const { ref } = db.db.dynamic - const { term, includeSoftDeleted } = opts - const distanceProfile = distance(term, ref('displayName')) - return db.db - .selectFrom('profile') - .innerJoin('repo_root', 'repo_root.did', 'profile.creator') - .if(!includeSoftDeleted, (qb) => - qb.where(notSoftDeletedClause(ref('repo_root'))), - ) - .where(similar(term, ref('displayName'))) // Coarse filter engaging trigram index - .select(['profile.creator as did', distanceProfile.as('distance')]) -} - -// Combine profile and account result sets -const combineAccountsAndProfilesQb = ( - db: Database, - accountsQb: AnyQb, - profilesQb: AnyQb, -) => { - // Combine user account and profile results, taking best matches from each - const emptyQb = db.db - .selectFrom('user_account') - .where(sql`1 = 0`) - .select([sql.literal('').as('did'), sql`0`.as('distance')]) - const resultsQb = db.db - .selectFrom( - emptyQb - .unionAll(sql`${accountsQb}`) // The sql`` is adding parens - .unionAll(sql`${profilesQb}`) - .as('accounts_and_profiles'), - ) - .selectAll() - .distinctOn('did') // Per did, take whichever of account and profile distance is best - .orderBy('did') - .orderBy('distance') - return db.db - .selectFrom(resultsQb.as('results')) - .innerJoin('did_handle', 'did_handle.did', 'results.did') -} - export const getUserSearchQuerySqlite = ( db: Database, opts: { From 1d890055bd995823907b8fa4930267544aa2a568 Mon Sep 17 00:00:00 2001 From: dholms Date: Thu, 21 Sep 2023 21:53:31 -0500 Subject: [PATCH 17/31] re-enable getPopular --- .../pds/src/app-view/api/app/bsky/unspecced.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/pds/src/app-view/api/app/bsky/unspecced.ts b/packages/pds/src/app-view/api/app/bsky/unspecced.ts index 222b58ffde5..482adb3dbd8 100644 --- a/packages/pds/src/app-view/api/app/bsky/unspecced.ts +++ b/packages/pds/src/app-view/api/app/bsky/unspecced.ts @@ -7,7 +7,20 @@ import AppContext from '../../../../context' export default function (server: Server, ctx: AppContext) { server.app.bsky.unspecced.getPopular({ auth: ctx.accessVerifier, - handler: async () => { + handler: async ({ auth, params }) => { + const requester = auth.credentials.did + const HOT_CLASSIC_URI = + 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/hot-classic' + const HOT_CLASSIC_DID = 'did:plc:5fllqkujj6kqp5izd5jg7gox' + const res = await ctx.appviewAgent.api.app.bsky.feed.getFeed( + { feed: HOT_CLASSIC_URI, limit: params.limit, cursor: params.cursor }, + await ctx.serviceAuthHeaders(requester, HOT_CLASSIC_DID), + ) + return { + encoding: 'application/json', + body: res.data, + } + return { encoding: 'application/json', body: { feed: [] }, From cdc36db359fac508ba84cec3af5700d7614fd93f Mon Sep 17 00:00:00 2001 From: dholms Date: Thu, 21 Sep 2023 21:58:25 -0500 Subject: [PATCH 18/31] format --- services/pds/index.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/services/pds/index.js b/services/pds/index.js index b65505152fb..4be4833e942 100644 --- a/services/pds/index.js +++ b/services/pds/index.js @@ -12,10 +12,7 @@ require('dd-trace') // Only works with commonjs // Tracer code above must come before anything else const path = require('path') -const { - KmsKeypair, - S3BlobStore, -} = require('@atproto/aws') +const { KmsKeypair, S3BlobStore } = require('@atproto/aws') const { Database, ServerConfig, From 5ff5b429a6dfa635afc55acebb700f1136899a83 Mon Sep 17 00:00:00 2001 From: dholms Date: Thu, 21 Sep 2023 22:13:38 -0500 Subject: [PATCH 19/31] cleaning up tests --- packages/pds/tests/account-deletion.test.ts | 147 +------ packages/pds/tests/duplicate-records.test.ts | 179 -------- .../pds/tests/labeler/apply-labels.test.ts | 180 -------- .../labeler/fixtures/hiveai_resp_example.json | 401 ------------------ packages/pds/tests/labeler/hive.test.ts | 16 - packages/pds/tests/labeler/labeler.test.ts | 178 -------- packages/pds/tests/moderation.test.ts | 205 +-------- 7 files changed, 22 insertions(+), 1284 deletions(-) delete mode 100644 packages/pds/tests/duplicate-records.test.ts delete mode 100644 packages/pds/tests/labeler/apply-labels.test.ts delete mode 100644 packages/pds/tests/labeler/fixtures/hiveai_resp_example.json delete mode 100644 packages/pds/tests/labeler/hive.test.ts delete mode 100644 packages/pds/tests/labeler/labeler.test.ts diff --git a/packages/pds/tests/account-deletion.test.ts b/packages/pds/tests/account-deletion.test.ts index b99807ec6d5..d6b4aa101ce 100644 --- a/packages/pds/tests/account-deletion.test.ts +++ b/packages/pds/tests/account-deletion.test.ts @@ -11,24 +11,12 @@ import { BlobNotFoundError, BlobStore } from '@atproto/repo' import { RepoRoot } from '../src/db/tables/repo-root' import { UserAccount } from '../src/db/tables/user-account' import { IpldBlock } from '../src/db/tables/ipld-block' -import { Post } from '../src/app-view/db/tables/post' -import { Like } from '../src/app-view/db/tables/like' -import { Repost } from '../src/app-view/db/tables/repost' -import { Follow } from '../src/app-view/db/tables/follow' import { RepoBlob } from '../src/db/tables/repo-blob' import { Blob } from '../src/db/tables/blob' -import { - PostEmbedImage, - PostEmbedExternal, - PostEmbedRecord, -} from '../src/app-view/db/tables/post-embed' import { Record } from '../src/db/tables/record' import { RepoSeq } from '../src/db/tables/repo-seq' import { ACKNOWLEDGE } from '../src/lexicon/types/com/atproto/admin/defs' import { UserState } from '../src/db/tables/user-state' -import { ActorBlock } from '../src/app-view/db/tables/actor-block' -import { List } from '../src/app-view/db/tables/list' -import { ListItem } from '../src/app-view/db/tables/list-item' describe('account deletion', () => { let server: util.TestServerInfo @@ -179,43 +167,10 @@ describe('account deletion', () => { (row) => row.did === carol.did && row.eventType === 'tombstone', ).length, ).toEqual(1) - }) - it('no longer stores indexed records from the user', async () => { expect(updatedDbContents.records).toEqual( initialDbContents.records.filter((row) => row.did !== carol.did), ) - expect(updatedDbContents.posts).toEqual( - initialDbContents.posts.filter((row) => row.creator !== carol.did), - ) - expect(updatedDbContents.likes).toEqual( - initialDbContents.likes.filter((row) => row.creator !== carol.did), - ) - expect(updatedDbContents.actorBlocks).toEqual( - initialDbContents.actorBlocks.filter((row) => row.creator !== carol.did), - ) - expect(updatedDbContents.lists).toEqual( - initialDbContents.lists.filter((row) => row.creator !== carol.did), - ) - expect(updatedDbContents.listItems).toEqual( - initialDbContents.listItems.filter((row) => row.creator !== carol.did), - ) - expect(updatedDbContents.reposts).toEqual( - initialDbContents.reposts.filter((row) => row.creator !== carol.did), - ) - expect(updatedDbContents.follows).toEqual( - initialDbContents.follows.filter((row) => row.creator !== carol.did), - ) - expect(updatedDbContents.postImages).toEqual( - initialDbContents.postImages.filter( - (row) => !row.postUri.includes(carol.did), - ), - ) - expect(updatedDbContents.postExternals).toEqual( - initialDbContents.postExternals.filter( - (row) => !row.postUri.includes(carol.did), - ), - ) }) it('deletes relevant blobs', async () => { @@ -269,82 +224,32 @@ type DbContents = { blocks: IpldBlock[] seqs: Selectable[] records: Record[] - posts: Post[] - postImages: PostEmbedImage[] - postExternals: PostEmbedExternal[] - postRecords: PostEmbedRecord[] - likes: Like[] - reposts: Repost[] - follows: Follow[] - actorBlocks: ActorBlock[] - lists: List[] - listItems: ListItem[] repoBlobs: RepoBlob[] blobs: Blob[] } const getDbContents = async (db: Database): Promise => { - const [ - roots, - users, - userState, - blocks, - seqs, - records, - posts, - postImages, - postExternals, - postRecords, - likes, - reposts, - follows, - actorBlocks, - lists, - listItems, - repoBlobs, - blobs, - ] = await Promise.all([ - db.db.selectFrom('repo_root').orderBy('did').selectAll().execute(), - db.db.selectFrom('user_account').orderBy('did').selectAll().execute(), - db.db.selectFrom('user_state').orderBy('did').selectAll().execute(), - db.db - .selectFrom('ipld_block') - .orderBy('creator') - .orderBy('cid') - .selectAll() - .execute(), - db.db.selectFrom('repo_seq').orderBy('id').selectAll().execute(), - db.db.selectFrom('record').orderBy('uri').selectAll().execute(), - db.db.selectFrom('post').orderBy('uri').selectAll().execute(), - db.db - .selectFrom('post_embed_image') - .orderBy('postUri') - .selectAll() - .execute(), - db.db - .selectFrom('post_embed_external') - .orderBy('postUri') - .selectAll() - .execute(), - db.db - .selectFrom('post_embed_record') - .orderBy('postUri') - .selectAll() - .execute(), - db.db.selectFrom('like').orderBy('uri').selectAll().execute(), - db.db.selectFrom('repost').orderBy('uri').selectAll().execute(), - db.db.selectFrom('follow').orderBy('uri').selectAll().execute(), - db.db.selectFrom('actor_block').orderBy('uri').selectAll().execute(), - db.db.selectFrom('list').orderBy('uri').selectAll().execute(), - db.db.selectFrom('list_item').orderBy('uri').selectAll().execute(), - db.db - .selectFrom('repo_blob') - .orderBy('did') - .orderBy('cid') - .selectAll() - .execute(), - db.db.selectFrom('blob').orderBy('cid').selectAll().execute(), - ]) + const [roots, users, userState, blocks, seqs, records, repoBlobs, blobs] = + await Promise.all([ + db.db.selectFrom('repo_root').orderBy('did').selectAll().execute(), + db.db.selectFrom('user_account').orderBy('did').selectAll().execute(), + db.db.selectFrom('user_state').orderBy('did').selectAll().execute(), + db.db + .selectFrom('ipld_block') + .orderBy('creator') + .orderBy('cid') + .selectAll() + .execute(), + db.db.selectFrom('repo_seq').orderBy('id').selectAll().execute(), + db.db.selectFrom('record').orderBy('uri').selectAll().execute(), + db.db + .selectFrom('repo_blob') + .orderBy('did') + .orderBy('cid') + .selectAll() + .execute(), + db.db.selectFrom('blob').orderBy('cid').selectAll().execute(), + ]) return { roots, @@ -353,16 +258,6 @@ const getDbContents = async (db: Database): Promise => { blocks, seqs, records, - posts, - postImages, - postExternals, - postRecords, - likes, - reposts, - follows, - actorBlocks, - lists, - listItems, repoBlobs, blobs, } diff --git a/packages/pds/tests/duplicate-records.test.ts b/packages/pds/tests/duplicate-records.test.ts deleted file mode 100644 index b435dca32f3..00000000000 --- a/packages/pds/tests/duplicate-records.test.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { CID } from 'multiformats/cid' -import { AtUri } from '@atproto/syntax' -import { cidForCbor, TID, cborEncode } from '@atproto/common' -import { CloseFn, runTestServer } from './_util' -import { Database } from '../src' -import * as lex from '../src/lexicon/lexicons' -import { Services } from '../src/services' - -describe('duplicate record', () => { - let close: CloseFn - let did: string - let db: Database - let services: Services - - beforeAll(async () => { - const server = await runTestServer({ - dbPostgresSchema: 'duplicates', - }) - db = server.ctx.db - services = server.ctx.services - close = server.close - did = 'did:example:alice' - }) - - afterAll(async () => { - await close() - }) - - const countRecords = async (db: Database, table: string) => { - const got = await db.db - .selectFrom(table as any) - .selectAll() - .where('creator', '=', did) - .execute() - return got.length - } - - const putBlock = async ( - db: Database, - creator: string, - data: object, - ): Promise => { - const cid = await cidForCbor(data) - const bytes = await cborEncode(data) - await db.db - .insertInto('ipld_block') - .values({ - cid: cid.toString(), - creator, - size: bytes.length, - content: bytes, - }) - .onConflict((oc) => oc.doNothing()) - .execute() - return cid - } - - it('dedupes reposts', async () => { - const subject = AtUri.make(did, lex.ids.AppBskyFeedPost, TID.nextStr()) - const subjectCid = await putBlock(db, did, { test: 'blah' }) - const coll = lex.ids.AppBskyFeedRepost - const uris: AtUri[] = [] - await db.transaction(async (tx) => { - for (let i = 0; i < 5; i++) { - const repost = { - $type: coll, - subject: { - uri: subject.toString(), - cid: subjectCid.toString(), - }, - createdAt: new Date().toISOString(), - } - const uri = AtUri.make(did, coll, TID.nextStr()) - const cid = await putBlock(tx, did, repost) - await services.record(tx).indexRecord(uri, cid, repost) - uris.push(uri) - } - }) - - let count = await countRecords(db, 'repost') - expect(count).toBe(1) - - await db.transaction(async (tx) => { - await services.record(tx).deleteRecord(uris[0], false) - }) - - count = await countRecords(db, 'repost') - expect(count).toBe(1) - - await db.transaction(async (tx) => { - await services.record(tx).deleteRecord(uris[1], true) - }) - - count = await countRecords(db, 'repost') - expect(count).toBe(0) - }) - - it('dedupes likes', async () => { - const subject = AtUri.make(did, lex.ids.AppBskyFeedPost, TID.nextStr()) - const subjectCid = await putBlock(db, did, { test: 'blah' }) - const coll = lex.ids.AppBskyFeedLike - const uris: AtUri[] = [] - await db.transaction(async (tx) => { - for (let i = 0; i < 5; i++) { - const like = { - $type: coll, - subject: { - uri: subject.toString(), - cid: subjectCid.toString(), - }, - createdAt: new Date().toISOString(), - } - const uri = AtUri.make(did, coll, TID.nextStr()) - const cid = await putBlock(tx, did, like) - await services.record(tx).indexRecord(uri, cid, like) - uris.push(uri) - } - }) - - let count = await countRecords(db, 'like') - expect(count).toBe(1) - - await db.transaction(async (tx) => { - await services.record(tx).deleteRecord(uris[0], false) - }) - - count = await countRecords(db, 'like') - expect(count).toBe(1) - - const got = await db.db - .selectFrom('like') - .where('creator', '=', did) - .selectAll() - .executeTakeFirst() - expect(got?.uri).toEqual(uris[1].toString()) - - await db.transaction(async (tx) => { - await services.record(tx).deleteRecord(uris[1], true) - }) - - count = await countRecords(db, 'like') - expect(count).toBe(0) - }) - - it('dedupes follows', async () => { - const coll = lex.ids.AppBskyGraphFollow - const uris: AtUri[] = [] - await db.transaction(async (tx) => { - for (let i = 0; i < 5; i++) { - const follow = { - $type: coll, - subject: 'did:example:bob', - createdAt: new Date().toISOString(), - } - const uri = AtUri.make(did, coll, TID.nextStr()) - const cid = await putBlock(tx, did, follow) - await services.record(tx).indexRecord(uri, cid, follow) - uris.push(uri) - } - }) - - let count = await countRecords(db, 'follow') - expect(count).toBe(1) - - await db.transaction(async (tx) => { - await services.record(tx).deleteRecord(uris[0], false) - }) - - count = await countRecords(db, 'follow') - expect(count).toBe(1) - - await db.transaction(async (tx) => { - await services.record(tx).deleteRecord(uris[1], true) - }) - - count = await countRecords(db, 'follow') - expect(count).toBe(0) - }) -}) diff --git a/packages/pds/tests/labeler/apply-labels.test.ts b/packages/pds/tests/labeler/apply-labels.test.ts deleted file mode 100644 index 80fedc459d5..00000000000 --- a/packages/pds/tests/labeler/apply-labels.test.ts +++ /dev/null @@ -1,180 +0,0 @@ -import AtpAgent from '@atproto/api' -import { TestNetworkNoAppView } from '@atproto/dev-env' -import { adminAuth, moderatorAuth } from '../_util' -import { SeedClient } from '../seeds/client' -import basicSeed from '../seeds/basic' - -describe('unspecced.applyLabels', () => { - let network: TestNetworkNoAppView - let agent: AtpAgent - let sc: SeedClient - - beforeAll(async () => { - network = await TestNetworkNoAppView.create({ - dbPostgresSchema: 'apply_labels', - }) - agent = network.pds.getClient() - sc = new SeedClient(agent) - await basicSeed(sc) - }) - - afterAll(async () => { - await network.close() - }) - - it('requires admin auth.', async () => { - const tryToLabel = agent.api.app.bsky.unspecced.applyLabels( - { - labels: [ - { - src: network.pds.ctx.cfg.labelerDid, - uri: sc.dids.carol, - val: 'cats', - neg: false, - cts: new Date().toISOString(), - }, - ], - }, - { - encoding: 'application/json', - headers: { authorization: moderatorAuth() }, - }, - ) - await expect(tryToLabel).rejects.toThrow('Insufficient privileges') - }) - - it('adds and removes labels on record as though applied by the labeler.', async () => { - const post = sc.posts[sc.dids.bob][1].ref - await agent.api.app.bsky.unspecced.applyLabels( - { - labels: [ - { - src: network.pds.ctx.cfg.labelerDid, - uri: post.uriStr, - cid: post.cidStr, - val: 'birds', - neg: false, - cts: new Date().toISOString(), - }, - { - src: network.pds.ctx.cfg.labelerDid, - uri: post.uriStr, - cid: post.cidStr, - val: 'bats', - neg: false, - cts: new Date().toISOString(), - }, - ], - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - await expect(getRecordLabels(post.uriStr)).resolves.toEqual([ - 'birds', - 'bats', - ]) - await agent.api.app.bsky.unspecced.applyLabels( - { - labels: [ - { - src: network.pds.ctx.cfg.labelerDid, - uri: post.uriStr, - cid: post.cidStr, - val: 'birds', - neg: true, - cts: new Date().toISOString(), - }, - { - src: network.pds.ctx.cfg.labelerDid, - uri: post.uriStr, - cid: post.cidStr, - val: 'bats', - neg: true, - cts: new Date().toISOString(), - }, - ], - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - await expect(getRecordLabels(post.uriStr)).resolves.toEqual([]) - }) - - it('adds and removes labels on repo as though applied by the labeler.', async () => { - await agent.api.app.bsky.unspecced.applyLabels( - { - labels: [ - { - src: network.pds.ctx.cfg.labelerDid, - uri: sc.dids.carol, - val: 'birds', - neg: false, - cts: new Date().toISOString(), - }, - { - src: network.pds.ctx.cfg.labelerDid, - uri: sc.dids.carol, - val: 'bats', - neg: false, - cts: new Date().toISOString(), - }, - ], - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - await expect(getRepoLabels(sc.dids.carol)).resolves.toEqual([ - 'birds', - 'bats', - ]) - await agent.api.app.bsky.unspecced.applyLabels( - { - labels: [ - { - src: network.pds.ctx.cfg.labelerDid, - uri: sc.dids.carol, - val: 'birds', - neg: true, - cts: new Date().toISOString(), - }, - { - src: network.pds.ctx.cfg.labelerDid, - uri: sc.dids.carol, - val: 'bats', - neg: true, - cts: new Date().toISOString(), - }, - ], - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - await expect(getRepoLabels(sc.dids.carol)).resolves.toEqual([]) - }) - - async function getRecordLabels(uri: string) { - const result = await agent.api.com.atproto.admin.getRecord( - { uri }, - { headers: { authorization: adminAuth() } }, - ) - const labels = result.data.labels ?? [] - return labels.map((l) => l.val) - } - - async function getRepoLabels(did: string) { - const result = await agent.api.com.atproto.admin.getRepo( - { did }, - { headers: { authorization: adminAuth() } }, - ) - const labels = result.data.labels ?? [] - return labels.map((l) => l.val) - } -}) diff --git a/packages/pds/tests/labeler/fixtures/hiveai_resp_example.json b/packages/pds/tests/labeler/fixtures/hiveai_resp_example.json deleted file mode 100644 index 2315fa9d0c0..00000000000 --- a/packages/pds/tests/labeler/fixtures/hiveai_resp_example.json +++ /dev/null @@ -1,401 +0,0 @@ -{ - "id": "02122580-c37f-11ed-81d2-000000000000", - "code": 200, - "project_id": 12345, - "user_id": 12345, - "created_on": "2023-03-15T22:16:18.408Z", - "status": [ - { - "status": { - "code": "0", - "message": "SUCCESS" - }, - "response": { - "input": { - "id": "02122580-c37f-11ed-81d2-000000000000", - "charge": 0.003, - "model": "mod55_dense", - "model_version": 1, - "model_type": "CATEGORIZATION", - "created_on": "2023-03-15T22:16:18.136Z", - "media": { - "url": null, - "filename": "bafkreiam7k6mvkyuoybq4ynhljvj5xa75sdbhjbolzjf5j2udx7vj5gnsy", - "type": "PHOTO", - "mime_type": "jpeg", - "mimetype": "image/jpeg", - "width": 800, - "height": 800, - "num_frames": 1, - "duration": 0 - }, - "user_id": 12345, - "project_id": 12345, - "config_version": 1, - "config_tag": "default" - }, - "output": [ - { - "time": 0, - "classes": [ - { - "class": "general_not_nsfw_not_suggestive", - "score": 0.9998097218132356 - }, - { - "class": "general_nsfw", - "score": 8.857344804177162e-5 - }, - { - "class": "general_suggestive", - "score": 0.00010170473872266839 - }, - { - "class": "no_female_underwear", - "score": 0.9999923079040384 - }, - { - "class": "yes_female_underwear", - "score": 7.692095961599136e-6 - }, - { - "class": "no_male_underwear", - "score": 0.9999984904867634 - }, - { - "class": "yes_male_underwear", - "score": 1.5095132367094679e-6 - }, - { - "class": "no_sex_toy", - "score": 0.9999970970762551 - }, - { - "class": "yes_sex_toy", - "score": 2.9029237450490604e-6 - }, - { - "class": "no_female_nudity", - "score": 0.9999739028909301 - }, - { - "class": "yes_female_nudity", - "score": 2.60971090699536e-5 - }, - { - "class": "no_male_nudity", - "score": 0.9999711373083747 - }, - { - "class": "yes_male_nudity", - "score": 2.8862691625255323e-5 - }, - { - "class": "no_female_swimwear", - "score": 0.9999917609899659 - }, - { - "class": "yes_female_swimwear", - "score": 8.239010034025379e-6 - }, - { - "class": "no_male_shirtless", - "score": 0.9999583350744331 - }, - { - "class": "yes_male_shirtless", - "score": 4.166492556688088e-5 - }, - { - "class": "no_text", - "score": 0.9958378716447616 - }, - { - "class": "text", - "score": 0.0041621283552384265 - }, - { - "class": "animated", - "score": 0.46755478950048235 - }, - { - "class": "hybrid", - "score": 0.0011440363434524984 - }, - { - "class": "natural", - "score": 0.5313011741560651 - }, - { - "class": "animated_gun", - "score": 2.0713000782979496e-5 - }, - { - "class": "gun_in_hand", - "score": 1.5844730446534659e-6 - }, - { - "class": "gun_not_in_hand", - "score": 1.0338973818006654e-6 - }, - { - "class": "no_gun", - "score": 0.9999766686287906 - }, - { - "class": "culinary_knife_in_hand", - "score": 3.8063500083369785e-6 - }, - { - "class": "culinary_knife_not_in_hand", - "score": 7.94057948996249e-7 - }, - { - "class": "knife_in_hand", - "score": 4.5578955723278505e-7 - }, - { - "class": "knife_not_in_hand", - "score": 3.842124714748908e-7 - }, - { - "class": "no_knife", - "score": 0.999994559590014 - }, - { - "class": "a_little_bloody", - "score": 2.1317745626539786e-7 - }, - { - "class": "no_blood", - "score": 0.9999793341236429 - }, - { - "class": "other_blood", - "score": 2.0322054269591763e-5 - }, - { - "class": "very_bloody", - "score": 1.306446309561673e-7 - }, - { - "class": "no_pills", - "score": 0.9999989592376954 - }, - { - "class": "yes_pills", - "score": 1.0407623044588633e-6 - }, - { - "class": "no_smoking", - "score": 0.9999939101969173 - }, - { - "class": "yes_smoking", - "score": 6.089803082758281e-6 - }, - { - "class": "illicit_injectables", - "score": 6.925695592003094e-7 - }, - { - "class": "medical_injectables", - "score": 8.587808234452378e-7 - }, - { - "class": "no_injectables", - "score": 0.9999984486496174 - }, - { - "class": "no_nazi", - "score": 0.9999987449628097 - }, - { - "class": "yes_nazi", - "score": 1.2550371902234279e-6 - }, - { - "class": "no_kkk", - "score": 0.999999762417549 - }, - { - "class": "yes_kkk", - "score": 2.3758245111050425e-7 - }, - { - "class": "no_middle_finger", - "score": 0.9999881515231847 - }, - { - "class": "yes_middle_finger", - "score": 1.184847681536747e-5 - }, - { - "class": "no_terrorist", - "score": 0.9999998870793229 - }, - { - "class": "yes_terrorist", - "score": 1.1292067715380635e-7 - }, - { - "class": "no_overlay_text", - "score": 0.9996453363440359 - }, - { - "class": "yes_overlay_text", - "score": 0.0003546636559640924 - }, - { - "class": "no_sexual_activity", - "score": 0.9999563580374798 - }, - { - "class": "yes_sexual_activity", - "score": 0.99, - "realScore": 4.364196252012032e-5 - }, - { - "class": "hanging", - "score": 3.6435135762510905e-7 - }, - { - "class": "no_hanging_no_noose", - "score": 0.9999980779196416 - }, - { - "class": "noose", - "score": 1.5577290007796094e-6 - }, - { - "class": "no_realistic_nsfw", - "score": 0.9999944341007805 - }, - { - "class": "yes_realistic_nsfw", - "score": 5.565899219571182e-6 - }, - { - "class": "animated_corpse", - "score": 5.276802046755426e-7 - }, - { - "class": "human_corpse", - "score": 2.5449360984211012e-8 - }, - { - "class": "no_corpse", - "score": 0.9999994468704343 - }, - { - "class": "no_self_harm", - "score": 0.9999994515625507 - }, - { - "class": "yes_self_harm", - "score": 5.484374493605692e-7 - }, - { - "class": "no_drawing", - "score": 0.9978276028816608 - }, - { - "class": "yes_drawing", - "score": 0.0021723971183392485 - }, - { - "class": "no_emaciated_body", - "score": 0.9999998146500432 - }, - { - "class": "yes_emaciated_body", - "score": 1.853499568724518e-7 - }, - { - "class": "no_child_present", - "score": 0.9999970498515446 - }, - { - "class": "yes_child_present", - "score": 2.950148455380443e-6 - }, - { - "class": "no_sexual_intent", - "score": 0.9999963861546292 - }, - { - "class": "yes_sexual_intent", - "score": 3.613845370766111e-6 - }, - { - "class": "animal_genitalia_and_human", - "score": 2.255472023465222e-8 - }, - { - "class": "animal_genitalia_only", - "score": 4.6783185199931176e-7 - }, - { - "class": "animated_animal_genitalia", - "score": 6.707857419436447e-7 - }, - { - "class": "no_animal_genitalia", - "score": 0.9999988388276858 - }, - { - "class": "no_gambling", - "score": 0.9999960939687145 - }, - { - "class": "yes_gambling", - "score": 3.906031285604864e-6 - }, - { - "class": "no_undressed", - "score": 0.99999923356218 - }, - { - "class": "yes_undressed", - "score": 7.664378199789045e-7 - }, - { - "class": "no_confederate", - "score": 0.9999925456900376 - }, - { - "class": "yes_confederate", - "score": 7.454309962453175e-6 - }, - { - "class": "animated_alcohol", - "score": 1.8109949948066074e-6 - }, - { - "class": "no_alcohol", - "score": 0.9999916620957963 - }, - { - "class": "yes_alcohol", - "score": 5.88781463445443e-6 - }, - { - "class": "yes_drinking_alcohol", - "score": 6.390945746578106e-7 - }, - { - "class": "no_religious_icon", - "score": 0.9999862158580689 - }, - { - "class": "yes_religious_icon", - "score": 1.3784141931119298e-5 - } - ] - } - ] - } - } - ], - "from_cache": false -} diff --git a/packages/pds/tests/labeler/hive.test.ts b/packages/pds/tests/labeler/hive.test.ts deleted file mode 100644 index 3213d794e30..00000000000 --- a/packages/pds/tests/labeler/hive.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import fs from 'fs/promises' -import * as hive from '../../src/labeler/hive' - -describe('labeling', () => { - it('correctly parses hive responses', async () => { - const exampleRespBytes = await fs.readFile( - 'tests/labeler/fixtures/hiveai_resp_example.json', - ) - const exampleResp = JSON.parse(exampleRespBytes.toString()) - const classes = hive.respToClasses(exampleResp) - expect(classes.length).toBeGreaterThan(10) - - const labels = hive.summarizeLabels(classes) - expect(labels).toEqual(['porn']) - }) -}) diff --git a/packages/pds/tests/labeler/labeler.test.ts b/packages/pds/tests/labeler/labeler.test.ts deleted file mode 100644 index 646e5d621d9..00000000000 --- a/packages/pds/tests/labeler/labeler.test.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { AtUri, BlobRef } from '@atproto/api' -import { runTestServer, CloseFn, TestServerInfo } from '../_util' -import { Labeler } from '../../src/labeler' -import { AppContext, Database } from '../../src' -import { BlobStore, cidForRecord } from '@atproto/repo' -import { keywordLabeling } from '../../src/labeler/util' -import { cidForCbor, TID } from '@atproto/common' -import { LabelService } from '../../src/app-view/services/label' -import { BackgroundQueue } from '../../src/event-stream/background-queue' -import { CID } from 'multiformats/cid' - -// outside of test suite so that TestLabeler can access them -let badCid1: CID | undefined = undefined -let badCid2: CID | undefined = undefined - -describe('labeler', () => { - let server: TestServerInfo - let close: CloseFn - let labeler: Labeler - let labelSrvc: LabelService - let ctx: AppContext - let labelerDid: string - let badBlob1: BlobRef - let badBlob2: BlobRef - let goodBlob: BlobRef - - beforeAll(async () => { - server = await runTestServer({ - dbPostgresSchema: 'labeler', - }) - close = server.close - ctx = server.ctx - labelerDid = ctx.cfg.labelerDid - labeler = new TestLabeler({ - db: ctx.db, - blobstore: ctx.blobstore, - backgroundQueue: ctx.backgroundQueue, - labelerDid, - keywords: { label_me: 'test-label', another_label: 'another-label' }, - }) - labelSrvc = ctx.services.appView.label(ctx.db) - const bytes1 = new Uint8Array([1, 2, 3, 4]) - const bytes2 = new Uint8Array([5, 6, 7, 8]) - const bytes3 = new Uint8Array([4, 3, 2, 1]) - const cid1 = await cidForCbor(bytes1) - const cid2 = await cidForCbor(bytes2) - const cid3 = await cidForCbor(bytes3) - ctx.blobstore.putPermanent(cid1, bytes1) - ctx.blobstore.putPermanent(cid2, bytes2) - ctx.blobstore.putPermanent(cid3, bytes3) - badBlob1 = new BlobRef(cid1, 'image/jpeg', 4) - badBlob2 = new BlobRef(cid2, 'image/jpeg', 4) - goodBlob = new BlobRef(cid3, 'image/jpeg', 4) - badCid1 = badBlob1.ref - badCid2 = badBlob2.ref - }) - - afterAll(async () => { - await close() - }) - - it('labels text in posts', async () => { - const post = { - $type: 'app.bsky.feed.post', - text: 'blah blah label_me', - createdAt: new Date().toISOString(), - } - const cid = await cidForRecord(post) - const uri = postUri() - labeler.processRecord(uri, post) - await labeler.processAll() - await server.processAll() - const labels = await labelSrvc.getLabels(uri.toString()) - expect(labels.length).toBe(1) - expect(labels[0]).toMatchObject({ - src: labelerDid, - uri: uri.toString(), - cid: cid.toString(), - val: 'test-label', - neg: false, - }) - }) - - it('labels embeds in posts', async () => { - const post = { - $type: 'app.bsky.feed.post', - text: 'blah blah', - embed: { - $type: 'app.bsky.embed.images', - images: [ - { - image: badBlob1, - alt: 'img', - }, - { - image: badBlob2, - alt: 'another_label', - }, - { - image: goodBlob, - alt: 'img', - }, - ], - }, - createdAt: new Date().toISOString(), - } - const uri = postUri() - labeler.processRecord(uri, post) - await labeler.processAll() - await server.processAll() - const dbLabels = await labelSrvc.getLabels(uri.toString()) - const labels = dbLabels.map((row) => row.val).sort() - expect(labels).toEqual( - ['another-label', 'img-label', 'other-img-label'].sort(), - ) - }) - - it('retrieves repo labels on profile views', async () => { - await ctx.db.db - .insertInto('label') - .values({ - src: labelerDid, - uri: aliceDid, - cid: '', - val: 'repo-label', - neg: 0, - cts: new Date().toISOString(), - }) - .execute() - await server.processAll() - - const labels = await labelSrvc.getLabelsForProfile('did:example:alice') - // 4 from earlier & then just added one - expect(labels.length).toBe(1) - expect(labels[0]).toMatchObject({ - src: labelerDid, - uri: aliceDid, - val: 'repo-label', - neg: false, - }) - }) -}) - -const aliceDid = 'did:example:alice' - -const postUri = () => AtUri.make(aliceDid, 'app.bsky.feed.post', TID.nextStr()) - -class TestLabeler extends Labeler { - hiveApiKey: string - keywords: Record - - constructor(opts: { - db: Database - blobstore: BlobStore - backgroundQueue: BackgroundQueue - labelerDid: string - keywords: Record - }) { - const { db, blobstore, backgroundQueue, labelerDid, keywords } = opts - super({ db, blobstore, backgroundQueue, labelerDid }) - this.keywords = keywords - } - - async labelText(text: string): Promise { - return keywordLabeling(this.keywords, text) - } - - async labelImg(cid: CID): Promise { - if (cid.equals(badCid1)) { - return ['img-label'] - } - - if (cid.equals(badCid2)) { - return ['other-img-label'] - } - return [] - } -} diff --git a/packages/pds/tests/moderation.test.ts b/packages/pds/tests/moderation.test.ts index 34e52a156a0..edbb23c6578 100644 --- a/packages/pds/tests/moderation.test.ts +++ b/packages/pds/tests/moderation.test.ts @@ -1,4 +1,4 @@ -import AtpAgent, { ComAtprotoAdminTakeModerationAction } from '@atproto/api' +import AtpAgent from '@atproto/api' import { AtUri } from '@atproto/syntax' import { BlobNotFoundError } from '@atproto/repo' import { @@ -827,164 +827,6 @@ describe('moderation', () => { ) }) - it('negates an existing label and reverses.', async () => { - const { ctx } = server - const post = sc.posts[sc.dids.bob][0].ref - const labelingService = ctx.services.appView.label(ctx.db) - await labelingService.formatAndCreate( - ctx.cfg.labelerDid, - post.uriStr, - post.cidStr, - { create: ['kittens'] }, - ) - const action = await actionWithLabels({ - negateLabelVals: ['kittens'], - subject: { - $type: 'com.atproto.repo.strongRef', - uri: post.uriStr, - cid: post.cidStr, - }, - }) - await expect(getRecordLabels(post.uriStr)).resolves.toEqual([]) - await reverse(action.id) - await expect(getRecordLabels(post.uriStr)).resolves.toEqual(['kittens']) - // Cleanup - await labelingService.formatAndCreate( - ctx.cfg.labelerDid, - post.uriStr, - post.cidStr, - { negate: ['kittens'] }, - ) - }) - - it('no-ops when negating an already-negated label and reverses.', async () => { - const { ctx } = server - const post = sc.posts[sc.dids.bob][0].ref - const labelingService = ctx.services.appView.label(ctx.db) - const action = await actionWithLabels({ - negateLabelVals: ['bears'], - subject: { - $type: 'com.atproto.repo.strongRef', - uri: post.uriStr, - cid: post.cidStr, - }, - }) - await expect(getRecordLabels(post.uriStr)).resolves.toEqual([]) - await reverse(action.id) - await expect(getRecordLabels(post.uriStr)).resolves.toEqual(['bears']) - // Cleanup - await labelingService.formatAndCreate( - ctx.cfg.labelerDid, - post.uriStr, - post.cidStr, - { negate: ['bears'] }, - ) - }) - - it('creates non-existing labels and reverses.', async () => { - const post = sc.posts[sc.dids.bob][0].ref - const action = await actionWithLabels({ - createLabelVals: ['puppies', 'doggies'], - negateLabelVals: [], - subject: { - $type: 'com.atproto.repo.strongRef', - uri: post.uriStr, - cid: post.cidStr, - }, - }) - await expect(getRecordLabels(post.uriStr)).resolves.toEqual([ - 'puppies', - 'doggies', - ]) - await reverse(action.id) - await expect(getRecordLabels(post.uriStr)).resolves.toEqual([]) - }) - - it('no-ops when creating an existing label and reverses.', async () => { - const { ctx } = server - const post = sc.posts[sc.dids.bob][0].ref - const labelingService = ctx.services.appView.label(ctx.db) - await labelingService.formatAndCreate( - ctx.cfg.labelerDid, - post.uriStr, - post.cidStr, - { create: ['birds'] }, - ) - const action = await actionWithLabels({ - createLabelVals: ['birds'], - subject: { - $type: 'com.atproto.repo.strongRef', - uri: post.uriStr, - cid: post.cidStr, - }, - }) - await expect(getRecordLabels(post.uriStr)).resolves.toEqual(['birds']) - await reverse(action.id) - await expect(getRecordLabels(post.uriStr)).resolves.toEqual([]) - }) - - it('creates labels on a repo and reverses.', async () => { - const action = await actionWithLabels({ - createLabelVals: ['puppies', 'doggies'], - negateLabelVals: [], - subject: { - $type: 'com.atproto.admin.defs#repoRef', - did: sc.dids.bob, - }, - }) - await expect(getRepoLabels(sc.dids.bob)).resolves.toEqual([ - 'puppies', - 'doggies', - ]) - await reverse(action.id) - await expect(getRepoLabels(sc.dids.bob)).resolves.toEqual([]) - }) - - it('creates and negates labels on a repo and reverses.', async () => { - const { ctx } = server - const labelingService = ctx.services.appView.label(ctx.db) - await labelingService.formatAndCreate( - ctx.cfg.labelerDid, - sc.dids.bob, - null, - { create: ['kittens'] }, - ) - const action = await actionWithLabels({ - createLabelVals: ['puppies'], - negateLabelVals: ['kittens'], - subject: { - $type: 'com.atproto.admin.defs#repoRef', - did: sc.dids.bob, - }, - }) - await expect(getRepoLabels(sc.dids.bob)).resolves.toEqual(['puppies']) - await reverse(action.id) - await expect(getRepoLabels(sc.dids.bob)).resolves.toEqual(['kittens']) - }) - - it('does not allow triage moderators to label.', async () => { - const attemptLabel = agent.api.com.atproto.admin.takeModerationAction( - { - action: ACKNOWLEDGE, - createdBy: 'did:example:moderator', - reason: 'Y', - subject: { - $type: 'com.atproto.admin.defs#repoRef', - did: sc.dids.bob, - }, - negateLabelVals: ['a'], - createLabelVals: ['b', 'c'], - }, - { - encoding: 'application/json', - headers: { authorization: triageAuth() }, - }, - ) - await expect(attemptLabel).rejects.toThrow( - 'Must be a full moderator to label content', - ) - }) - it('allows full moderators to takedown.', async () => { const { data: action } = await agent.api.com.atproto.admin.takeModerationAction( @@ -1017,7 +859,6 @@ describe('moderation', () => { $type: 'com.atproto.admin.defs#repoRef', did: sc.dids.bob, }, - createLabelVals: ['takendown'], // Use negative value to set the expiry time in the past so that the action is automatically reversed // right away without having to wait n number of hours for a successful assertion durationInHours: -1, @@ -1028,8 +869,6 @@ describe('moderation', () => { }, ) - const labelsAfterTakedown = await getRepoLabels(sc.dids.bob) - expect(labelsAfterTakedown).toContain('takendown') // In the actual app, this will be instantiated and run on server startup const periodicReversal = new PeriodicModerationActionReversal(server.ctx) await periodicReversal.findAndRevertDueActions() @@ -1046,10 +885,6 @@ describe('moderation', () => { createdBy: action.createdBy, reason: '[SCHEDULED_REVERSAL] Reverting action as originally scheduled', }) - - // Verify that labels are also reversed when takedown action is reversed - const labelsAfterReversal = await getRepoLabels(sc.dids.bob) - expect(labelsAfterReversal).not.toContain('takendown') }) it('does not allow non-full moderators to takedown.', async () => { @@ -1074,26 +909,6 @@ describe('moderation', () => { ) }) - async function actionWithLabels( - opts: Partial & { - subject: ComAtprotoAdminTakeModerationAction.InputSchema['subject'] - }, - ) { - const result = await agent.api.com.atproto.admin.takeModerationAction( - { - action: FLAG, - createdBy: 'did:example:admin', - reason: 'Y', - ...opts, - }, - { - encoding: 'application/json', - headers: { authorization: adminAuth() }, - }, - ) - return result.data - } - async function reverse(actionId: number) { await agent.api.com.atproto.admin.reverseModerationAction( { @@ -1107,24 +922,6 @@ describe('moderation', () => { }, ) } - - async function getRecordLabels(uri: string) { - const result = await agent.api.com.atproto.admin.getRecord( - { uri }, - { headers: { authorization: adminAuth() } }, - ) - const labels = result.data.labels ?? [] - return labels.map((l) => l.val) - } - - async function getRepoLabels(did: string) { - const result = await agent.api.com.atproto.admin.getRepo( - { did }, - { headers: { authorization: adminAuth() } }, - ) - const labels = result.data.labels ?? [] - return labels.map((l) => l.val) - } }) describe('blob takedown', () => { From a6b4b4fd1bb91c3f52cd3f373263913fcedb7dd6 Mon Sep 17 00:00:00 2001 From: dholms Date: Thu, 21 Sep 2023 22:18:14 -0500 Subject: [PATCH 20/31] rm unused sharp code --- packages/pds/src/image/index.ts | 82 ++++++++++- packages/pds/src/image/sharp.ts | 93 ------------- packages/pds/src/image/util.ts | 32 ----- packages/pds/tests/image/sharp.test.ts | 185 ------------------------- 4 files changed, 80 insertions(+), 312 deletions(-) delete mode 100644 packages/pds/src/image/sharp.ts delete mode 100644 packages/pds/src/image/util.ts delete mode 100644 packages/pds/tests/image/sharp.test.ts diff --git a/packages/pds/src/image/index.ts b/packages/pds/src/image/index.ts index 3197b5aeb5c..ebf47088f6b 100644 --- a/packages/pds/src/image/index.ts +++ b/packages/pds/src/image/index.ts @@ -1,2 +1,80 @@ -export * from './sharp' -export type { Options, ImageInfo } from './util' +import { Readable } from 'stream' +import { pipeline } from 'stream/promises' +import sharp from 'sharp' +import { errHasMsg } from '@atproto/common' + +export async function maybeGetInfo( + stream: Readable, +): Promise { + let metadata: sharp.Metadata + try { + const processor = sharp() + const [result] = await Promise.all([ + processor.metadata(), + pipeline(stream, processor), // Handles error propagation + ]) + metadata = result + } catch (err) { + if (errHasMsg(err, 'Input buffer contains unsupported image format')) { + return null + } + throw err + } + const { size, height, width, format } = metadata + if ( + size === undefined || + height === undefined || + width === undefined || + format === undefined + ) { + return null + } + + return { + height, + width, + size, + mime: formatsToMimes[format] ?? ('unknown' as const), + } +} + +export async function getInfo(stream: Readable): Promise { + const maybeInfo = await maybeGetInfo(stream) + if (!maybeInfo) { + throw new Error('could not obtain all image metadata') + } + return maybeInfo +} + +export type Options = Dimensions & { + format: 'jpeg' | 'png' + // When 'cover' (default), scale to fill given dimensions, cropping if necessary. + // When 'inside', scale to fit within given dimensions. + fit?: 'cover' | 'inside' + // When false (default), do not scale up. + // When true, scale up to hit dimensions given in options. + // Otherwise, scale up to hit specified min dimensions. + min?: Dimensions | boolean + // A number 1-100 + quality?: number +} + +export type ImageInfo = Dimensions & { + size: number + mime: `image/${string}` | 'unknown' +} + +export type Dimensions = { height: number; width: number } + +export const formatsToMimes: { + [s in keyof sharp.FormatEnum]?: `image/${string}` +} = { + jpg: 'image/jpeg', + jpeg: 'image/jpeg', + png: 'image/png', + gif: 'image/gif', + svg: 'image/svg+xml', + tif: 'image/tiff', + tiff: 'image/tiff', + webp: 'image/webp', +} diff --git a/packages/pds/src/image/sharp.ts b/packages/pds/src/image/sharp.ts deleted file mode 100644 index 1edc7a58835..00000000000 --- a/packages/pds/src/image/sharp.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { Readable } from 'stream' -import { pipeline } from 'stream/promises' -import sharp from 'sharp' -import { errHasMsg, forwardStreamErrors } from '@atproto/common' -import { formatsToMimes, ImageInfo, Options } from './util' - -export type { Options } - -export async function resize( - stream: Readable, - options: Options, -): Promise { - const { height, width, min = false, fit = 'cover', format, quality } = options - - let processor = sharp() - - // Scale up to hit any specified minimum size - if (typeof min !== 'boolean') { - const upsizeProcessor = sharp().resize({ - fit: 'outside', - width: min.width, - height: min.height, - withoutReduction: true, - withoutEnlargement: false, - }) - forwardStreamErrors(stream, upsizeProcessor) - stream = stream.pipe(upsizeProcessor) - } - - // Scale down (or possibly up if min is true) to desired size - processor = processor.resize({ - fit, - width, - height, - withoutEnlargement: min !== true, - }) - - // Output to specified format - if (format === 'jpeg') { - processor = processor.jpeg({ quality: quality ?? 100 }) - } else if (format === 'png') { - processor = processor.png({ quality: quality ?? 100 }) - } else { - const exhaustiveCheck: never = format - throw new Error(`Unhandled case: ${exhaustiveCheck}`) - } - - forwardStreamErrors(stream, processor) - return stream.pipe(processor) -} - -export async function maybeGetInfo( - stream: Readable, -): Promise { - let metadata: sharp.Metadata - try { - const processor = sharp() - const [result] = await Promise.all([ - processor.metadata(), - pipeline(stream, processor), // Handles error propagation - ]) - metadata = result - } catch (err) { - if (errHasMsg(err, 'Input buffer contains unsupported image format')) { - return null - } - throw err - } - const { size, height, width, format } = metadata - if ( - size === undefined || - height === undefined || - width === undefined || - format === undefined - ) { - return null - } - - return { - height, - width, - size, - mime: formatsToMimes[format] ?? ('unknown' as const), - } -} - -export async function getInfo(stream: Readable): Promise { - const maybeInfo = await maybeGetInfo(stream) - if (!maybeInfo) { - throw new Error('could not obtain all image metadata') - } - return maybeInfo -} diff --git a/packages/pds/src/image/util.ts b/packages/pds/src/image/util.ts deleted file mode 100644 index ce18ba343d5..00000000000 --- a/packages/pds/src/image/util.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { FormatEnum } from 'sharp' - -export type Options = Dimensions & { - format: 'jpeg' | 'png' - // When 'cover' (default), scale to fill given dimensions, cropping if necessary. - // When 'inside', scale to fit within given dimensions. - fit?: 'cover' | 'inside' - // When false (default), do not scale up. - // When true, scale up to hit dimensions given in options. - // Otherwise, scale up to hit specified min dimensions. - min?: Dimensions | boolean - // A number 1-100 - quality?: number -} - -export type ImageInfo = Dimensions & { - size: number - mime: `image/${string}` | 'unknown' -} - -export type Dimensions = { height: number; width: number } - -export const formatsToMimes: { [s in keyof FormatEnum]?: `image/${string}` } = { - jpg: 'image/jpeg', - jpeg: 'image/jpeg', - png: 'image/png', - gif: 'image/gif', - svg: 'image/svg+xml', - tif: 'image/tiff', - tiff: 'image/tiff', - webp: 'image/webp', -} diff --git a/packages/pds/tests/image/sharp.test.ts b/packages/pds/tests/image/sharp.test.ts deleted file mode 100644 index d0a46b662b3..00000000000 --- a/packages/pds/tests/image/sharp.test.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { createReadStream } from 'fs' -import { Options, getInfo, resize } from '../../src/image/sharp' - -describe('sharp image processor', () => { - it('scales up to cover.', async () => { - const result = await processFixture('key-landscape-small.jpg', { - format: 'jpeg', - fit: 'cover', - width: 500, - height: 500, - min: true, - }) - expect(result).toEqual( - expect.objectContaining({ - height: 500, - width: 500, - }), - ) - }) - - it('scales up to inside (landscape).', async () => { - const result = await processFixture('key-landscape-small.jpg', { - format: 'jpeg', - fit: 'inside', - width: 500, - height: 500, - min: true, - }) - expect(result).toEqual( - expect.objectContaining({ - height: 290, - width: 500, - }), - ) - }) - - it('scales up to inside (portrait).', async () => { - const result = await processFixture('key-portrait-small.jpg', { - format: 'jpeg', - fit: 'inside', - width: 500, - height: 500, - min: true, - }) - expect(result).toEqual( - expect.objectContaining({ - height: 500, - width: 290, - }), - ) - }) - - it('scales up to min.', async () => { - const result = await processFixture('key-landscape-small.jpg', { - format: 'jpeg', - width: 500, - height: 500, - min: { height: 200, width: 200 }, - }) - expect(result).toEqual( - expect.objectContaining({ - height: 200, - width: 345, - }), - ) - }) - - it('does not scale image up when min is false.', async () => { - const result = await processFixture('key-landscape-small.jpg', { - format: 'jpeg', - width: 500, - height: 500, - min: false, - }) - expect(result).toEqual( - expect.objectContaining({ - height: 87, - width: 150, - mime: 'image/jpeg', - }), - ) - }) - - it('scales down to cover.', async () => { - const result = await processFixture('key-landscape-large.jpg', { - format: 'jpeg', - fit: 'cover', - width: 500, - height: 500, - }) - expect(result).toEqual( - expect.objectContaining({ - height: 500, - width: 500, - }), - ) - }) - - it('scales down to inside (landscape).', async () => { - const result = await processFixture('key-landscape-large.jpg', { - format: 'jpeg', - fit: 'inside', - width: 500, - height: 500, - }) - expect(result).toEqual( - expect.objectContaining({ - height: 290, - width: 500, - }), - ) - }) - - it('scales down to inside (portrait).', async () => { - const result = await processFixture('key-portrait-large.jpg', { - format: 'jpeg', - fit: 'inside', - width: 500, - height: 500, - }) - expect(result).toEqual( - expect.objectContaining({ - height: 500, - width: 290, - }), - ) - }) - - it('converts jpeg to png.', async () => { - const result = await processFixture('key-landscape-small.jpg', { - format: 'png', - width: 500, - height: 500, - min: false, - }) - expect(result).toEqual( - expect.objectContaining({ - height: 87, - width: 150, - size: expect.any(Number), - mime: 'image/png', - }), - ) - }) - - it('controls quality (jpeg).', async () => { - const high = await processFixture('key-portrait-small.jpg', { - format: 'jpeg', - width: 500, - height: 500, - quality: 90, - }) - const low = await processFixture('key-portrait-small.jpg', { - format: 'jpeg', - width: 500, - height: 500, - quality: 10, - }) - expect(high.size).toBeGreaterThan(1000) - expect(low.size).toBeLessThan(1000) - }) - - it('controls quality (png).', async () => { - const high = await processFixture('key-portrait-small.jpg', { - format: 'png', - width: 500, - height: 500, - quality: 80, - }) - const low = await processFixture('key-portrait-small.jpg', { - format: 'png', - width: 500, - height: 500, - quality: 10, - }) - expect(high.size).toBeGreaterThan(3000) - expect(low.size).toBeLessThan(3000) - }) - - async function processFixture(fixture: string, options: Options) { - const image = createReadStream(`${__dirname}/fixtures/${fixture}`) - const resized = await resize(image, options) - return await getInfo(resized) - } -}) From 8af6925a1785b008c853be5db77e9b85ffcfd9f4 Mon Sep 17 00:00:00 2001 From: dholms Date: Thu, 21 Sep 2023 22:21:06 -0500 Subject: [PATCH 21/31] rm pds build --- .github/workflows/build-and-push-pds-aws.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build-and-push-pds-aws.yaml b/.github/workflows/build-and-push-pds-aws.yaml index 9c5a4cbad6b..097f782d88e 100644 --- a/.github/workflows/build-and-push-pds-aws.yaml +++ b/.github/workflows/build-and-push-pds-aws.yaml @@ -3,7 +3,6 @@ on: push: branches: - main - - disable-pds-appview-routes env: REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }} USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }} From f908d90975cc32d60b803dc86238ef732825b7e0 Mon Sep 17 00:00:00 2001 From: dholms Date: Thu, 21 Sep 2023 22:36:54 -0500 Subject: [PATCH 22/31] clean up tests --- packages/pds/src/services/account/index.ts | 2 - packages/pds/src/services/util/search.ts | 9 +- .../__snapshots__/get-record.test.ts.snap | 173 ---------- .../admin/__snapshots__/get-repo.test.ts.snap | 118 ------- packages/pds/tests/admin/get-record.test.ts | 25 -- packages/pds/tests/admin/get-repo.test.ts | 22 -- packages/pds/tests/admin/repo-search.test.ts | 309 +----------------- packages/pds/tests/proxied/admin.test.ts | 21 +- 8 files changed, 32 insertions(+), 647 deletions(-) diff --git a/packages/pds/src/services/account/index.ts b/packages/pds/src/services/account/index.ts index bb89e092dc5..0cb293ff26c 100644 --- a/packages/pds/src/services/account/index.ts +++ b/packages/pds/src/services/account/index.ts @@ -375,11 +375,9 @@ export class AccountService { const builder = this.db.dialect === 'pg' ? getUserSearchQueryPg(this.db, opts) - .innerJoin('repo_root', 'repo_root.did', 'did_handle.did') .selectAll('did_handle') .selectAll('repo_root') : getUserSearchQuerySqlite(this.db, opts) - .innerJoin('repo_root', 'repo_root.did', 'did_handle.did') .selectAll('did_handle') .selectAll('repo_root') .select(sql`0`.as('distance')) diff --git a/packages/pds/src/services/util/search.ts b/packages/pds/src/services/util/search.ts index e66818bb420..26b3e81bf06 100644 --- a/packages/pds/src/services/util/search.ts +++ b/packages/pds/src/services/util/search.ts @@ -70,7 +70,10 @@ export const getUserSearchQuerySqlite = ( if (!safeWords.length) { // Return no results. This could happen with weird input like ' % _ '. - return db.db.selectFrom('did_handle').where(sql`1 = 0`) + return db.db + .selectFrom('did_handle') + .innerJoin('repo_root', 'repo_root.did', 'did_handle.did') + .where(sql`1 = 0`) } // We'll ensure there's a space before each word in both textForMatch and in safeWords, @@ -84,9 +87,9 @@ export const getUserSearchQuerySqlite = ( return db.db .selectFrom('did_handle') - .innerJoin('repo_root as _repo_root', '_repo_root.did', 'did_handle.did') + .innerJoin('repo_root', 'repo_root.did', 'did_handle.did') .if(!includeSoftDeleted, (qb) => - qb.where(notSoftDeletedClause(ref('_repo_root'))), + qb.where(notSoftDeletedClause(ref('repo_root'))), ) .where((q) => { safeWords.forEach((word) => { diff --git a/packages/pds/tests/admin/__snapshots__/get-record.test.ts.snap b/packages/pds/tests/admin/__snapshots__/get-record.test.ts.snap index 3a816975459..00fbc5bda1c 100644 --- a/packages/pds/tests/admin/__snapshots__/get-record.test.ts.snap +++ b/packages/pds/tests/admin/__snapshots__/get-record.test.ts.snap @@ -6,16 +6,6 @@ Object { "blobs": Array [], "cid": "cids(0)", "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(0)", - "val": "self-label", - }, - ], "moderation": Object { "actions": Array [ Object { @@ -143,169 +133,6 @@ Object { "blobs": Array [], "cid": "cids(0)", "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(0)", - "val": "self-label", - }, - ], - "moderation": Object { - "actions": Array [ - Object { - "action": "com.atproto.admin.defs#takedown", - "createdAt": "1970-01-01T00:00:00.000Z", - "createdBy": "did:example:admin", - "id": 3, - "reason": "X", - "resolvedReportIds": Array [], - "subject": Object { - "$type": "com.atproto.repo.strongRef", - "cid": "cids(0)", - "uri": "record(0)", - }, - "subjectBlobCids": Array [], - }, - Object { - "action": "com.atproto.admin.defs#acknowledge", - "createdAt": "1970-01-01T00:00:00.000Z", - "createdBy": "did:example:admin", - "id": 2, - "reason": "X", - "resolvedReportIds": Array [], - "reversal": Object { - "createdAt": "1970-01-01T00:00:00.000Z", - "createdBy": "did:example:admin", - "reason": "X", - }, - "subject": Object { - "$type": "com.atproto.repo.strongRef", - "cid": "cids(0)", - "uri": "record(0)", - }, - "subjectBlobCids": Array [], - }, - ], - "currentAction": Object { - "action": "com.atproto.admin.defs#takedown", - "id": 3, - }, - "reports": Array [ - Object { - "createdAt": "1970-01-01T00:00:00.000Z", - "id": 2, - "reason": "defamation", - "reasonType": "com.atproto.moderation.defs#reasonOther", - "reportedBy": "user(1)", - "resolvedByActionIds": Array [], - "subject": Object { - "$type": "com.atproto.repo.strongRef", - "cid": "cids(0)", - "uri": "record(0)", - }, - "subjectRepoHandle": "alice.test", - }, - Object { - "createdAt": "1970-01-01T00:00:00.000Z", - "id": 1, - "reasonType": "com.atproto.moderation.defs#reasonSpam", - "reportedBy": "user(2)", - "resolvedByActionIds": Array [], - "subject": Object { - "$type": "com.atproto.repo.strongRef", - "cid": "cids(0)", - "uri": "record(0)", - }, - "subjectRepoHandle": "alice.test", - }, - ], - }, - "repo": Object { - "did": "user(0)", - "email": "alice@test.com", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "invitesDisabled": false, - "moderation": Object {}, - "relatedRecords": Array [ - Object { - "$type": "app.bsky.actor.profile", - "avatar": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(1)", - }, - "size": 3976, - }, - "description": "its me!", - "displayName": "ali", - "labels": Object { - "$type": "com.atproto.label.defs#selfLabels", - "values": Array [ - Object { - "val": "self-label-a", - }, - Object { - "val": "self-label-b", - }, - ], - }, - }, - ], - }, - "uri": "record(0)", - "value": Object { - "$type": "app.bsky.feed.post", - "createdAt": "1970-01-01T00:00:00.000Z", - "labels": Object { - "$type": "com.atproto.label.defs#selfLabels", - "values": Array [ - Object { - "val": "self-label", - }, - ], - }, - "text": "hey there", - }, -} -`; - -exports[`pds admin get record view serves labels. 1`] = ` -Object { - "blobCids": Array [], - "blobs": Array [], - "cid": "cids(0)", - "indexedAt": "1970-01-01T00:00:00.000Z", - "labels": Array [ - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(0)", - "val": "kittens", - }, - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "record(0)", - "val": "puppies", - }, - Object { - "cid": "cids(0)", - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "user(0)", - "uri": "record(0)", - "val": "self-label", - }, - ], "moderation": Object { "actions": Array [ Object { diff --git a/packages/pds/tests/admin/__snapshots__/get-repo.test.ts.snap b/packages/pds/tests/admin/__snapshots__/get-repo.test.ts.snap index 7234789c2c1..c90b1a070b2 100644 --- a/packages/pds/tests/admin/__snapshots__/get-repo.test.ts.snap +++ b/packages/pds/tests/admin/__snapshots__/get-repo.test.ts.snap @@ -8,124 +8,6 @@ Object { "indexedAt": "1970-01-01T00:00:00.000Z", "invites": Array [], "invitesDisabled": false, - "labels": Array [], - "moderation": Object { - "actions": Array [ - Object { - "action": "com.atproto.admin.defs#takedown", - "createdAt": "1970-01-01T00:00:00.000Z", - "createdBy": "did:example:admin", - "id": 3, - "reason": "X", - "resolvedReportIds": Array [], - "subject": Object { - "$type": "com.atproto.admin.defs#repoRef", - "did": "user(0)", - }, - "subjectBlobCids": Array [], - }, - Object { - "action": "com.atproto.admin.defs#acknowledge", - "createdAt": "1970-01-01T00:00:00.000Z", - "createdBy": "did:example:admin", - "id": 2, - "reason": "X", - "resolvedReportIds": Array [], - "reversal": Object { - "createdAt": "1970-01-01T00:00:00.000Z", - "createdBy": "did:example:admin", - "reason": "X", - }, - "subject": Object { - "$type": "com.atproto.admin.defs#repoRef", - "did": "user(0)", - }, - "subjectBlobCids": Array [], - }, - ], - "currentAction": Object { - "action": "com.atproto.admin.defs#takedown", - "id": 3, - }, - "reports": Array [ - Object { - "createdAt": "1970-01-01T00:00:00.000Z", - "id": 2, - "reason": "defamation", - "reasonType": "com.atproto.moderation.defs#reasonOther", - "reportedBy": "user(1)", - "resolvedByActionIds": Array [], - "subject": Object { - "$type": "com.atproto.admin.defs#repoRef", - "did": "user(0)", - }, - }, - Object { - "createdAt": "1970-01-01T00:00:00.000Z", - "id": 1, - "reasonType": "com.atproto.moderation.defs#reasonSpam", - "reportedBy": "user(2)", - "resolvedByActionIds": Array [], - "subject": Object { - "$type": "com.atproto.admin.defs#repoRef", - "did": "user(0)", - }, - }, - ], - }, - "relatedRecords": Array [ - Object { - "$type": "app.bsky.actor.profile", - "avatar": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(0)", - }, - "size": 3976, - }, - "description": "its me!", - "displayName": "ali", - "labels": Object { - "$type": "com.atproto.label.defs#selfLabels", - "values": Array [ - Object { - "val": "self-label-a", - }, - Object { - "val": "self-label-b", - }, - ], - }, - }, - ], -} -`; - -exports[`pds admin get repo view serves labels. 1`] = ` -Object { - "did": "user(0)", - "email": "alice@test.com", - "handle": "alice.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "invites": Array [], - "invitesDisabled": false, - "labels": Array [ - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(0)", - "val": "kittens", - }, - Object { - "cts": "1970-01-01T00:00:00.000Z", - "neg": false, - "src": "did:example:labeler", - "uri": "user(0)", - "val": "puppies", - }, - ], "moderation": Object { "actions": Array [ Object { diff --git a/packages/pds/tests/admin/get-record.test.ts b/packages/pds/tests/admin/get-record.test.ts index d70707b2b70..6c38419612e 100644 --- a/packages/pds/tests/admin/get-record.test.ts +++ b/packages/pds/tests/admin/get-record.test.ts @@ -120,29 +120,4 @@ describe('pds admin get record view', () => { ) await expect(promise).rejects.toThrow('Record not found') }) - - it('serves labels.', async () => { - const { ctx } = server - const labelingService = ctx.services.appView.label(ctx.db) - await labelingService.formatAndCreate( - ctx.cfg.labelerDid, - sc.posts[sc.dids.alice][0].ref.uriStr, - sc.posts[sc.dids.alice][0].ref.cidStr, - { create: ['kittens', 'puppies', 'birds'] }, - ) - await labelingService.formatAndCreate( - ctx.cfg.labelerDid, - sc.posts[sc.dids.alice][0].ref.uriStr, - sc.posts[sc.dids.alice][0].ref.cidStr, - { negate: ['birds'] }, - ) - const result = await agent.api.com.atproto.admin.getRecord( - { - uri: sc.posts[sc.dids.alice][0].ref.uriStr, - cid: sc.posts[sc.dids.alice][0].ref.cidStr, - }, - { headers: { authorization: adminAuth() } }, - ) - expect(forSnapshot(result.data)).toMatchSnapshot() - }) }) diff --git a/packages/pds/tests/admin/get-repo.test.ts b/packages/pds/tests/admin/get-repo.test.ts index 3cb997f6ff2..9cd38ae101f 100644 --- a/packages/pds/tests/admin/get-repo.test.ts +++ b/packages/pds/tests/admin/get-repo.test.ts @@ -108,26 +108,4 @@ describe('pds admin get repo view', () => { ) await expect(promise).rejects.toThrow('Repo not found') }) - - it('serves labels.', async () => { - const { ctx } = server - const labelingService = ctx.services.appView.label(ctx.db) - await labelingService.formatAndCreate( - ctx.cfg.labelerDid, - sc.dids.alice, - null, - { create: ['kittens', 'puppies', 'birds'] }, - ) - await labelingService.formatAndCreate( - ctx.cfg.labelerDid, - sc.dids.alice, - null, - { negate: ['birds'] }, - ) - const result = await agent.api.com.atproto.admin.getRepo( - { did: sc.dids.alice }, - { headers: { authorization: adminAuth() } }, - ) - expect(forSnapshot(result.data)).toMatchSnapshot() - }) }) diff --git a/packages/pds/tests/admin/repo-search.test.ts b/packages/pds/tests/admin/repo-search.test.ts index 3f25c893901..e3fcdef2d80 100644 --- a/packages/pds/tests/admin/repo-search.test.ts +++ b/packages/pds/tests/admin/repo-search.test.ts @@ -1,19 +1,11 @@ import AtpAgent from '@atproto/api' import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/defs' -import { - runTestServer, - forSnapshot, - CloseFn, - paginateAll, - adminAuth, -} from '../_util' +import { runTestServer, CloseFn, paginateAll, adminAuth } from '../_util' import { SeedClient } from '../seeds/client' import usersBulkSeed from '../seeds/users-bulk' -import { Database } from '../../src' describe('pds admin repo search view', () => { let agent: AtpAgent - let db: Database let close: CloseFn let sc: SeedClient let headers: { [s: string]: string } @@ -23,7 +15,6 @@ describe('pds admin repo search view', () => { dbPostgresSchema: 'views_admin_repo_search', }) close = server.close - db = server.ctx.db agent = new AtpAgent({ service: server.url }) sc = new SeedClient(agent) await usersBulkSeed(sc) @@ -54,21 +45,12 @@ describe('pds admin repo search view', () => { const shouldContain = [ 'cara-wiegand69.test', // Present despite repo takedown - 'eudora-dietrich4.test', // Carol Littel - 'shane-torphy52.test', // Sadie Carter - 'aliya-hodkiewicz.test', // Carlton Abernathy IV 'carlos6.test', 'carolina-mcdermott77.test', ] shouldContain.forEach((handle) => expect(handles).toContain(handle)) - if (db.dialect === 'pg') { - expect(handles).toContain('cayla-marquardt39.test') // Fuzzy match supported by postgres - } else { - expect(handles).not.toContain('cayla-marquardt39.test') - } - const shouldNotContain = [ 'sven70.test', 'hilario84.test', @@ -80,18 +62,12 @@ describe('pds admin repo search view', () => { ] shouldNotContain.forEach((handle) => expect(handles).not.toContain(handle)) - - if (db.dialect === 'pg') { - expect(forSnapshot(result.data.repos)).toMatchInlineSnapshot(snapPg) - } else { - expect(forSnapshot(result.data.repos)).toMatchInlineSnapshot(snapSqlite) - } }) it('finds repo by did', async () => { const term = sc.dids['cara-wiegand69.test'] const res = await agent.api.com.atproto.admin.searchRepos( - { term, limit: 1 }, + { term }, { headers }, ) @@ -99,6 +75,19 @@ describe('pds admin repo search view', () => { expect(res.data.repos[0].did).toEqual(term) }) + it('finds repo by email', async () => { + const did = sc.dids['cara-wiegand69.test'] + const { email } = sc.accounts[did] + const res = await agent.api.com.atproto.admin.searchRepos( + { term: email }, + { headers }, + ) + + expect(res.data.repos.length).toEqual(1) + expect(res.data.repos[0].did).toEqual(did) + expect(res.data.repos[0].email).toEqual(email) + }) + it('paginates with term', async () => { const results = (results) => results.flatMap((res) => res.users) const paginator = async (cursor?: string) => { @@ -119,7 +108,7 @@ describe('pds admin repo search view', () => { { headers }, ) - expect(full.data.repos.length).toBeGreaterThan(5) + expect(full.data.repos.length).toBeGreaterThan(3) expect(results(paginatedAll)).toEqual(results([full.data])) }) @@ -147,269 +136,3 @@ describe('pds admin repo search view', () => { expect(results(paginatedAll)).toEqual(results([full.data])) }) }) - -// Not using jest snapshots because it doesn't handle the conditional pg/sqlite very well: -// you can achieve it using named snapshots, but when you run the tests for pg the test suite fails -// since the sqlite snapshots appear obsolete to jest (and vice-versa when you run the sqlite suite). - -const snapPg = ` -Array [ - Object { - "did": "user(0)", - "email": "aliya-hodkiewicz.test@bsky.app", - "handle": "aliya-hodkiewicz.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "invitesDisabled": false, - "moderation": Object {}, - "relatedRecords": Array [ - Object { - "$type": "app.bsky.actor.profile", - "avatar": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(0)", - }, - "size": 3976, - }, - "description": "", - "displayName": "Carlton Abernathy IV", - }, - ], - }, - Object { - "did": "user(1)", - "email": "cara-wiegand69.test@bsky.app", - "handle": "cara-wiegand69.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "invitesDisabled": false, - "moderation": Object { - "currentAction": Object { - "action": "com.atproto.admin.defs#takedown", - "id": 1, - }, - }, - "relatedRecords": Array [], - }, - Object { - "did": "user(2)", - "email": "carlos6.test@bsky.app", - "handle": "carlos6.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "invitesDisabled": false, - "moderation": Object {}, - "relatedRecords": Array [], - }, - Object { - "did": "user(3)", - "email": "carolina-mcdermott77.test@bsky.app", - "handle": "carolina-mcdermott77.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "invitesDisabled": false, - "moderation": Object {}, - "relatedRecords": Array [ - Object { - "$type": "app.bsky.actor.profile", - "avatar": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(0)", - }, - "size": 3976, - }, - "description": "", - "displayName": "Latoya Windler", - }, - ], - }, - Object { - "did": "user(4)", - "email": "eudora-dietrich4.test@bsky.app", - "handle": "eudora-dietrich4.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "invitesDisabled": false, - "moderation": Object {}, - "relatedRecords": Array [ - Object { - "$type": "app.bsky.actor.profile", - "avatar": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(0)", - }, - "size": 3976, - }, - "description": "", - "displayName": "Carol Littel", - }, - ], - }, - Object { - "did": "user(5)", - "email": "shane-torphy52.test@bsky.app", - "handle": "shane-torphy52.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "invitesDisabled": false, - "moderation": Object {}, - "relatedRecords": Array [ - Object { - "$type": "app.bsky.actor.profile", - "avatar": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(0)", - }, - "size": 3976, - }, - "description": "", - "displayName": "Sadie Carter", - }, - ], - }, - Object { - "did": "user(6)", - "email": "cayla-marquardt39.test@bsky.app", - "handle": "cayla-marquardt39.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "invitesDisabled": false, - "moderation": Object {}, - "relatedRecords": Array [ - Object { - "$type": "app.bsky.actor.profile", - "avatar": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(0)", - }, - "size": 3976, - }, - "description": "", - "displayName": "Rachel Kshlerin", - }, - ], - }, -] -` -const snapSqlite = ` -Array [ - Object { - "did": "user(0)", - "email": "aliya-hodkiewicz.test@bsky.app", - "handle": "aliya-hodkiewicz.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "invitesDisabled": false, - "moderation": Object {}, - "relatedRecords": Array [ - Object { - "$type": "app.bsky.actor.profile", - "avatar": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(0)", - }, - "size": 3976, - }, - "description": "", - "displayName": "Carlton Abernathy IV", - }, - ], - }, - Object { - "did": "user(1)", - "email": "cara-wiegand69.test@bsky.app", - "handle": "cara-wiegand69.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "invitesDisabled": false, - "moderation": Object { - "currentAction": Object { - "action": "com.atproto.admin.defs#takedown", - "id": 1, - }, - }, - "relatedRecords": Array [], - }, - Object { - "did": "user(2)", - "email": "carlos6.test@bsky.app", - "handle": "carlos6.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "invitesDisabled": false, - "moderation": Object {}, - "relatedRecords": Array [], - }, - Object { - "did": "user(3)", - "email": "carolina-mcdermott77.test@bsky.app", - "handle": "carolina-mcdermott77.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "invitesDisabled": false, - "moderation": Object {}, - "relatedRecords": Array [ - Object { - "$type": "app.bsky.actor.profile", - "avatar": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(0)", - }, - "size": 3976, - }, - "description": "", - "displayName": "Latoya Windler", - }, - ], - }, - Object { - "did": "user(4)", - "email": "eudora-dietrich4.test@bsky.app", - "handle": "eudora-dietrich4.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "invitesDisabled": false, - "moderation": Object {}, - "relatedRecords": Array [ - Object { - "$type": "app.bsky.actor.profile", - "avatar": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(0)", - }, - "size": 3976, - }, - "description": "", - "displayName": "Carol Littel", - }, - ], - }, - Object { - "did": "user(5)", - "email": "shane-torphy52.test@bsky.app", - "handle": "shane-torphy52.test", - "indexedAt": "1970-01-01T00:00:00.000Z", - "invitesDisabled": false, - "moderation": Object {}, - "relatedRecords": Array [ - Object { - "$type": "app.bsky.actor.profile", - "avatar": Object { - "$type": "blob", - "mimeType": "image/jpeg", - "ref": Object { - "$link": "cids(0)", - }, - "size": 3976, - }, - "description": "", - "displayName": "Sadie Carter", - }, - ], - }, -] -` diff --git a/packages/pds/tests/proxied/admin.test.ts b/packages/pds/tests/proxied/admin.test.ts index 127717225a6..98ec8f1f70c 100644 --- a/packages/pds/tests/proxied/admin.test.ts +++ b/packages/pds/tests/proxied/admin.test.ts @@ -24,7 +24,6 @@ describe('proxies admin requests', () => { dbPostgresSchema: 'proxy_admin', pds: { // @NOTE requires admin pass be the same on pds and appview, which TestNetwork is handling for us. - enableInProcessAppView: true, bskyAppViewModeration: true, inviteRequired: true, }, @@ -246,7 +245,7 @@ describe('proxies admin requests', () => { }) it('takesdown and labels repos, and reverts.', async () => { - const { db, services } = network.pds.ctx + const { db, services } = network.bsky.ctx // takedown repo const { data: action } = await agent.api.com.atproto.admin.takeModerationAction( @@ -276,8 +275,8 @@ describe('proxies admin requests', () => { await expect(tryGetProfileAppview).rejects.toThrow( 'Account has been taken down', ) - const labelsA = await services.appView - .label(db) + const labelsA = await services + .label(db.getPrimary()) .getLabels(sc.dids.alice, { includeNeg: false, skipCache: true }) expect(labelsA.map((l) => l.val)).toEqual(['dogs']) // reverse action @@ -298,14 +297,14 @@ describe('proxies admin requests', () => { expect(profileAppview).toEqual( expect.objectContaining({ did: sc.dids.alice, handle: 'alice.test' }), ) - const labelsB = await services.appView - .label(db) + const labelsB = await services + .label(db.getPrimary()) .getLabels(sc.dids.alice, { includeNeg: false, skipCache: true }) expect(labelsB.map((l) => l.val)).toEqual(['cats']) }) it('takesdown and labels records, and reverts.', async () => { - const { db, services } = network.pds.ctx + const { db, services } = network.bsky.ctx const post = sc.posts[sc.dids.alice][0] // takedown post const { data: action } = @@ -335,8 +334,8 @@ describe('proxies admin requests', () => { }, ) await expect(tryGetPostAppview).rejects.toThrow(NotFoundError) - const labelsA = await services.appView - .label(db) + const labelsA = await services + .label(db.getPrimary()) .getLabels(post.ref.uriStr, { includeNeg: false, skipCache: true }) expect(labelsA.map((l) => l.val)).toEqual(['dogs']) // reverse action @@ -357,8 +356,8 @@ describe('proxies admin requests', () => { expect(threadAppview.thread.post).toEqual( expect.objectContaining({ uri: post.ref.uriStr, cid: post.ref.cidStr }), ) - const labelsB = await services.appView - .label(db) + const labelsB = await services + .label(db.getPrimary()) .getLabels(post.ref.uriStr, { includeNeg: false, skipCache: true }) expect(labelsB.map((l) => l.val)).toEqual(['cats']) }) From 222377be76b6d33587f9a854903a1fe629f7a52d Mon Sep 17 00:00:00 2001 From: dholms Date: Thu, 21 Sep 2023 22:41:08 -0500 Subject: [PATCH 23/31] fix build --- packages/dev-env/src/bin.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/dev-env/src/bin.ts b/packages/dev-env/src/bin.ts index 532265e18ec..6563029cfb3 100644 --- a/packages/dev-env/src/bin.ts +++ b/packages/dev-env/src/bin.ts @@ -15,7 +15,6 @@ const run = async () => { const network = await TestNetworkNoAppView.create({ pds: { port: 2583, - enableLabelsCache: true, publicUrl: 'http://localhost:2583', }, plc: { port: 2582 }, From 4a4f3cb8a3e64cfe0812cd3ba24085c57d8e3ace Mon Sep 17 00:00:00 2001 From: dholms Date: Thu, 21 Sep 2023 22:43:31 -0500 Subject: [PATCH 24/31] fix build --- packages/dev-env/src/bin-network.ts | 1 - packages/dev-env/src/mock/index.ts | 48 ++++++++++++++--------------- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/packages/dev-env/src/bin-network.ts b/packages/dev-env/src/bin-network.ts index 193c7ea968a..56f0138b3b2 100644 --- a/packages/dev-env/src/bin-network.ts +++ b/packages/dev-env/src/bin-network.ts @@ -16,7 +16,6 @@ const run = async () => { pds: { port: 2583, publicUrl: 'http://localhost:2583', - enableLabelsCache: true, dbPostgresSchema: 'pds', }, bsky: { diff --git a/packages/dev-env/src/mock/index.ts b/packages/dev-env/src/mock/index.ts index afa661f7ecb..60508997968 100644 --- a/packages/dev-env/src/mock/index.ts +++ b/packages/dev-env/src/mock/index.ts @@ -186,30 +186,30 @@ export async function generateMockSetup(env: TestNetworkNoAppView) { }, ) - const ctx = env.pds.ctx - if (ctx) { - await ctx.db.db - .insertInto('label') - .values([ - { - src: ctx.cfg.labelerDid, - uri: labeledPost.uri, - cid: labeledPost.cid, - val: 'nudity', - neg: 0, - cts: new Date().toISOString(), - }, - { - src: ctx.cfg.labelerDid, - uri: filteredPost.uri, - cid: filteredPost.cid, - val: 'dmca-violation', - neg: 0, - cts: new Date().toISOString(), - }, - ]) - .execute() - } + // const ctx = env.pds.ctx + // if (ctx) { + // await ctx.db.db + // .insertInto('label') + // .values([ + // { + // src: ctx.cfg.labelerDid, + // uri: labeledPost.uri, + // cid: labeledPost.cid, + // val: 'nudity', + // neg: 0, + // cts: new Date().toISOString(), + // }, + // { + // src: ctx.cfg.labelerDid, + // uri: filteredPost.uri, + // cid: filteredPost.cid, + // val: 'dmca-violation', + // neg: 0, + // cts: new Date().toISOString(), + // }, + // ]) + // .execute() + // } // a set of replies for (let i = 0; i < 100; i++) { From c9994bde3047c752178e8cf4fac2108e667b091a Mon Sep 17 00:00:00 2001 From: dholms Date: Thu, 21 Sep 2023 22:48:58 -0500 Subject: [PATCH 25/31] migration --- .../20230922T033938477Z-remove-appview.ts | 28 +++++++++++++++++++ packages/pds/src/db/migrations/index.ts | 1 + 2 files changed, 29 insertions(+) create mode 100644 packages/pds/src/db/migrations/20230922T033938477Z-remove-appview.ts diff --git a/packages/pds/src/db/migrations/20230922T033938477Z-remove-appview.ts b/packages/pds/src/db/migrations/20230922T033938477Z-remove-appview.ts new file mode 100644 index 00000000000..6528c6bf0ef --- /dev/null +++ b/packages/pds/src/db/migrations/20230922T033938477Z-remove-appview.ts @@ -0,0 +1,28 @@ +import { Kysely } from 'kysely' + +export async function up(db: Kysely): Promise { + await db.schema.dropView('algo_whats_hot_view') + await db.schema.dropTable('actor_block').execute() + await db.schema.dropTable('duplicate_record').execute() + await db.schema.dropTable('feed_generator').execute() + await db.schema.dropTable('feed_item').execute() + await db.schema.dropTable('follow').execute() + await db.schema.dropTable('like').execute() + await db.schema.dropTable('list_item').execute() + await db.schema.dropTable('list').execute() + await db.schema.dropTable('post_agg').execute() + await db.schema.dropTable('post_embed_image').execute() + await db.schema.dropTable('post_embed_external').execute() + await db.schema.dropTable('post_embed_record').execute() + await db.schema.dropTable('post').execute() + await db.schema.dropTable('profile_agg').execute() + await db.schema.dropTable('profile').execute() + await db.schema.dropTable('repost').execute() + await db.schema.dropTable('subscription').execute() + await db.schema.dropTable('suggested_follow').execute() + await db.schema.dropTable('view_param').execute() +} + +export async function down(db: Kysely): Promise { + // Migration code +} diff --git a/packages/pds/src/db/migrations/index.ts b/packages/pds/src/db/migrations/index.ts index e7e521e986a..fde3ddd2398 100644 --- a/packages/pds/src/db/migrations/index.ts +++ b/packages/pds/src/db/migrations/index.ts @@ -65,3 +65,4 @@ export * as _20230818T134357818Z from './20230818T134357818Z-runtime-flags' export * as _20230824T182048120Z from './20230824T182048120Z-remove-post-hierarchy' export * as _20230825T142507884Z from './20230825T142507884Z-blob-tempkey-idx' export * as _20230828T153013575Z from './20230828T153013575Z-repo-history-rewrite' +export * as _20230922T033938477Z from './20230922T033938477Z-remove-appview' From be46a91523b8bb208fa65389d2f56520032ccd1e Mon Sep 17 00:00:00 2001 From: dholms Date: Thu, 21 Sep 2023 22:54:34 -0500 Subject: [PATCH 26/31] tidy --- packages/pds/src/db/database-schema.ts | 2 -- .../migrations/20230922T033938477Z-remove-appview.ts | 3 ++- packages/pds/src/db/tables/label.ts | 12 ------------ 3 files changed, 2 insertions(+), 15 deletions(-) delete mode 100644 packages/pds/src/db/tables/label.ts diff --git a/packages/pds/src/db/database-schema.ts b/packages/pds/src/db/database-schema.ts index ed86d15939f..eda67a4e6fb 100644 --- a/packages/pds/src/db/database-schema.ts +++ b/packages/pds/src/db/database-schema.ts @@ -18,7 +18,6 @@ import * as deleteAccountToken from './tables/delete-account-token' import * as moderation from './tables/moderation' import * as mute from './tables/mute' import * as listMute from './tables/list-mute' -import * as label from './tables/label' import * as repoSeq from './tables/repo-seq' import * as appMigration from './tables/app-migration' import * as runtimeFlag from './tables/runtime-flag' @@ -44,7 +43,6 @@ export type DatabaseSchemaType = runtimeFlag.PartialDB & moderation.PartialDB & mute.PartialDB & listMute.PartialDB & - label.PartialDB & repoSeq.PartialDB export type DatabaseSchema = Kysely diff --git a/packages/pds/src/db/migrations/20230922T033938477Z-remove-appview.ts b/packages/pds/src/db/migrations/20230922T033938477Z-remove-appview.ts index 6528c6bf0ef..c4cfba3a8c4 100644 --- a/packages/pds/src/db/migrations/20230922T033938477Z-remove-appview.ts +++ b/packages/pds/src/db/migrations/20230922T033938477Z-remove-appview.ts @@ -1,12 +1,13 @@ import { Kysely } from 'kysely' export async function up(db: Kysely): Promise { - await db.schema.dropView('algo_whats_hot_view') + await db.schema.dropView('algo_whats_hot_view').materialized().execute() await db.schema.dropTable('actor_block').execute() await db.schema.dropTable('duplicate_record').execute() await db.schema.dropTable('feed_generator').execute() await db.schema.dropTable('feed_item').execute() await db.schema.dropTable('follow').execute() + await db.schema.dropTable('label').execute() await db.schema.dropTable('like').execute() await db.schema.dropTable('list_item').execute() await db.schema.dropTable('list').execute() diff --git a/packages/pds/src/db/tables/label.ts b/packages/pds/src/db/tables/label.ts deleted file mode 100644 index 1837faab1c8..00000000000 --- a/packages/pds/src/db/tables/label.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const tableName = 'label' - -export interface Label { - src: string - uri: string - cid: string - val: string - neg: 0 | 1 // @TODO convert to boolean in app-view - cts: string -} - -export type PartialDB = { [tableName]: Label } From 967c3a707ebfa56f91467aff166c3b5676291f69 Mon Sep 17 00:00:00 2001 From: dholms Date: Mon, 25 Sep 2023 18:09:06 -0500 Subject: [PATCH 27/31] build branch --- .github/workflows/build-and-push-pds-aws.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-and-push-pds-aws.yaml b/.github/workflows/build-and-push-pds-aws.yaml index 097f782d88e..f3e2e4b5e88 100644 --- a/.github/workflows/build-and-push-pds-aws.yaml +++ b/.github/workflows/build-and-push-pds-aws.yaml @@ -3,6 +3,7 @@ on: push: branches: - main + - disable-pds-appview-indexing env: REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }} USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }} From 16c950f88be8511ada208429bba0b34ac44a53dd Mon Sep 17 00:00:00 2001 From: dholms Date: Mon, 25 Sep 2023 18:34:34 -0500 Subject: [PATCH 28/31] tidy --- lexicons/app/bsky/unspecced/applyLabels.json | 23 --------- packages/api/src/client/index.ts | 13 ----- packages/api/src/client/lexicons.ts | 27 ---------- .../types/app/bsky/unspecced/applyLabels.ts | 33 ------------ packages/bsky/src/auto-moderator/index.ts | 18 +------ packages/bsky/src/lexicon/index.ts | 12 ----- packages/bsky/src/lexicon/lexicons.ts | 27 ---------- .../types/app/bsky/unspecced/applyLabels.ts | 39 --------------- packages/dev-env/build.js | 2 +- packages/dev-env/package.json | 3 +- packages/dev-env/src/bin-network.ts | 40 --------------- packages/dev-env/src/bin.ts | 9 +++- packages/dev-env/src/mock/index.ts | 50 +++++++++---------- packages/pds/src/lexicon/index.ts | 12 ----- packages/pds/src/lexicon/lexicons.ts | 27 ---------- .../types/app/bsky/unspecced/applyLabels.ts | 39 --------------- 16 files changed, 34 insertions(+), 340 deletions(-) delete mode 100644 lexicons/app/bsky/unspecced/applyLabels.json delete mode 100644 packages/api/src/client/types/app/bsky/unspecced/applyLabels.ts delete mode 100644 packages/bsky/src/lexicon/types/app/bsky/unspecced/applyLabels.ts delete mode 100644 packages/dev-env/src/bin-network.ts delete mode 100644 packages/pds/src/lexicon/types/app/bsky/unspecced/applyLabels.ts diff --git a/lexicons/app/bsky/unspecced/applyLabels.json b/lexicons/app/bsky/unspecced/applyLabels.json deleted file mode 100644 index 24c9e716ad5..00000000000 --- a/lexicons/app/bsky/unspecced/applyLabels.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "lexicon": 1, - "id": "app.bsky.unspecced.applyLabels", - "defs": { - "main": { - "type": "procedure", - "description": "Allow a labeler to apply labels directly.", - "input": { - "encoding": "application/json", - "schema": { - "type": "object", - "required": ["labels"], - "properties": { - "labels": { - "type": "array", - "items": { "type": "ref", "ref": "com.atproto.label.defs#label" } - } - } - } - } - } - } -} diff --git a/packages/api/src/client/index.ts b/packages/api/src/client/index.ts index d72fe659e50..d542991a7f2 100644 --- a/packages/api/src/client/index.ts +++ b/packages/api/src/client/index.ts @@ -128,7 +128,6 @@ import * as AppBskyNotificationListNotifications from './types/app/bsky/notifica import * as AppBskyNotificationRegisterPush from './types/app/bsky/notification/registerPush' import * as AppBskyNotificationUpdateSeen from './types/app/bsky/notification/updateSeen' import * as AppBskyRichtextFacet from './types/app/bsky/richtext/facet' -import * as AppBskyUnspeccedApplyLabels from './types/app/bsky/unspecced/applyLabels' import * as AppBskyUnspeccedGetPopular from './types/app/bsky/unspecced/getPopular' import * as AppBskyUnspeccedGetPopularFeedGenerators from './types/app/bsky/unspecced/getPopularFeedGenerators' import * as AppBskyUnspeccedGetTimelineSkeleton from './types/app/bsky/unspecced/getTimelineSkeleton' @@ -254,7 +253,6 @@ export * as AppBskyNotificationListNotifications from './types/app/bsky/notifica export * as AppBskyNotificationRegisterPush from './types/app/bsky/notification/registerPush' export * as AppBskyNotificationUpdateSeen from './types/app/bsky/notification/updateSeen' export * as AppBskyRichtextFacet from './types/app/bsky/richtext/facet' -export * as AppBskyUnspeccedApplyLabels from './types/app/bsky/unspecced/applyLabels' export * as AppBskyUnspeccedGetPopular from './types/app/bsky/unspecced/getPopular' export * as AppBskyUnspeccedGetPopularFeedGenerators from './types/app/bsky/unspecced/getPopularFeedGenerators' export * as AppBskyUnspeccedGetTimelineSkeleton from './types/app/bsky/unspecced/getTimelineSkeleton' @@ -2234,17 +2232,6 @@ export class UnspeccedNS { this._service = service } - applyLabels( - data?: AppBskyUnspeccedApplyLabels.InputSchema, - opts?: AppBskyUnspeccedApplyLabels.CallOptions, - ): Promise { - return this._service.xrpc - .call('app.bsky.unspecced.applyLabels', opts?.qp, data, opts) - .catch((e) => { - throw AppBskyUnspeccedApplyLabels.toKnownErr(e) - }) - } - getPopular( params?: AppBskyUnspeccedGetPopular.QueryParams, opts?: AppBskyUnspeccedGetPopular.CallOptions, diff --git a/packages/api/src/client/lexicons.ts b/packages/api/src/client/lexicons.ts index a5cbf08d608..7f54e7a423e 100644 --- a/packages/api/src/client/lexicons.ts +++ b/packages/api/src/client/lexicons.ts @@ -6829,32 +6829,6 @@ export const schemaDict = { }, }, }, - AppBskyUnspeccedApplyLabels: { - lexicon: 1, - id: 'app.bsky.unspecced.applyLabels', - defs: { - main: { - type: 'procedure', - description: 'Allow a labeler to apply labels directly.', - input: { - encoding: 'application/json', - schema: { - type: 'object', - required: ['labels'], - properties: { - labels: { - type: 'array', - items: { - type: 'ref', - ref: 'lex:com.atproto.label.defs#label', - }, - }, - }, - }, - }, - }, - }, - }, AppBskyUnspeccedGetPopular: { lexicon: 1, id: 'app.bsky.unspecced.getPopular', @@ -7130,7 +7104,6 @@ export const ids = { AppBskyNotificationRegisterPush: 'app.bsky.notification.registerPush', AppBskyNotificationUpdateSeen: 'app.bsky.notification.updateSeen', AppBskyRichtextFacet: 'app.bsky.richtext.facet', - AppBskyUnspeccedApplyLabels: 'app.bsky.unspecced.applyLabels', AppBskyUnspeccedGetPopular: 'app.bsky.unspecced.getPopular', AppBskyUnspeccedGetPopularFeedGenerators: 'app.bsky.unspecced.getPopularFeedGenerators', diff --git a/packages/api/src/client/types/app/bsky/unspecced/applyLabels.ts b/packages/api/src/client/types/app/bsky/unspecced/applyLabels.ts deleted file mode 100644 index c8e72746a42..00000000000 --- a/packages/api/src/client/types/app/bsky/unspecced/applyLabels.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * GENERATED CODE - DO NOT MODIFY - */ -import { Headers, XRPCError } from '@atproto/xrpc' -import { ValidationResult, BlobRef } from '@atproto/lexicon' -import { isObj, hasProp } from '../../../../util' -import { lexicons } from '../../../../lexicons' -import { CID } from 'multiformats/cid' -import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' - -export interface QueryParams {} - -export interface InputSchema { - labels: ComAtprotoLabelDefs.Label[] - [k: string]: unknown -} - -export interface CallOptions { - headers?: Headers - qp?: QueryParams - encoding: 'application/json' -} - -export interface Response { - success: boolean - headers: Headers -} - -export function toKnownErr(e: any) { - if (e instanceof XRPCError) { - } - return e -} diff --git a/packages/bsky/src/auto-moderator/index.ts b/packages/bsky/src/auto-moderator/index.ts index 1be099759f1..30befc19110 100644 --- a/packages/bsky/src/auto-moderator/index.ts +++ b/packages/bsky/src/auto-moderator/index.ts @@ -280,28 +280,12 @@ export class AutoModerator { async storeLabels(uri: AtUri, cid: CID, labels: string[]): Promise { if (labels.length < 1) return const labelSrvc = this.services.label(this.ctx.db) - const formatted = await labelSrvc.formatAndCreate( + await labelSrvc.formatAndCreate( this.ctx.cfg.labelerDid, uri.toString(), cid.toString(), { create: labels }, ) - if (this.pushAgent) { - const agent = this.pushAgent - try { - await agent.api.app.bsky.unspecced.applyLabels({ labels: formatted }) - } catch (err) { - log.error( - { - err, - uri: uri.toString(), - labels, - receiver: agent.service.toString(), - }, - 'failed to push labels', - ) - } - } } async processAll() { diff --git a/packages/bsky/src/lexicon/index.ts b/packages/bsky/src/lexicon/index.ts index 52635132470..d30ecb2700f 100644 --- a/packages/bsky/src/lexicon/index.ts +++ b/packages/bsky/src/lexicon/index.ts @@ -106,7 +106,6 @@ import * as AppBskyNotificationGetUnreadCount from './types/app/bsky/notificatio import * as AppBskyNotificationListNotifications from './types/app/bsky/notification/listNotifications' import * as AppBskyNotificationRegisterPush from './types/app/bsky/notification/registerPush' import * as AppBskyNotificationUpdateSeen from './types/app/bsky/notification/updateSeen' -import * as AppBskyUnspeccedApplyLabels from './types/app/bsky/unspecced/applyLabels' import * as AppBskyUnspeccedGetPopular from './types/app/bsky/unspecced/getPopular' import * as AppBskyUnspeccedGetPopularFeedGenerators from './types/app/bsky/unspecced/getPopularFeedGenerators' import * as AppBskyUnspeccedGetTimelineSkeleton from './types/app/bsky/unspecced/getTimelineSkeleton' @@ -1388,17 +1387,6 @@ export class UnspeccedNS { this._server = server } - applyLabels( - cfg: ConfigOf< - AV, - AppBskyUnspeccedApplyLabels.Handler>, - AppBskyUnspeccedApplyLabels.HandlerReqCtx> - >, - ) { - const nsid = 'app.bsky.unspecced.applyLabels' // @ts-ignore - return this._server.xrpc.method(nsid, cfg) - } - getPopular( cfg: ConfigOf< AV, diff --git a/packages/bsky/src/lexicon/lexicons.ts b/packages/bsky/src/lexicon/lexicons.ts index a5cbf08d608..7f54e7a423e 100644 --- a/packages/bsky/src/lexicon/lexicons.ts +++ b/packages/bsky/src/lexicon/lexicons.ts @@ -6829,32 +6829,6 @@ export const schemaDict = { }, }, }, - AppBskyUnspeccedApplyLabels: { - lexicon: 1, - id: 'app.bsky.unspecced.applyLabels', - defs: { - main: { - type: 'procedure', - description: 'Allow a labeler to apply labels directly.', - input: { - encoding: 'application/json', - schema: { - type: 'object', - required: ['labels'], - properties: { - labels: { - type: 'array', - items: { - type: 'ref', - ref: 'lex:com.atproto.label.defs#label', - }, - }, - }, - }, - }, - }, - }, - }, AppBskyUnspeccedGetPopular: { lexicon: 1, id: 'app.bsky.unspecced.getPopular', @@ -7130,7 +7104,6 @@ export const ids = { AppBskyNotificationRegisterPush: 'app.bsky.notification.registerPush', AppBskyNotificationUpdateSeen: 'app.bsky.notification.updateSeen', AppBskyRichtextFacet: 'app.bsky.richtext.facet', - AppBskyUnspeccedApplyLabels: 'app.bsky.unspecced.applyLabels', AppBskyUnspeccedGetPopular: 'app.bsky.unspecced.getPopular', AppBskyUnspeccedGetPopularFeedGenerators: 'app.bsky.unspecced.getPopularFeedGenerators', diff --git a/packages/bsky/src/lexicon/types/app/bsky/unspecced/applyLabels.ts b/packages/bsky/src/lexicon/types/app/bsky/unspecced/applyLabels.ts deleted file mode 100644 index 1d359a9547d..00000000000 --- a/packages/bsky/src/lexicon/types/app/bsky/unspecced/applyLabels.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * GENERATED CODE - DO NOT MODIFY - */ -import express from 'express' -import { ValidationResult, BlobRef } from '@atproto/lexicon' -import { lexicons } from '../../../../lexicons' -import { isObj, hasProp } from '../../../../util' -import { CID } from 'multiformats/cid' -import { HandlerAuth } from '@atproto/xrpc-server' -import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' - -export interface QueryParams {} - -export interface InputSchema { - labels: ComAtprotoLabelDefs.Label[] - [k: string]: unknown -} - -export interface HandlerInput { - encoding: 'application/json' - body: InputSchema -} - -export interface HandlerError { - status: number - message?: string -} - -export type HandlerOutput = HandlerError | void -export type HandlerReqCtx = { - auth: HA - params: QueryParams - input: HandlerInput - req: express.Request - res: express.Response -} -export type Handler = ( - ctx: HandlerReqCtx, -) => Promise | HandlerOutput diff --git a/packages/dev-env/build.js b/packages/dev-env/build.js index 98bb2df6133..60634cf4503 100644 --- a/packages/dev-env/build.js +++ b/packages/dev-env/build.js @@ -6,7 +6,7 @@ const buildShallow = require('esbuild').build({ logLevel: 'info', - entryPoints: ['src/index.ts', 'src/bin.ts', 'src/bin-network.ts'], + entryPoints: ['src/index.ts', 'src/bin.ts'], bundle: true, sourcemap: true, outdir: 'dist', diff --git a/packages/dev-env/package.json b/packages/dev-env/package.json index d945103437e..87da0e8a218 100644 --- a/packages/dev-env/package.json +++ b/packages/dev-env/package.json @@ -22,8 +22,7 @@ "build": "node ./build.js", "postbuild": "tsc --build tsconfig.build.json", "update-main-to-dist": "node ../../update-main-to-dist.js packages/dev-env", - "start": "node dist/bin.js", - "start:network": "../dev-infra/with-test-redis-and-db.sh node dist/bin-network.js" + "start": "../dev-infra/with-test-redis-and-db.sh node dist/bin.js" }, "dependencies": { "@atproto/api": "workspace:^", diff --git a/packages/dev-env/src/bin-network.ts b/packages/dev-env/src/bin-network.ts deleted file mode 100644 index 56f0138b3b2..00000000000 --- a/packages/dev-env/src/bin-network.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { generateMockSetup } from './mock' -import { TestNetwork } from './network' - -const run = async () => { - console.log(` -██████╗ -██╔═══██╗ -██║██╗██║ -██║██║██║ -╚█║████╔╝ - ╚╝╚═══╝ protocol - -[ created by Bluesky ]`) - - const network = await TestNetwork.create({ - pds: { - port: 2583, - publicUrl: 'http://localhost:2583', - dbPostgresSchema: 'pds', - }, - bsky: { - dbPostgresSchema: 'bsky', - }, - plc: { port: 2582 }, - }) - await generateMockSetup(network) - - console.log( - `👤 DID Placeholder server started http://localhost:${network.plc.port}`, - ) - console.log( - `🌞 Personal Data server started http://localhost:${network.pds.port}`, - ) - console.log(`🌅 Bsky Appview started http://localhost:${network.bsky.port}`) - for (const fg of network.feedGens) { - console.log(`🤖 Feed Generator started http://localhost:${fg.port}`) - } -} - -run() diff --git a/packages/dev-env/src/bin.ts b/packages/dev-env/src/bin.ts index 6563029cfb3..56f0138b3b2 100644 --- a/packages/dev-env/src/bin.ts +++ b/packages/dev-env/src/bin.ts @@ -1,5 +1,5 @@ import { generateMockSetup } from './mock' -import { TestNetworkNoAppView } from './network-no-appview' +import { TestNetwork } from './network' const run = async () => { console.log(` @@ -12,10 +12,14 @@ const run = async () => { [ created by Bluesky ]`) - const network = await TestNetworkNoAppView.create({ + const network = await TestNetwork.create({ pds: { port: 2583, publicUrl: 'http://localhost:2583', + dbPostgresSchema: 'pds', + }, + bsky: { + dbPostgresSchema: 'bsky', }, plc: { port: 2582 }, }) @@ -27,6 +31,7 @@ const run = async () => { console.log( `🌞 Personal Data server started http://localhost:${network.pds.port}`, ) + console.log(`🌅 Bsky Appview started http://localhost:${network.bsky.port}`) for (const fg of network.feedGens) { console.log(`🤖 Feed Generator started http://localhost:${fg.port}`) } diff --git a/packages/dev-env/src/mock/index.ts b/packages/dev-env/src/mock/index.ts index 60508997968..10f76b1c259 100644 --- a/packages/dev-env/src/mock/index.ts +++ b/packages/dev-env/src/mock/index.ts @@ -4,7 +4,7 @@ import { REASONSPAM, REASONOTHER, } from '@atproto/api/src/client/types/com/atproto/moderation/defs' -import { TestNetworkNoAppView } from '../index' +import { TestNetwork } from '../index' import { postTexts, replyTexts } from './data' import labeledImgB64 from './img/labeled-img-b64' import blurHashB64 from './img/blur-hash-avatar-b64' @@ -23,7 +23,7 @@ function* dateGen() { return '' } -export async function generateMockSetup(env: TestNetworkNoAppView) { +export async function generateMockSetup(env: TestNetwork) { const date = dateGen() const rand = (n: number) => Math.floor(Math.random() * n) @@ -186,30 +186,28 @@ export async function generateMockSetup(env: TestNetworkNoAppView) { }, ) - // const ctx = env.pds.ctx - // if (ctx) { - // await ctx.db.db - // .insertInto('label') - // .values([ - // { - // src: ctx.cfg.labelerDid, - // uri: labeledPost.uri, - // cid: labeledPost.cid, - // val: 'nudity', - // neg: 0, - // cts: new Date().toISOString(), - // }, - // { - // src: ctx.cfg.labelerDid, - // uri: filteredPost.uri, - // cid: filteredPost.cid, - // val: 'dmca-violation', - // neg: 0, - // cts: new Date().toISOString(), - // }, - // ]) - // .execute() - // } + const ctx = env.bsky.ctx + if (ctx) { + const labelSrvc = ctx.services.label(ctx.db.getPrimary()) + await labelSrvc.createLabels([ + { + src: ctx.cfg.labelerDid, + uri: labeledPost.uri, + cid: labeledPost.cid, + val: 'nudity', + neg: false, + cts: new Date().toISOString(), + }, + { + src: ctx.cfg.labelerDid, + uri: filteredPost.uri, + cid: filteredPost.cid, + val: 'dmca-violation', + neg: false, + cts: new Date().toISOString(), + }, + ]) + } // a set of replies for (let i = 0; i < 100; i++) { diff --git a/packages/pds/src/lexicon/index.ts b/packages/pds/src/lexicon/index.ts index 52635132470..d30ecb2700f 100644 --- a/packages/pds/src/lexicon/index.ts +++ b/packages/pds/src/lexicon/index.ts @@ -106,7 +106,6 @@ import * as AppBskyNotificationGetUnreadCount from './types/app/bsky/notificatio import * as AppBskyNotificationListNotifications from './types/app/bsky/notification/listNotifications' import * as AppBskyNotificationRegisterPush from './types/app/bsky/notification/registerPush' import * as AppBskyNotificationUpdateSeen from './types/app/bsky/notification/updateSeen' -import * as AppBskyUnspeccedApplyLabels from './types/app/bsky/unspecced/applyLabels' import * as AppBskyUnspeccedGetPopular from './types/app/bsky/unspecced/getPopular' import * as AppBskyUnspeccedGetPopularFeedGenerators from './types/app/bsky/unspecced/getPopularFeedGenerators' import * as AppBskyUnspeccedGetTimelineSkeleton from './types/app/bsky/unspecced/getTimelineSkeleton' @@ -1388,17 +1387,6 @@ export class UnspeccedNS { this._server = server } - applyLabels( - cfg: ConfigOf< - AV, - AppBskyUnspeccedApplyLabels.Handler>, - AppBskyUnspeccedApplyLabels.HandlerReqCtx> - >, - ) { - const nsid = 'app.bsky.unspecced.applyLabels' // @ts-ignore - return this._server.xrpc.method(nsid, cfg) - } - getPopular( cfg: ConfigOf< AV, diff --git a/packages/pds/src/lexicon/lexicons.ts b/packages/pds/src/lexicon/lexicons.ts index a5cbf08d608..7f54e7a423e 100644 --- a/packages/pds/src/lexicon/lexicons.ts +++ b/packages/pds/src/lexicon/lexicons.ts @@ -6829,32 +6829,6 @@ export const schemaDict = { }, }, }, - AppBskyUnspeccedApplyLabels: { - lexicon: 1, - id: 'app.bsky.unspecced.applyLabels', - defs: { - main: { - type: 'procedure', - description: 'Allow a labeler to apply labels directly.', - input: { - encoding: 'application/json', - schema: { - type: 'object', - required: ['labels'], - properties: { - labels: { - type: 'array', - items: { - type: 'ref', - ref: 'lex:com.atproto.label.defs#label', - }, - }, - }, - }, - }, - }, - }, - }, AppBskyUnspeccedGetPopular: { lexicon: 1, id: 'app.bsky.unspecced.getPopular', @@ -7130,7 +7104,6 @@ export const ids = { AppBskyNotificationRegisterPush: 'app.bsky.notification.registerPush', AppBskyNotificationUpdateSeen: 'app.bsky.notification.updateSeen', AppBskyRichtextFacet: 'app.bsky.richtext.facet', - AppBskyUnspeccedApplyLabels: 'app.bsky.unspecced.applyLabels', AppBskyUnspeccedGetPopular: 'app.bsky.unspecced.getPopular', AppBskyUnspeccedGetPopularFeedGenerators: 'app.bsky.unspecced.getPopularFeedGenerators', diff --git a/packages/pds/src/lexicon/types/app/bsky/unspecced/applyLabels.ts b/packages/pds/src/lexicon/types/app/bsky/unspecced/applyLabels.ts deleted file mode 100644 index 1d359a9547d..00000000000 --- a/packages/pds/src/lexicon/types/app/bsky/unspecced/applyLabels.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * GENERATED CODE - DO NOT MODIFY - */ -import express from 'express' -import { ValidationResult, BlobRef } from '@atproto/lexicon' -import { lexicons } from '../../../../lexicons' -import { isObj, hasProp } from '../../../../util' -import { CID } from 'multiformats/cid' -import { HandlerAuth } from '@atproto/xrpc-server' -import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' - -export interface QueryParams {} - -export interface InputSchema { - labels: ComAtprotoLabelDefs.Label[] - [k: string]: unknown -} - -export interface HandlerInput { - encoding: 'application/json' - body: InputSchema -} - -export interface HandlerError { - status: number - message?: string -} - -export type HandlerOutput = HandlerError | void -export type HandlerReqCtx = { - auth: HA - params: QueryParams - input: HandlerInput - req: express.Request - res: express.Response -} -export type Handler = ( - ctx: HandlerReqCtx, -) => Promise | HandlerOutput From 2e09cc0a2eefdc0ca028d0bdc15480786d8d17db Mon Sep 17 00:00:00 2001 From: dholms Date: Mon, 25 Sep 2023 18:35:24 -0500 Subject: [PATCH 29/31] build branch --- .github/workflows/build-and-push-bsky-aws.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-and-push-bsky-aws.yaml b/.github/workflows/build-and-push-bsky-aws.yaml index 36b1aa23cb3..486d83934b9 100644 --- a/.github/workflows/build-and-push-bsky-aws.yaml +++ b/.github/workflows/build-and-push-bsky-aws.yaml @@ -3,6 +3,7 @@ on: push: branches: - main + - disable-pds-appview-indexing env: REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }} USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }} From c47f8b2731795ce27a4ad5ea4a12ecc928feef48 Mon Sep 17 00:00:00 2001 From: dholms Date: Mon, 25 Sep 2023 18:38:53 -0500 Subject: [PATCH 30/31] small tidy --- .../pds/src/db/migrations/20230922T033938477Z-remove-appview.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pds/src/db/migrations/20230922T033938477Z-remove-appview.ts b/packages/pds/src/db/migrations/20230922T033938477Z-remove-appview.ts index c4cfba3a8c4..f66825fa722 100644 --- a/packages/pds/src/db/migrations/20230922T033938477Z-remove-appview.ts +++ b/packages/pds/src/db/migrations/20230922T033938477Z-remove-appview.ts @@ -24,6 +24,6 @@ export async function up(db: Kysely): Promise { await db.schema.dropTable('view_param').execute() } -export async function down(db: Kysely): Promise { +export async function down(_db: Kysely): Promise { // Migration code } From e10ba81b250092915832ab8320f2e20c95db952b Mon Sep 17 00:00:00 2001 From: dholms Date: Mon, 25 Sep 2023 18:58:04 -0500 Subject: [PATCH 31/31] dont build --- .github/workflows/build-and-push-bsky-aws.yaml | 1 - .github/workflows/build-and-push-pds-aws.yaml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/build-and-push-bsky-aws.yaml b/.github/workflows/build-and-push-bsky-aws.yaml index 486d83934b9..36b1aa23cb3 100644 --- a/.github/workflows/build-and-push-bsky-aws.yaml +++ b/.github/workflows/build-and-push-bsky-aws.yaml @@ -3,7 +3,6 @@ on: push: branches: - main - - disable-pds-appview-indexing env: REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }} USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }} diff --git a/.github/workflows/build-and-push-pds-aws.yaml b/.github/workflows/build-and-push-pds-aws.yaml index f3e2e4b5e88..097f782d88e 100644 --- a/.github/workflows/build-and-push-pds-aws.yaml +++ b/.github/workflows/build-and-push-pds-aws.yaml @@ -3,7 +3,6 @@ on: push: branches: - main - - disable-pds-appview-indexing env: REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }} USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }}