diff --git a/.changeset/quick-rules-film.md b/.changeset/quick-rules-film.md new file mode 100644 index 00000000000..dd11c4fc4df --- /dev/null +++ b/.changeset/quick-rules-film.md @@ -0,0 +1,5 @@ +--- +"@atproto/xrpc-server": patch +--- + +Update lxm check error name to BadJwtLexiconMethod diff --git a/.changeset/red-dolls-marry.md b/.changeset/red-dolls-marry.md new file mode 100644 index 00000000000..212bca82221 --- /dev/null +++ b/.changeset/red-dolls-marry.md @@ -0,0 +1,5 @@ +--- +"@atproto/xrpc-server": patch +--- + +Support http.IncomingMessage input to parseReqNsid() diff --git a/.changeset/silver-beers-buy.md b/.changeset/silver-beers-buy.md new file mode 100644 index 00000000000..f42a60bc3b5 --- /dev/null +++ b/.changeset/silver-beers-buy.md @@ -0,0 +1,5 @@ +--- +"@atproto/pds": patch +--- + +Validate lxm claims in service auth diff --git a/.changeset/slow-dolls-rhyme.md b/.changeset/slow-dolls-rhyme.md new file mode 100644 index 00000000000..86d88e65803 --- /dev/null +++ b/.changeset/slow-dolls-rhyme.md @@ -0,0 +1,5 @@ +--- +"@atproto/ozone": patch +--- + +Set lxm claim on service auth calls from ozone diff --git a/.github/workflows/build-and-push-bsky-ghcr.yaml b/.github/workflows/build-and-push-bsky-ghcr.yaml index 42483b86f4f..fb025f4635b 100644 --- a/.github/workflows/build-and-push-bsky-ghcr.yaml +++ b/.github/workflows/build-and-push-bsky-ghcr.yaml @@ -3,7 +3,7 @@ on: push: branches: - main - - divy/mod-full-thread + - service-auth-scopes env: REGISTRY: ghcr.io USERNAME: ${{ github.actor }} diff --git a/.github/workflows/build-and-push-ozone-aws.yaml b/.github/workflows/build-and-push-ozone-aws.yaml index 53f95c5b731..f9ebbd37a2a 100644 --- a/.github/workflows/build-and-push-ozone-aws.yaml +++ b/.github/workflows/build-and-push-ozone-aws.yaml @@ -3,6 +3,7 @@ on: push: branches: - main + - service-auth-scopes 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-ghcr.yaml b/.github/workflows/build-and-push-pds-ghcr.yaml index c35b3fcc5df..621b89dc69b 100644 --- a/.github/workflows/build-and-push-pds-ghcr.yaml +++ b/.github/workflows/build-and-push-pds-ghcr.yaml @@ -3,7 +3,7 @@ on: push: branches: - main - - divy/starter-packs + - service-auth-scopes env: REGISTRY: ghcr.io USERNAME: ${{ github.actor }} diff --git a/packages/bsky/src/api/app/bsky/actor/getProfiles.ts b/packages/bsky/src/api/app/bsky/actor/getProfiles.ts index 81fb34e5ea5..22ace2f3a34 100644 --- a/packages/bsky/src/api/app/bsky/actor/getProfiles.ts +++ b/packages/bsky/src/api/app/bsky/actor/getProfiles.ts @@ -10,11 +10,20 @@ import { Hydrator, } from '../../../../hydration/hydrator' import { Views } from '../../../../views' +import { ids } from '../../../../lexicon/lexicons' export default function (server: Server, ctx: AppContext) { const getProfile = createPipeline(skeleton, hydration, noRules, presentation) server.app.bsky.actor.getProfiles({ - auth: ctx.authVerifier.standardOptional, + auth: ctx.authVerifier.standardOptionalParameterized({ + lxmCheck: (method) => { + if (!method) return false + return ( + method === ids.AppBskyActorGetProfiles || + method.startsWith('chat.bsky.') + ) + }, + }), handler: async ({ auth, params, req }) => { const viewer = auth.credentials.iss const labelers = ctx.reqLabelers(req) diff --git a/packages/bsky/src/api/app/bsky/feed/getFeed.ts b/packages/bsky/src/api/app/bsky/feed/getFeed.ts index dfc363bee44..398b9ff1340 100644 --- a/packages/bsky/src/api/app/bsky/feed/getFeed.ts +++ b/packages/bsky/src/api/app/bsky/feed/getFeed.ts @@ -29,6 +29,7 @@ import { unpackIdentityServices, } from '../../../../data-plane' import { resHeaders } from '../../../util' +import { ids } from '../../../../lexicon/lexicons' export default function (server: Server, ctx: AppContext) { const getFeed = createPipeline( @@ -38,7 +39,17 @@ export default function (server: Server, ctx: AppContext) { presentation, ) server.app.bsky.feed.getFeed({ - auth: ctx.authVerifier.standardOptionalAnyAud, + auth: ctx.authVerifier.standardOptionalParameterized({ + lxmCheck: (method) => { + return ( + method !== undefined && + [ids.AppBskyFeedGetFeedSkeleton, ids.AppBskyFeedGetFeed].includes( + method, + ) + ) + }, + skipAudCheck: true, + }), handler: async ({ params, auth, req }) => { const viewer = auth.credentials.iss const labelers = ctx.reqLabelers(req) diff --git a/packages/bsky/src/api/app/bsky/feed/getPosts.ts b/packages/bsky/src/api/app/bsky/feed/getPosts.ts index 283639a5606..89a07317644 100644 --- a/packages/bsky/src/api/app/bsky/feed/getPosts.ts +++ b/packages/bsky/src/api/app/bsky/feed/getPosts.ts @@ -11,11 +11,19 @@ import { import { Views } from '../../../../views' import { creatorFromUri } from '../../../../views/util' import { resHeaders } from '../../../util' +import { ids } from '../../../../lexicon/lexicons' export default function (server: Server, ctx: AppContext) { const getPosts = createPipeline(skeleton, hydration, noBlocks, presentation) server.app.bsky.feed.getPosts({ - auth: ctx.authVerifier.standardOptional, + auth: ctx.authVerifier.standardOptionalParameterized({ + lxmCheck: (method) => { + if (!method) return false + return ( + method === ids.AppBskyFeedGetPosts || method.startsWith('chat.bsky.') + ) + }, + }), handler: async ({ params, auth, req }) => { const viewer = auth.credentials.iss const labelers = ctx.reqLabelers(req) diff --git a/packages/bsky/src/auth-verifier.ts b/packages/bsky/src/auth-verifier.ts index a9ac7b408b1..24747470f35 100644 --- a/packages/bsky/src/auth-verifier.ts +++ b/packages/bsky/src/auth-verifier.ts @@ -1,5 +1,6 @@ import { AuthRequiredError, + parseReqNsid, verifyJwt as verifyServiceJwt, } from '@atproto/xrpc-server' import * as ui8 from 'uint8arrays' @@ -17,6 +18,11 @@ type ReqCtx = { req: express.Request } +type StandardAuthOpts = { + skipAudCheck?: boolean + lxmCheck?: (method?: string) => boolean +} + export enum RoleStatus { Valid, Invalid, @@ -80,61 +86,55 @@ export class AuthVerifier { } // verifiers (arrow fns to preserve scope) - - standard = async (ctx: ReqCtx): Promise => { - // @TODO remove! basic auth + did supported just for testing. - if (isBasicToken(ctx.req)) { - const aud = this.ownDid - const iss = ctx.req.headers['appview-as-did'] - if (typeof iss !== 'string' || !iss.startsWith('did:')) { - throw new AuthRequiredError('bad issuer') - } - if (!this.parseRoleCreds(ctx.req).admin) { - throw new AuthRequiredError('bad credentials') - } - return { - credentials: { type: 'standard', iss, aud }, + standardOptionalParameterized = + (opts: StandardAuthOpts) => + async (ctx: ReqCtx): Promise => { + // @TODO remove! basic auth + did supported just for testing. + if (isBasicToken(ctx.req)) { + const aud = this.ownDid + const iss = ctx.req.headers['appview-as-did'] + if (typeof iss !== 'string' || !iss.startsWith('did:')) { + throw new AuthRequiredError('bad issuer') + } + if (!this.parseRoleCreds(ctx.req).admin) { + throw new AuthRequiredError('bad credentials') + } + return { + credentials: { type: 'standard', iss, aud }, + } + } else if (isBearerToken(ctx.req)) { + const { iss, aud } = await this.verifyServiceJwt(ctx, { + lxmCheck: opts.lxmCheck, + iss: null, + aud: null, + }) + if (!opts.skipAudCheck && !this.standardAudienceDids.has(aud)) { + throw new AuthRequiredError( + 'jwt audience does not match service did', + 'BadJwtAudience', + ) + } + return { + credentials: { + type: 'standard', + iss, + aud, + }, + } + } else { + return this.nullCreds() } } - const { iss, aud } = await this.verifyServiceJwt(ctx, { - aud: null, - iss: null, - }) - if (!this.standardAudienceDids.has(aud)) { - throw new AuthRequiredError( - 'jwt audience does not match service did', - 'BadJwtAudience', - ) - } - return { - credentials: { - type: 'standard', - iss, - aud, - }, - } - } - standardOptional = async ( - ctx: ReqCtx, - ): Promise => { - if (isBearerToken(ctx.req) || isBasicToken(ctx.req)) { - return this.standard(ctx) - } - return this.nullCreds() - } + standardOptional: (ctx: ReqCtx) => Promise = + this.standardOptionalParameterized({}) - standardOptionalAnyAud = async ( - ctx: ReqCtx, - ): Promise => { - if (!isBearerToken(ctx.req)) { - return this.nullCreds() + standard = async (ctx: ReqCtx): Promise => { + const output = await this.standardOptional(ctx) + if (output.credentials.type === 'none') { + throw new AuthRequiredError(undefined, 'AuthMissing') } - const { iss, aud } = await this.verifyServiceJwt(ctx, { - aud: null, - iss: null, - }) - return { credentials: { type: 'standard', iss, aud } } + return output as StandardOutput } role = (ctx: ReqCtx): RoleOutput => { @@ -215,7 +215,11 @@ export class AuthVerifier { async verifyServiceJwt( reqCtx: ReqCtx, - opts: { aud: string | null; iss: string[] | null }, + opts: { + iss: string[] | null + aud: string | null + lxmCheck?: (method?: string) => boolean + }, ) { const getSigningKey = async ( iss: string, @@ -243,17 +247,40 @@ export class AuthVerifier { } return didKey } + const assertLxmCheck = () => { + const lxm = parseReqNsid(reqCtx.req) + if ( + (opts.lxmCheck && !opts.lxmCheck(payload.lxm)) || + (!opts.lxmCheck && payload.lxm !== lxm) + ) { + throw new AuthRequiredError( + payload.lxm !== undefined + ? `bad jwt lexicon method ("lxm"). must match: ${lxm}` + : `missing jwt lexicon method ("lxm"). must match: ${lxm}`, + 'BadJwtLexiconMethod', + ) + } + } const jwtStr = bearerTokenFromReq(reqCtx.req) if (!jwtStr) { throw new AuthRequiredError('missing jwt', 'MissingJwt') } + // if validating additional scopes, skip scope check in initial validation & follow up afterwards const payload = await verifyServiceJwt( jwtStr, opts.aud, null, getSigningKey, ) + if ( + !payload.iss.endsWith('#atproto_labeler') || + payload.lxm !== undefined + ) { + // @TODO currently permissive of labelers who dont set lxm yet. + // we'll allow ozone self-hosters to upgrade before removing this condition. + assertLxmCheck() + } return { iss: payload.iss, aud: payload.aud } } diff --git a/packages/bsky/tests/admin/admin-auth.test.ts b/packages/bsky/tests/admin/admin-auth.test.ts index d811bf55dc3..027ff49eb1e 100644 --- a/packages/bsky/tests/admin/admin-auth.test.ts +++ b/packages/bsky/tests/admin/admin-auth.test.ts @@ -3,6 +3,7 @@ import { AtpAgent } from '@atproto/api' import { Secp256k1Keypair } from '@atproto/crypto' import { createServiceAuthHeaders } from '@atproto/xrpc-server' import { RepoRef } from '../../src/lexicon/types/com/atproto/admin/defs' +import { ids } from '../../src/lexicon/lexicons' describe('admin auth', () => { let network: TestNetwork @@ -68,10 +69,10 @@ describe('admin auth', () => { }) it('allows service auth requests from the configured appview did', async () => { - const headers = await createServiceAuthHeaders({ + const updateHeaders = await createServiceAuthHeaders({ iss: modServiceDid, aud: bskyDid, - lxm: null, + lxm: ids.ComAtprotoAdminUpdateSubjectStatus, keypair: modServiceKey, }) await agent.api.com.atproto.admin.updateSubjectStatus( @@ -80,14 +81,20 @@ describe('admin auth', () => { takedown: { applied: true, ref: 'test-repo' }, }, { - ...headers, + ...updateHeaders, encoding: 'application/json', }, ) + const getHeaders = await createServiceAuthHeaders({ + iss: modServiceDid, + aud: bskyDid, + lxm: ids.ComAtprotoAdminGetSubjectStatus, + keypair: modServiceKey, + }) const res = await agent.api.com.atproto.admin.getSubjectStatus( { did: repoSubject.did }, - headers, + getHeaders, ) expect(res.data.subject.did).toBe(repoSubject.did) expect(res.data.takedown?.applied).toBe(true) @@ -97,7 +104,7 @@ describe('admin auth', () => { const headers = await createServiceAuthHeaders({ iss: altModDid, aud: bskyDid, - lxm: null, + lxm: ids.ComAtprotoAdminUpdateSubjectStatus, keypair: modServiceKey, }) const attempt = agent.api.com.atproto.admin.updateSubjectStatus( @@ -118,7 +125,7 @@ describe('admin auth', () => { const headers = await createServiceAuthHeaders({ iss: sc.dids.alice, aud: bskyDid, - lxm: null, + lxm: ids.ComAtprotoAdminUpdateSubjectStatus, keypair: aliceKey, }) const attempt = agent.api.com.atproto.admin.updateSubjectStatus( @@ -139,7 +146,7 @@ describe('admin auth', () => { const headers = await createServiceAuthHeaders({ iss: modServiceDid, aud: bskyDid, - lxm: null, + lxm: ids.ComAtprotoAdminUpdateSubjectStatus, keypair: badKey, }) const attempt = agent.api.com.atproto.admin.updateSubjectStatus( @@ -162,7 +169,7 @@ describe('admin auth', () => { const headers = await createServiceAuthHeaders({ iss: modServiceDid, aud: sc.dids.alice, - lxm: null, + lxm: ids.ComAtprotoAdminUpdateSubjectStatus, keypair: modServiceKey, }) const attempt = agent.api.com.atproto.admin.updateSubjectStatus( diff --git a/packages/bsky/tests/auth.test.ts b/packages/bsky/tests/auth.test.ts index da756723bb8..a6af4a84d48 100644 --- a/packages/bsky/tests/auth.test.ts +++ b/packages/bsky/tests/auth.test.ts @@ -2,6 +2,7 @@ import { AtpAgent } from '@atproto/api' import { SeedClient, TestNetwork, usersSeed } from '@atproto/dev-env' import { createServiceJwt } from '@atproto/xrpc-server' import { Keypair, Secp256k1Keypair } from '@atproto/crypto' +import { ids } from '../src/lexicon/lexicons' describe('auth', () => { let network: TestNetwork @@ -29,7 +30,7 @@ describe('auth', () => { const jwt = await createServiceJwt({ iss: issuer, aud: network.bsky.ctx.cfg.serverDid, - lxm: null, + lxm: ids.AppBskyActorGetProfile, keypair, }) return agent.api.app.bsky.actor.getProfile( diff --git a/packages/bsky/tests/data-plane/handle-invalidation.test.ts b/packages/bsky/tests/data-plane/handle-invalidation.test.ts index 8469a8507ef..8c366e23d4b 100644 --- a/packages/bsky/tests/data-plane/handle-invalidation.test.ts +++ b/packages/bsky/tests/data-plane/handle-invalidation.test.ts @@ -1,6 +1,7 @@ import { DAY } from '@atproto/common' import { TestNetwork, SeedClient, usersSeed } from '@atproto/dev-env' import { AtpAgent } from '@atproto/api' +import { ids } from '../../src/lexicon/lexicons' describe('handle invalidation', () => { let network: TestNetwork @@ -62,7 +63,12 @@ describe('handle invalidation', () => { const res = await agent.api.app.bsky.actor.getProfile( { actor: eveAccnt.did }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyActorGetProfile, + ), + }, ) expect(res.data.handle).toEqual('handle.invalid') }) @@ -77,7 +83,12 @@ describe('handle invalidation', () => { await network.processAll() const res = await agent.api.app.bsky.actor.getProfile( { actor: alice }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyActorGetProfile, + ), + }, ) expect(res.data.handle).toEqual('handle.invalid') }) @@ -92,7 +103,12 @@ describe('handle invalidation', () => { await network.processAll() const res = await agent.api.app.bsky.actor.getProfile( { actor: alice }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyActorGetProfile, + ), + }, ) expect(res.data.handle).toEqual(sc.accounts[alice].handle) }) @@ -112,13 +128,23 @@ describe('handle invalidation', () => { const aliceRes = await agent.api.app.bsky.actor.getProfile( { actor: alice }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyActorGetProfile, + ), + }, ) expect(aliceRes.data.handle).toEqual('handle.invalid') const bobRes = await agent.api.app.bsky.actor.getProfile( { actor: bob }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyActorGetProfile, + ), + }, ) expect(bobRes.data.handle).toEqual(sc.accounts[alice].handle) }) diff --git a/packages/bsky/tests/data-plane/indexing.test.ts b/packages/bsky/tests/data-plane/indexing.test.ts index 406d56305f3..88c3ac6851e 100644 --- a/packages/bsky/tests/data-plane/indexing.test.ts +++ b/packages/bsky/tests/data-plane/indexing.test.ts @@ -97,7 +97,12 @@ describe('indexing', () => { const getAfterCreate = await agent.api.app.bsky.feed.getPostThread( { uri: uri.toString() }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(getAfterCreate.data)).toMatchSnapshot() const createNotifications = await getNotifications(db, uri) @@ -107,7 +112,12 @@ describe('indexing', () => { const getAfterUpdate = await agent.api.app.bsky.feed.getPostThread( { uri: uri.toString() }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(getAfterUpdate.data)).toMatchSnapshot() const updateNotifications = await getNotifications(db, uri) @@ -117,7 +127,12 @@ describe('indexing', () => { const getAfterDelete = agent.api.app.bsky.feed.getPostThread( { uri: uri.toString() }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) await expect(getAfterDelete).rejects.toThrow(/Post not found:/) const deleteNotifications = await getNotifications(db, uri) @@ -162,7 +177,12 @@ describe('indexing', () => { const getAfterCreate = await agent.api.app.bsky.actor.getProfile( { actor: sc.dids.dan }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyActorGetProfile, + ), + }, ) expect(forSnapshot(getAfterCreate.data)).toMatchSnapshot() @@ -171,7 +191,12 @@ describe('indexing', () => { const getAfterUpdate = await agent.api.app.bsky.actor.getProfile( { actor: sc.dids.dan }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyActorGetProfile, + ), + }, ) expect(forSnapshot(getAfterUpdate.data)).toMatchSnapshot() @@ -180,7 +205,12 @@ describe('indexing', () => { const getAfterDelete = await agent.api.app.bsky.actor.getProfile( { actor: sc.dids.dan }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyActorGetProfile, + ), + }, ) expect(forSnapshot(getAfterDelete.data)).toMatchSnapshot() }) @@ -331,7 +361,12 @@ describe('indexing', () => { data: { notifications }, } = await agent.api.app.bsky.notification.listNotifications( {}, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyNotificationListNotifications, + ), + }, ) expect(notifications).toHaveLength(2) @@ -404,15 +439,30 @@ describe('indexing', () => { // Mark originals const { data: origProfile } = await agent.api.app.bsky.actor.getProfile( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyActorGetProfile, + ), + }, ) const { data: origFeed } = await agent.api.app.bsky.feed.getAuthorFeed( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) const { data: origFollows } = await agent.api.app.bsky.graph.getFollows( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyGraphGetFollows, + ), + }, ) // Index const { data: commit } = @@ -424,15 +474,30 @@ describe('indexing', () => { // Check const { data: profile } = await agent.api.app.bsky.actor.getProfile( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyActorGetProfile, + ), + }, ) const { data: feed } = await agent.api.app.bsky.feed.getAuthorFeed( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) const { data: follows } = await agent.api.app.bsky.graph.getFollows( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyGraphGetFollows, + ), + }, ) expect(forSnapshot([origProfile, origFeed, origFollows])).toEqual( forSnapshot([profile, feed, follows]), @@ -468,15 +533,30 @@ describe('indexing', () => { // Check const { data: profile } = await agent.api.app.bsky.actor.getProfile( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyActorGetProfile, + ), + }, ) const { data: feed } = await agent.api.app.bsky.feed.getAuthorFeed( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) const { data: follows } = await agent.api.app.bsky.graph.getFollows( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyGraphGetFollows, + ), + }, ) expect(profile.description).toEqual('freshening things up') expect(feed.feed[0].post.uri).toEqual(newPost.ref.uriStr) @@ -525,12 +605,22 @@ describe('indexing', () => { // Check const getGoodPost = agent.api.app.bsky.feed.getPostThread( { uri: writes[0].uri.toString(), depth: 0 }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) await expect(getGoodPost).resolves.toBeDefined() const getBadPost = agent.api.app.bsky.feed.getPostThread( { uri: writes[1].uri.toString(), depth: 0 }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) await expect(getBadPost).rejects.toThrow('Post not found') }) @@ -540,7 +630,12 @@ describe('indexing', () => { const getIndexedHandle = async (did) => { const res = await agent.api.app.bsky.actor.getProfile( { actor: did }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyActorGetProfile, + ), + }, ) return res.data.handle } @@ -618,13 +713,23 @@ describe('indexing', () => { it('does not unindex actor when they are still being hosted by their pds', async () => { const { data: profileBefore } = await agent.api.app.bsky.actor.getProfile( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyActorGetProfile, + ), + }, ) // Attempt indexing tombstone await network.bsky.sub.indexingSvc.deleteActor(sc.dids.alice) const { data: profileAfter } = await agent.api.app.bsky.actor.getProfile( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyActorGetProfile, + ), + }, ) expect(profileAfter).toEqual(profileBefore) }) @@ -633,7 +738,12 @@ describe('indexing', () => { const { alice } = sc.dids const getProfileBefore = agent.api.app.bsky.actor.getProfile( { actor: alice }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyActorGetProfile, + ), + }, ) await expect(getProfileBefore).resolves.toBeDefined() // Delete account on pds @@ -651,7 +761,12 @@ describe('indexing', () => { await network.bsky.sub.indexingSvc.deleteActor(alice) const getProfileAfter = agent.api.app.bsky.actor.getProfile( { actor: alice }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyActorGetProfile, + ), + }, ) await expect(getProfileAfter).rejects.toThrow('Profile not found') }) diff --git a/packages/bsky/tests/data-plane/thread-mutes.test.ts b/packages/bsky/tests/data-plane/thread-mutes.test.ts index 8b03b0d973e..5e96432463f 100644 --- a/packages/bsky/tests/data-plane/thread-mutes.test.ts +++ b/packages/bsky/tests/data-plane/thread-mutes.test.ts @@ -1,5 +1,6 @@ import { RecordRef, SeedClient, TestNetwork, usersSeed } from '@atproto/dev-env' import { AtpAgent } from '@atproto/api' +import { ids } from '../../src/lexicon/lexicons' describe('thread mutes', () => { let network: TestNetwork @@ -35,7 +36,10 @@ describe('thread mutes', () => { { root: rootPost.uriStr }, { encoding: 'application/json', - headers: await network.serviceHeaders(alice), + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphMuteThread, + ), }, ) }) @@ -46,7 +50,7 @@ describe('thread mutes', () => { uris: [rootPost.uriStr, replyPost.uriStr], }, { - headers: await network.serviceHeaders(alice), + headers: await network.serviceHeaders(alice, ids.AppBskyFeedGetPosts), }, ) expect(res.data.posts[0].viewer?.threadMuted).toBe(true) @@ -60,7 +64,12 @@ describe('thread mutes', () => { const notifsRes = await agent.api.app.bsky.notification.listNotifications( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationListNotifications, + ), + }, ) expect(notifsRes.data.notifications.length).toBe(0) }) @@ -72,7 +81,12 @@ describe('thread mutes', () => { const notifsRes = await agent.api.app.bsky.notification.listNotifications( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationListNotifications, + ), + }, ) expect(notifsRes.data.notifications.length).toBe(0) }) @@ -84,7 +98,12 @@ describe('thread mutes', () => { const notifsRes = await agent.api.app.bsky.notification.listNotifications( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationListNotifications, + ), + }, ) expect(notifsRes.data.notifications.length).toBe(0) }) @@ -96,7 +115,12 @@ describe('thread mutes', () => { const notifsRes = await agent.api.app.bsky.notification.listNotifications( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationListNotifications, + ), + }, ) expect(notifsRes.data.notifications.length).toBe(0) }) @@ -106,7 +130,10 @@ describe('thread mutes', () => { { root: rootPost.uriStr }, { encoding: 'application/json', - headers: await network.serviceHeaders(alice), + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphUnmuteThread, + ), }, ) }) @@ -117,7 +144,7 @@ describe('thread mutes', () => { uris: [rootPost.uriStr, replyPost.uriStr], }, { - headers: await network.serviceHeaders(alice), + headers: await network.serviceHeaders(alice, ids.AppBskyFeedGetPosts), }, ) expect(res.data.posts[0].viewer?.threadMuted).toBe(false) @@ -133,7 +160,12 @@ describe('thread mutes', () => { const notifsRes = await agent.api.app.bsky.notification.listNotifications( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationListNotifications, + ), + }, ) expect(notifsRes.data.notifications.length).toBe(4) }) diff --git a/packages/bsky/tests/feed-generation.test.ts b/packages/bsky/tests/feed-generation.test.ts index 82629717f7d..e73505e1c65 100644 --- a/packages/bsky/tests/feed-generation.test.ts +++ b/packages/bsky/tests/feed-generation.test.ts @@ -191,7 +191,12 @@ describe('feed generation', () => { const paginator = async (cursor?: string) => { const res = await agent.api.app.bsky.feed.getActorFeeds( { actor: alice, cursor, limit: 2 }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyFeedGetActorFeeds, + ), + }, ) return res.data } @@ -224,7 +229,12 @@ describe('feed generation', () => { await network.processAll() const view = await agent.api.app.bsky.feed.getPosts( { uris: [res.uri] }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyFeedGetPosts, + ), + }, ) expect(view.data.posts.length).toBe(1) expect(forSnapshot(view.data.posts[0])).toMatchSnapshot() @@ -246,7 +256,12 @@ describe('feed generation', () => { await network.processAll() const view = await agent.api.app.bsky.feed.getPosts( { uris: [res.uri] }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyFeedGetPosts, + ), + }, ) expect(view.data.posts.length).toBe(1) expect(forSnapshot(view.data.posts[0])).toMatchSnapshot() @@ -300,7 +315,12 @@ describe('feed generation', () => { await network.processAll() const view = await agent.api.app.bsky.feed.getPosts( { uris: [res.uri] }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyFeedGetPosts, + ), + }, ) expect(view.data.posts.length).toBe(1) expect(forSnapshot(view.data.posts[0])).toMatchSnapshot() @@ -325,7 +345,12 @@ describe('feed generation', () => { await network.processAll() const view = await agent.api.app.bsky.feed.getPosts( { uris: [res.uri] }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyFeedGetPosts, + ), + }, ) expect(view.data.posts.length).toBe(1) expect(forSnapshot(view.data.posts[0])).toMatchSnapshot() @@ -335,7 +360,12 @@ describe('feed generation', () => { it('describes a feed gen & returns online status', async () => { const resEven = await agent.api.app.bsky.feed.getFeedGenerator( { feed: feedUriAll }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyFeedGetFeedGenerator, + ), + }, ) expect(forSnapshot(resEven.data)).toMatchSnapshot() expect(resEven.data.isOnline).toBe(true) @@ -345,7 +375,12 @@ describe('feed generation', () => { it('does not describe taken-down feed', async () => { const tryGetFeed = agent.api.app.bsky.feed.getFeedGenerator( { feed: feedUriPrime }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyFeedGetFeedGenerator, + ), + }, ) await expect(tryGetFeed).rejects.toThrow('could not find feed') }) @@ -354,7 +389,12 @@ describe('feed generation', () => { it.skip('handles an unsupported algo', async () => { const resOdd = await agent.api.app.bsky.feed.getFeedGenerator( { feed: feedUriOdd }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyFeedGetFeedGenerator, + ), + }, ) expect(resOdd.data.isOnline).toBe(true) expect(resOdd.data.isValid).toBe(false) @@ -391,7 +431,12 @@ describe('feed generation', () => { { feed: allUriBob.toString(), }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetFeedGenerator, + ), + }, ) expect(res.data.isOnline).toBe(false) expect(res.data.isValid).toBe(false) @@ -402,7 +447,12 @@ describe('feed generation', () => { it('describes multiple feed gens', async () => { const resEven = await agent.api.app.bsky.feed.getFeedGenerators( { feeds: [feedUriEven, feedUriAll, feedUriPrime] }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyFeedGetFeedGenerators, + ), + }, ) expect(forSnapshot(resEven.data)).toMatchSnapshot() expect(resEven.data.feeds.map((fg) => fg.uri)).not.toContain(feedUriPrime) // taken-down @@ -413,7 +463,12 @@ describe('feed generation', () => { it('returns list of suggested feed generators', async () => { const resEven = await agent.api.app.bsky.feed.getSuggestedFeeds( {}, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyFeedGetSuggestedFeeds, + ), + }, ) expect(forSnapshot(resEven.data)).toMatchSnapshot() expect(resEven.data.feeds.map((fg) => fg.uri)).not.toContain(feedUriPrime) // taken-down @@ -424,7 +479,12 @@ describe('feed generation', () => { it('gets popular feed generators', async () => { const res = await agent.api.app.bsky.unspecced.getPopularFeedGenerators( {}, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyUnspeccedGetPopularFeedGenerators, + ), + }, ) expect(res.data.feeds.map((f) => f.uri)).not.toContain(feedUriPrime) // taken-down expect(res.data.feeds.map((f) => f.uri)).toEqual([ @@ -437,7 +497,12 @@ describe('feed generation', () => { it('searches feed generators', async () => { const res = await agent.api.app.bsky.unspecced.getPopularFeedGenerators( { query: 'all' }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyUnspeccedGetPopularFeedGenerators, + ), + }, ) expect(res.data.feeds.map((f) => f.uri)).toEqual([feedUriAll]) }) @@ -446,17 +511,32 @@ describe('feed generation', () => { const resFull = await agent.api.app.bsky.unspecced.getPopularFeedGenerators( {}, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyUnspeccedGetPopularFeedGenerators, + ), + }, ) const resOne = await agent.api.app.bsky.unspecced.getPopularFeedGenerators( { limit: 2 }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyUnspeccedGetPopularFeedGenerators, + ), + }, ) const resTwo = await agent.api.app.bsky.unspecced.getPopularFeedGenerators( { cursor: resOne.data.cursor }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyUnspeccedGetPopularFeedGenerators, + ), + }, ) expect([...resOne.data.feeds, ...resTwo.data.feeds]).toEqual( resFull.data.feeds, @@ -468,7 +548,13 @@ describe('feed generation', () => { it('resolves basic feed contents.', async () => { const feed = await agent.api.app.bsky.feed.getFeed( { feed: feedUriEven }, - { headers: await network.serviceHeaders(alice, gen.did) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetFeed, + gen.did, + ), + }, ) expect(feed.data.feed.map((item) => item.post.uri)).toEqual([ sc.posts[sc.dids.alice][0].ref.uriStr, @@ -493,7 +579,13 @@ describe('feed generation', () => { const paginator = async (cursor?: string) => { const res = await agent.api.app.bsky.feed.getFeed( { feed: feedUriAll, cursor, limit: 2 }, - { headers: await network.serviceHeaders(alice, gen.did) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetFeed, + gen.did, + ), + }, ) return res.data } @@ -514,7 +606,13 @@ describe('feed generation', () => { it('paginates, handling feed not respecting limit.', async () => { const res = await agent.api.app.bsky.feed.getFeed( { feed: feedUriBadPagination, limit: 3 }, - { headers: await network.serviceHeaders(alice, gen.did) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetFeed, + gen.did, + ), + }, ) // refused to respect pagination limit, so it got cut short by appview but the cursor remains. expect(res.data.feed.length).toBeLessThanOrEqual(3) @@ -529,7 +627,13 @@ describe('feed generation', () => { it('fails on unknown feed.', async () => { const tryGetFeed = agent.api.app.bsky.feed.getFeed( { feed: feedUriOdd }, - { headers: await network.serviceHeaders(alice, gen.did) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetFeed, + gen.did, + ), + }, ) await expect(tryGetFeed).rejects.toMatchObject({ error: 'UnknownFeed', @@ -539,7 +643,9 @@ describe('feed generation', () => { it('resolves contents of taken-down feed.', async () => { const tryGetFeed = agent.api.app.bsky.feed.getFeed( { feed: feedUriPrime }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders(alice, ids.AppBskyFeedGetFeed), + }, ) await expect(tryGetFeed).resolves.toBeDefined() }) @@ -547,19 +653,19 @@ describe('feed generation', () => { it('receives proper auth details.', async () => { const feed = await agent.api.app.bsky.feed.getFeed( { feed: feedUriEven }, - { headers: await network.serviceHeaders(alice, gen.did) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetFeedSkeleton, + gen.did, + ), + }, ) 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: await network.serviceHeaders(alice, gen.did) }, + expect(feed.data['$auth']?.['lxm']).toEqual( + ids.AppBskyFeedGetFeedSkeleton, ) - expect(feed.data['$auth']?.['aud']).toEqual(gen.did) - expect(feed.data['$auth']?.['iss']).toEqual(alice) }) it('passes through auth error from feed.', async () => { @@ -575,7 +681,13 @@ describe('feed generation', () => { it('provides timing info in server-timing header.', async () => { const result = await agent.api.app.bsky.feed.getFeed( { feed: feedUriEven }, - { headers: await network.serviceHeaders(alice, gen.did) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetFeed, + gen.did, + ), + }, ) expect(result.headers['server-timing']).toMatch( /^skele;dur=\d+, hydr;dur=\d+$/, @@ -586,7 +698,13 @@ describe('feed generation', () => { await gen.close() // @NOTE must be last test const tryGetFeed = agent.api.app.bsky.feed.getFeed( { feed: feedUriEven }, - { headers: await network.serviceHeaders(alice, gen.did) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetFeed, + gen.did, + ), + }, ) await expect(tryGetFeed).rejects.toThrow('feed unavailable') }) diff --git a/packages/bsky/tests/server.test.ts b/packages/bsky/tests/server.test.ts index 3cc0257a4eb..f1bdafeb16d 100644 --- a/packages/bsky/tests/server.test.ts +++ b/packages/bsky/tests/server.test.ts @@ -89,7 +89,7 @@ describe('server', () => { { decompress: false, headers: { - ...(await network.serviceHeaders(alice)), + ...(await network.serviceHeaders(alice, 'app.bsky.feed.getTimeline')), 'accept-encoding': 'gzip', }, }, diff --git a/packages/bsky/tests/views/account-deactivation.test.ts b/packages/bsky/tests/views/account-deactivation.test.ts index 31b07c01c5c..1efebfdf12e 100644 --- a/packages/bsky/tests/views/account-deactivation.test.ts +++ b/packages/bsky/tests/views/account-deactivation.test.ts @@ -1,5 +1,6 @@ import { AtpAgent } from '@atproto/api' -import { basicSeed, SeedClient, TestNetwork } from '@atproto/dev-env' +import { TestNetwork, SeedClient, basicSeed } from '@atproto/dev-env' +import { ids } from '../../src/lexicon/lexicons' describe('bsky account deactivation', () => { let network: TestNetwork @@ -69,7 +70,12 @@ describe('bsky account deactivation', () => { it('does not return posts from deactivated in timelines', async () => { const res = await agent.api.app.bsky.feed.getTimeline( {}, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyFeedGetTimeline, + ), + }, ) expect(res.data.feed.some((p) => p.post.author.did === alice)).toBe(false) }) diff --git a/packages/bsky/tests/views/actor-likes.test.ts b/packages/bsky/tests/views/actor-likes.test.ts index b65aa8ccc1b..6879bc58240 100644 --- a/packages/bsky/tests/views/actor-likes.test.ts +++ b/packages/bsky/tests/views/actor-likes.test.ts @@ -1,5 +1,6 @@ -import { AtpAgent, AtUri } from '@atproto/api' -import { basicSeed, SeedClient, TestNetwork } from '@atproto/dev-env' +import { AtUri, AtpAgent } from '@atproto/api' +import { TestNetwork, SeedClient, basicSeed } from '@atproto/dev-env' +import { ids } from '../../src/lexicon/lexicons' describe('bsky actor likes feed views', () => { let network: TestNetwork @@ -35,7 +36,12 @@ describe('bsky actor likes feed views', () => { data: { feed: bobLikes }, } = await agent.api.app.bsky.feed.getActorLikes( { actor: sc.accounts[bob].handle }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetActorLikes, + ), + }, ) expect(bobLikes).toHaveLength(3) @@ -43,7 +49,12 @@ describe('bsky actor likes feed views', () => { await expect( agent.api.app.bsky.feed.getActorLikes( { actor: sc.accounts[bob].handle }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetActorLikes, + ), + }, ), ).rejects.toThrow('Profile not found') }) @@ -66,7 +77,12 @@ describe('bsky actor likes feed views', () => { data: { feed }, } = await agent.api.app.bsky.feed.getActorLikes( { actor: sc.accounts[bob].handle }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetActorLikes, + ), + }, ) expect( @@ -100,7 +116,12 @@ describe('bsky actor likes feed views', () => { data: { feed }, } = await agent.api.app.bsky.feed.getActorLikes( { actor: sc.accounts[bob].handle }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetActorLikes, + ), + }, ) expect( diff --git a/packages/bsky/tests/views/actor-search.test.ts b/packages/bsky/tests/views/actor-search.test.ts index 7bbf7fa0af5..5537794764d 100644 --- a/packages/bsky/tests/views/actor-search.test.ts +++ b/packages/bsky/tests/views/actor-search.test.ts @@ -2,6 +2,7 @@ import { AtpAgent } from '@atproto/api' import { wait } from '@atproto/common' import { TestNetwork, SeedClient, usersBulkSeed } from '@atproto/dev-env' import { forSnapshot, paginateAll, stripViewer } from '../_util' +import { ids } from '../../src/lexicon/lexicons' // @NOTE skipped to help with CI failures // The search code is not used in production & we should switch it out for tests on the search proxy interface @@ -40,7 +41,10 @@ describe.skip('pds actor search views', () => { // Process remaining profiles network.bsky.sub.run() await network.processAll(50000) - headers = await network.serviceHeaders(Object.values(sc.dids)[0]) + headers = await network.serviceHeaders( + Object.values(sc.dids)[0], + ids.AppBskyActorSearchActorsTypeahead, + ) }) afterAll(async () => { diff --git a/packages/bsky/tests/views/author-feed.test.ts b/packages/bsky/tests/views/author-feed.test.ts index 7cf6bf3c778..8162c6c8cea 100644 --- a/packages/bsky/tests/views/author-feed.test.ts +++ b/packages/bsky/tests/views/author-feed.test.ts @@ -10,6 +10,7 @@ import { ReplyRef, 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' import { isPostView } from '../../src/lexicon/types/app/bsky/feed/defs' +import { ids } from '../../src/lexicon/lexicons' describe('pds author feed views', () => { let network: TestNetwork @@ -48,28 +49,48 @@ describe('pds author feed views', () => { 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: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) expect(forSnapshot(aliceForAlice.data.feed)).toMatchSnapshot() const bobForBob = await agent.api.app.bsky.feed.getAuthorFeed( { actor: sc.accounts[bob].handle }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) expect(forSnapshot(bobForBob.data.feed)).toMatchSnapshot() const carolForCarol = await agent.api.app.bsky.feed.getAuthorFeed( { actor: sc.accounts[carol].handle }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) expect(forSnapshot(carolForCarol.data.feed)).toMatchSnapshot() const danForDan = await agent.api.app.bsky.feed.getAuthorFeed( { actor: sc.accounts[dan].handle }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) expect(forSnapshot(danForDan.data.feed)).toMatchSnapshot() @@ -78,7 +99,12 @@ describe('pds author feed views', () => { 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: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) aliceForCarol.data.feed.forEach((postView) => { @@ -99,7 +125,12 @@ describe('pds author feed views', () => { cursor, limit: 2, }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) return res.data } @@ -111,7 +142,12 @@ describe('pds author feed views', () => { const full = await agent.api.app.bsky.feed.getAuthorFeed( { actor: sc.accounts[alice].handle }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) expect(full.data.feed.length).toEqual(4) @@ -121,7 +157,12 @@ describe('pds author feed views', () => { it('fetches results unauthed.', async () => { const { data: authed } = await agent.api.app.bsky.feed.getAuthorFeed( { actor: sc.accounts[alice].handle }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) const { data: unauthed } = await agent.api.app.bsky.feed.getAuthorFeed({ actor: sc.accounts[alice].handle, @@ -150,7 +191,12 @@ describe('pds author feed views', () => { it('non-admins blocked by actor takedown.', async () => { const { data: preBlock } = await agent.api.app.bsky.feed.getAuthorFeed( { actor: alice }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) expect(preBlock.feed.length).toBeGreaterThan(0) @@ -161,7 +207,12 @@ describe('pds author feed views', () => { const attemptAsUser = agent.api.app.bsky.feed.getAuthorFeed( { actor: alice }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) await expect(attemptAsUser).rejects.toThrow('Profile not found') @@ -180,7 +231,12 @@ describe('pds author feed views', () => { it('blocked by record takedown.', async () => { const { data: preBlock } = await agent.api.app.bsky.feed.getAuthorFeed( { actor: alice }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) expect(preBlock.feed.length).toBeGreaterThan(0) @@ -195,7 +251,12 @@ describe('pds author feed views', () => { await Promise.all([ agent.api.app.bsky.feed.getAuthorFeed( { actor: alice }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ), agent.api.app.bsky.feed.getAuthorFeed( { actor: alice }, diff --git a/packages/bsky/tests/views/block-lists.test.ts b/packages/bsky/tests/views/block-lists.test.ts index fbdc51bae82..5bd3989e9b5 100644 --- a/packages/bsky/tests/views/block-lists.test.ts +++ b/packages/bsky/tests/views/block-lists.test.ts @@ -1,6 +1,7 @@ import { AtpAgent, AtUri } from '@atproto/api' import { TestNetwork, SeedClient, RecordRef, basicSeed } from '@atproto/dev-env' import { forSnapshot } from '../_util' +import { ids } from '../../src/lexicon/lexicons' describe('pds views with blocking from block lists', () => { let network: TestNetwork @@ -109,7 +110,12 @@ describe('pds views with blocking from block lists', () => { const { carol, dan } = sc.dids const { data: threadAlice } = await agent.api.app.bsky.feed.getPostThread( { depth: 1, uri: sc.posts[carol][0].ref.uriStr }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(threadAlice.thread).toEqual( expect.objectContaining({ @@ -120,7 +126,12 @@ describe('pds views with blocking from block lists', () => { ) const { data: threadCarol } = await agent.api.app.bsky.feed.getPostThread( { depth: 1, uri: sc.posts[dan][0].ref.uriStr }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(threadCarol.thread).toEqual( expect.objectContaining({ @@ -135,7 +146,12 @@ describe('pds views with blocking from block lists', () => { // Contains reply by carol const { data: thread } = await agent.api.app.bsky.feed.getPostThread( { depth: 1, uri: sc.posts[alice][1].ref.uriStr }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(thread)).toMatchSnapshot() }) @@ -144,7 +160,12 @@ describe('pds views with blocking from block lists', () => { // Parent is a post by dan const { data: thread } = await agent.api.app.bsky.feed.getPostThread( { depth: 1, uri: aliceReplyToDan.ref.uriStr }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(thread)).toMatchSnapshot() }) @@ -153,7 +174,12 @@ describe('pds views with blocking from block lists', () => { // 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: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(thread)).toMatchSnapshot() }) @@ -161,7 +187,12 @@ describe('pds views with blocking from block lists', () => { it('errors on getting author feed', async () => { const attempt1 = agent.api.app.bsky.feed.getAuthorFeed( { actor: carol }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) await expect(attempt1).rejects.toMatchObject({ error: 'BlockedActor', @@ -169,7 +200,12 @@ describe('pds views with blocking from block lists', () => { const attempt2 = agent.api.app.bsky.feed.getAuthorFeed( { actor: dan }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) await expect(attempt2).rejects.toMatchObject({ error: 'BlockedByActor', @@ -179,7 +215,12 @@ describe('pds views with blocking from block lists', () => { it('strips blocked users out of getTimeline', async () => { const resCarol = await agent.api.app.bsky.feed.getTimeline( { limit: 100 }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetTimeline, + ), + }, ) expect( resCarol.data.feed.some((post) => post.post.author.did === dan), @@ -187,7 +228,9 @@ describe('pds views with blocking from block lists', () => { const resDan = await agent.api.app.bsky.feed.getTimeline( { limit: 100 }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders(dan, ids.AppBskyFeedGetTimeline), + }, ) expect( resDan.data.feed.some((post) => @@ -199,7 +242,12 @@ describe('pds views with blocking from block lists', () => { it('returns block status on getProfile', async () => { const resCarol = await agent.api.app.bsky.actor.getProfile( { actor: dan }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyActorGetProfile, + ), + }, ) expect(resCarol.data.viewer?.blocking).toBeUndefined() expect(resCarol.data.viewer?.blockingByList).toBeUndefined() @@ -207,7 +255,9 @@ describe('pds views with blocking from block lists', () => { const resDan = await agent.api.app.bsky.actor.getProfile( { actor: carol }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders(dan, ids.AppBskyActorGetProfile), + }, ) expect(resDan.data.viewer?.blocking).toBeDefined() expect(resDan.data.viewer?.blockingByList?.uri).toEqual( @@ -219,7 +269,12 @@ describe('pds views with blocking from block lists', () => { it('returns block status on getProfiles', async () => { const resCarol = await agent.api.app.bsky.actor.getProfiles( { actors: [alice, dan] }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyActorGetProfiles, + ), + }, ) expect(resCarol.data.profiles[0].viewer?.blocking).toBeUndefined() expect(resCarol.data.profiles[0].viewer?.blockingByList).toBeUndefined() @@ -230,7 +285,9 @@ describe('pds views with blocking from block lists', () => { const resDan = await agent.api.app.bsky.actor.getProfiles( { actors: [alice, carol] }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders(dan, ids.AppBskyActorGetProfiles), + }, ) expect(resDan.data.profiles[0].viewer?.blocking).toBeUndefined() expect(resDan.data.profiles[0].viewer?.blockingByList).toBeUndefined() @@ -245,7 +302,9 @@ describe('pds views with blocking from block lists', () => { it('ignores self-blocks', async () => { const res = await agent.api.app.bsky.actor.getProfile( { actor: dan }, // dan subscribes to list that contains himself - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders(dan, ids.AppBskyActorGetProfile), + }, ) expect(res.data.viewer?.blocking).toBeUndefined() expect(res.data.viewer?.blockingByList).toBeUndefined() @@ -257,7 +316,12 @@ describe('pds views with blocking from block lists', () => { { limit: 100, }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyNotificationListNotifications, + ), + }, ) expect( resCarol.data.notifications.some((notif) => notif.author.did === dan), @@ -267,7 +331,12 @@ describe('pds views with blocking from block lists', () => { { limit: 100, }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyNotificationListNotifications, + ), + }, ) expect( resDan.data.notifications.some((notif) => notif.author.did === carol), @@ -279,7 +348,12 @@ describe('pds views with blocking from block lists', () => { { term: 'dan.test', }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyActorSearchActors, + ), + }, ) expect(resCarol.data.actors.some((actor) => actor.did === dan)).toBeFalsy() @@ -287,7 +361,12 @@ describe('pds views with blocking from block lists', () => { { term: 'carol.test', }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyActorSearchActors, + ), + }, ) expect(resDan.data.actors.some((actor) => actor.did === carol)).toBeFalsy() }) @@ -297,7 +376,12 @@ describe('pds views with blocking from block lists', () => { { term: 'dan.tes', }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyActorSearchActorsTypeahead, + ), + }, ) expect(resCarol.data.actors.some((actor) => actor.did === dan)).toBeFalsy() @@ -305,7 +389,12 @@ describe('pds views with blocking from block lists', () => { { term: 'carol.tes', }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyActorSearchActorsTypeahead, + ), + }, ) expect(resDan.data.actors.some((actor) => actor.did === carol)).toBeFalsy() }) @@ -315,7 +404,12 @@ describe('pds views with blocking from block lists', () => { { term: 'dan.test', }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyActorSearchActorsTypeahead, + ), + }, ) expect(resCarol.data.actors.some((actor) => actor.did === dan)).toBeTruthy() @@ -323,7 +417,12 @@ describe('pds views with blocking from block lists', () => { { term: 'carol.test', }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyActorSearchActorsTypeahead, + ), + }, ) expect(resDan.data.actors.some((actor) => actor.did === carol)).toBeTruthy() }) @@ -338,7 +437,12 @@ describe('pds views with blocking from block lists', () => { { limit: 100, }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyActorGetSuggestions, + ), + }, ) expect(resCarol.data.actors.some((actor) => actor.did === dan)).toBeFalsy() @@ -346,7 +450,12 @@ describe('pds views with blocking from block lists', () => { { limit: 100, }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyActorGetSuggestions, + ), + }, ) expect(resDan.data.actors.some((actor) => actor.did === carol)).toBeFalsy() }) @@ -354,7 +463,7 @@ describe('pds views with blocking from block lists', () => { it('returns the contents of a list', async () => { const res = await agent.api.app.bsky.graph.getList( { list: listUri }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetList) }, ) expect(forSnapshot(res.data)).toMatchSnapshot() }) @@ -362,15 +471,15 @@ describe('pds views with blocking from block lists', () => { it('paginates getList', async () => { const full = await agent.api.app.bsky.graph.getList( { list: listUri }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetList) }, ) const first = await agent.api.app.bsky.graph.getList( { list: listUri, limit: 1 }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetList) }, ) const second = await agent.api.app.bsky.graph.getList( { list: listUri, cursor: first.data.cursor }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetList) }, ) const combined = [...first.data.items, ...second.data.items] expect(combined).toEqual(full.data.items) @@ -394,7 +503,7 @@ describe('pds views with blocking from block lists', () => { const res = await agent.api.app.bsky.graph.getLists( { actor: alice }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetLists) }, ) expect(forSnapshot(res.data)).toMatchSnapshot() }) @@ -402,15 +511,15 @@ describe('pds views with blocking from block lists', () => { it('paginates getLists', async () => { const full = await agent.api.app.bsky.graph.getLists( { actor: alice }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetLists) }, ) const first = await agent.api.app.bsky.graph.getLists( { actor: alice, limit: 1 }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetLists) }, ) const second = await agent.api.app.bsky.graph.getLists( { actor: alice, cursor: first.data.cursor }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetLists) }, ) const combined = [...first.data.lists, ...second.data.lists] expect(combined).toEqual(full.data.lists) @@ -429,7 +538,12 @@ describe('pds views with blocking from block lists', () => { const res = await agent.api.app.bsky.graph.getListBlocks( {}, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyGraphGetListBlocks, + ), + }, ) expect(forSnapshot(res.data)).toMatchSnapshot() }) @@ -437,15 +551,30 @@ describe('pds views with blocking from block lists', () => { it('paginates getListBlocks', async () => { const full = await agent.api.app.bsky.graph.getListBlocks( {}, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyGraphGetListBlocks, + ), + }, ) const first = await agent.api.app.bsky.graph.getListBlocks( { limit: 1 }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyGraphGetListBlocks, + ), + }, ) const second = await agent.api.app.bsky.graph.getListBlocks( { cursor: first.data.cursor }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyGraphGetListBlocks, + ), + }, ) const combined = [...first.data.lists, ...second.data.lists] expect(combined).toEqual(full.data.lists) @@ -470,7 +599,12 @@ describe('pds views with blocking from block lists', () => { const resCarol = await agent.api.app.bsky.feed.getTimeline( { limit: 100 }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetTimeline, + ), + }, ) expect( resCarol.data.feed.some((post) => post.post.author.did === dan), @@ -478,7 +612,9 @@ describe('pds views with blocking from block lists', () => { const resDan = await agent.api.app.bsky.feed.getTimeline( { limit: 100 }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders(dan, ids.AppBskyFeedGetTimeline), + }, ) expect( resDan.data.feed.some((post) => @@ -500,7 +636,12 @@ describe('pds views with blocking from block lists', () => { const resCarol = await agent.api.app.bsky.feed.getTimeline( { limit: 100 }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetTimeline, + ), + }, ) expect( resCarol.data.feed.some((post) => post.post.author.did === dan), @@ -508,7 +649,9 @@ describe('pds views with blocking from block lists', () => { const resDan = await agent.api.app.bsky.feed.getTimeline( { limit: 100 }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders(dan, ids.AppBskyFeedGetTimeline), + }, ) expect( resDan.data.feed.some((post) => diff --git a/packages/bsky/tests/views/blocks.test.ts b/packages/bsky/tests/views/blocks.test.ts index 50c1649a509..848b8f89c20 100644 --- a/packages/bsky/tests/views/blocks.test.ts +++ b/packages/bsky/tests/views/blocks.test.ts @@ -2,6 +2,7 @@ import assert from 'assert' import { TestNetwork, RecordRef, SeedClient, basicSeed } from '@atproto/dev-env' import { AtpAgent, AtUri } from '@atproto/api' import { assertIsThreadViewPost, forSnapshot } from '../_util' +import { ids } from '../../src/lexicon/lexicons' describe('pds views with blocking', () => { let network: TestNetwork @@ -68,7 +69,12 @@ describe('pds views with blocking', () => { 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: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(threadAlice).toEqual({ thread: { @@ -86,7 +92,12 @@ describe('pds views with blocking', () => { }) const { data: threadCarol } = await agent.api.app.bsky.feed.getPostThread( { depth: 1, uri: sc.posts[dan][0].ref.uriStr }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(threadCarol).toEqual({ thread: { @@ -107,7 +118,12 @@ describe('pds views with blocking', () => { // Contains reply by carol const { data: thread } = await agent.api.app.bsky.feed.getPostThread( { depth: 1, uri: sc.posts[alice][1].ref.uriStr }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(thread)).toMatchSnapshot() }) @@ -115,7 +131,12 @@ describe('pds views with blocking', () => { it('loads blocked reply as anchor with blocked parent', async () => { const { data: thread } = await agent.api.app.bsky.feed.getPostThread( { depth: 1, uri: carolReplyToDan.ref.uriStr }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assertIsThreadViewPost(thread.thread) @@ -131,7 +152,12 @@ describe('pds views with blocking', () => { // Parent is a post by dan const { data: thread } = await agent.api.app.bsky.feed.getPostThread( { depth: 1, uri: aliceReplyToDan.ref.uriStr }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(thread)).toMatchSnapshot() }) @@ -140,7 +166,12 @@ describe('pds views with blocking', () => { // 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: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(thread)).toMatchSnapshot() }) @@ -148,7 +179,12 @@ describe('pds views with blocking', () => { it('errors on getting author feed', async () => { const attempt1 = agent.api.app.bsky.feed.getAuthorFeed( { actor: carol }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) await expect(attempt1).rejects.toMatchObject({ error: 'BlockedActor', @@ -156,7 +192,12 @@ describe('pds views with blocking', () => { const attempt2 = agent.api.app.bsky.feed.getAuthorFeed( { actor: dan }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) await expect(attempt2).rejects.toMatchObject({ error: 'BlockedByActor', @@ -166,7 +207,12 @@ describe('pds views with blocking', () => { it('strips blocked users out of getTimeline', async () => { const resCarol = await agent.api.app.bsky.feed.getTimeline( { limit: 100 }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetTimeline, + ), + }, ) // dan's posts don't appear, nor alice's reply to dan, nor carol's reply to alice (which was a reply to dan) @@ -181,7 +227,9 @@ describe('pds views with blocking', () => { const resDan = await agent.api.app.bsky.feed.getTimeline( { limit: 100 }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders(dan, ids.AppBskyFeedGetTimeline), + }, ) expect( resDan.data.feed.some( @@ -201,7 +249,12 @@ describe('pds views with blocking', () => { const resCarol = await agent.api.app.bsky.feed.getListFeed( { list: listRef.uriStr, limit: 100 }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetListFeed, + ), + }, ) expect( resCarol.data.feed.some((post) => post.post.author.did === dan), @@ -209,7 +262,9 @@ describe('pds views with blocking', () => { const resDan = await agent.api.app.bsky.feed.getListFeed( { list: listRef.uriStr, limit: 100 }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders(dan, ids.AppBskyFeedGetListFeed), + }, ) expect( resDan.data.feed.some((post) => post.post.author.did === carol), @@ -219,14 +274,21 @@ describe('pds views with blocking', () => { it('returns block status on getProfile', async () => { const resCarol = await agent.api.app.bsky.actor.getProfile( { actor: dan }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyActorGetProfile, + ), + }, ) 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: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders(dan, ids.AppBskyActorGetProfile), + }, ) expect(resDan.data.viewer?.blocking).toBeDefined() expect(resDan.data.viewer?.blockedBy).toBe(false) @@ -236,13 +298,15 @@ describe('pds views with blocking', () => { // there are follows between carol and dan const { data: profile } = await agent.api.app.bsky.actor.getProfile( { actor: carol }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders(dan, ids.AppBskyActorGetProfile), + }, ) expect(profile.viewer?.following).toBeUndefined() expect(profile.viewer?.followedBy).toBeUndefined() const { data: result } = await agent.api.app.bsky.graph.getBlocks( {}, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetBlocks) }, ) const blocked = result.blocks.find((block) => block.did === carol) expect(blocked).toBeDefined() @@ -253,7 +317,12 @@ describe('pds views with blocking', () => { it('returns block status on getProfiles', async () => { const resCarol = await agent.api.app.bsky.actor.getProfiles( { actors: [alice, dan] }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyActorGetProfiles, + ), + }, ) expect(resCarol.data.profiles[0].viewer?.blocking).toBeUndefined() expect(resCarol.data.profiles[0].viewer?.blockingByList).toBeUndefined() @@ -264,7 +333,9 @@ describe('pds views with blocking', () => { const resDan = await agent.api.app.bsky.actor.getProfiles( { actors: [alice, carol] }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders(dan, ids.AppBskyActorGetProfiles), + }, ) expect(resDan.data.profiles[0].viewer?.blocking).toBeUndefined() expect(resDan.data.profiles[0].viewer?.blockingByList).toBeUndefined() @@ -277,13 +348,23 @@ describe('pds views with blocking', () => { it('does not return block violating follows', async () => { const resCarol = await agent.api.app.bsky.graph.getFollows( { actor: carol }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollows, + ), + }, ) expect(resCarol.data.follows.some((f) => f.did === dan)).toBe(false) const resDan = await agent.api.app.bsky.graph.getFollows( { actor: dan }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollows, + ), + }, ) expect(resDan.data.follows.some((f) => f.did === carol)).toBe(false) }) @@ -291,13 +372,23 @@ describe('pds views with blocking', () => { it('does not return block violating followers', async () => { const resCarol = await agent.api.app.bsky.graph.getFollowers( { actor: carol }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollowers, + ), + }, ) expect(resCarol.data.followers.some((f) => f.did === dan)).toBe(false) const resDan = await agent.api.app.bsky.graph.getFollowers( { actor: dan }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollowers, + ), + }, ) expect(resDan.data.followers.some((f) => f.did === carol)).toBe(false) }) @@ -309,7 +400,7 @@ describe('pds views with blocking', () => { const resCarol = await agent.api.app.bsky.feed.getPosts( { uris: [alicePost, carolPost, danPost] }, - { headers: await network.serviceHeaders(carol) }, + { headers: await network.serviceHeaders(carol, ids.AppBskyFeedGetPosts) }, ) expect(resCarol.data.posts.some((p) => p.uri === alicePost)).toBe(true) expect(resCarol.data.posts.some((p) => p.uri === carolPost)).toBe(true) @@ -317,7 +408,7 @@ describe('pds views with blocking', () => { const resDan = await agent.api.app.bsky.feed.getPosts( { uris: [alicePost, carolPost, danPost] }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyFeedGetPosts) }, ) expect(resDan.data.posts.some((p) => p.uri === alicePost)).toBe(true) expect(resDan.data.posts.some((p) => p.uri === carolPost)).toBe(false) @@ -329,7 +420,12 @@ describe('pds views with blocking', () => { { limit: 100, }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyNotificationListNotifications, + ), + }, ) expect( resCarol.data.notifications.some((notif) => notif.author.did === dan), @@ -339,7 +435,12 @@ describe('pds views with blocking', () => { { limit: 100, }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyNotificationListNotifications, + ), + }, ) expect( resDan.data.notifications.some((notif) => notif.author.did === carol), @@ -351,7 +452,12 @@ describe('pds views with blocking', () => { { term: 'dan.test', }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyActorSearchActors, + ), + }, ) expect(resCarol.data.actors.some((actor) => actor.did === dan)).toBeFalsy() @@ -359,7 +465,12 @@ describe('pds views with blocking', () => { { term: 'carol.test', }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyActorSearchActors, + ), + }, ) expect(resDan.data.actors.some((actor) => actor.did === carol)).toBeFalsy() }) @@ -369,7 +480,12 @@ describe('pds views with blocking', () => { { term: 'dan.tes', }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyActorSearchActorsTypeahead, + ), + }, ) expect(resCarol.data.actors.some((actor) => actor.did === dan)).toBeFalsy() @@ -377,7 +493,12 @@ describe('pds views with blocking', () => { { term: 'carol.tes', }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyActorSearchActorsTypeahead, + ), + }, ) expect(resDan.data.actors.some((actor) => actor.did === carol)).toBeFalsy() }) @@ -387,7 +508,12 @@ describe('pds views with blocking', () => { { term: 'dan.test', }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyActorSearchActorsTypeahead, + ), + }, ) expect(resCarol.data.actors.some((actor) => actor.did === dan)).toBeTruthy() @@ -395,7 +521,12 @@ describe('pds views with blocking', () => { { term: 'carol.test', }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyActorSearchActorsTypeahead, + ), + }, ) expect(resDan.data.actors.some((actor) => actor.did === carol)).toBeTruthy() }) @@ -410,7 +541,12 @@ describe('pds views with blocking', () => { { limit: 100, }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyActorGetSuggestions, + ), + }, ) expect(resCarol.data.actors.some((actor) => actor.did === dan)).toBeFalsy() @@ -418,7 +554,12 @@ describe('pds views with blocking', () => { { limit: 100, }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyActorGetSuggestions, + ), + }, ) expect(resDan.data.actors.some((actor) => actor.did === carol)).toBeFalsy() }) @@ -429,7 +570,12 @@ describe('pds views with blocking', () => { const { data: replyThenBlock } = await agent.api.app.bsky.feed.getPostThread( { depth: 1, uri: sc.posts[dan][0].ref.uriStr }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assertIsThreadViewPost(replyThenBlock.thread) @@ -445,7 +591,12 @@ describe('pds views with blocking', () => { await network.processAll() const { data: unblock } = await agent.api.app.bsky.feed.getPostThread( { depth: 1, uri: sc.posts[dan][0].ref.uriStr }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assertIsThreadViewPost(unblock.thread) @@ -470,7 +621,12 @@ describe('pds views with blocking', () => { const { data: blockThenReply } = await agent.api.app.bsky.feed.getPostThread( { depth: 1, uri: sc.posts[dan][0].ref.uriStr }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assertIsThreadViewPost(blockThenReply.thread) @@ -492,7 +648,12 @@ describe('pds views with blocking', () => { const { data: embedThenBlock } = await agent.api.app.bsky.feed.getPostThread( { depth: 0, uri: sc.posts[dan][1].ref.uriStr }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assertIsThreadViewPost(embedThenBlock.thread) @@ -509,7 +670,12 @@ describe('pds views with blocking', () => { await network.processAll() const { data: unblock } = await agent.api.app.bsky.feed.getPostThread( { depth: 0, uri: sc.posts[dan][1].ref.uriStr }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assertIsThreadViewPost(unblock.thread) @@ -535,7 +701,12 @@ describe('pds views with blocking', () => { const { data: blockThenEmbed } = await agent.api.app.bsky.feed.getPostThread( { depth: 0, uri: carolEmbedsDan.ref.uriStr }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assertIsThreadViewPost(blockThenEmbed.thread) @@ -558,7 +729,12 @@ describe('pds views with blocking', () => { const embedBlockedUri = sc.posts[dan][1].ref.uriStr const { data: timeline } = await agent.api.app.bsky.feed.getTimeline( { limit: 100 }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetTimeline, + ), + }, ) const replyBlockedPost = timeline.feed.find( (item) => item.post.uri === replyBlockedUri, @@ -593,7 +769,7 @@ describe('pds views with blocking', () => { const res = await agent.api.app.bsky.graph.getBlocks( {}, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetBlocks) }, ) const dids = res.data.blocks.map((block) => block.did).sort() expect(dids).toEqual([alice, carol].sort()) @@ -602,15 +778,15 @@ describe('pds views with blocking', () => { it('paginates getBlocks', async () => { const full = await agent.api.app.bsky.graph.getBlocks( {}, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetBlocks) }, ) const first = await agent.api.app.bsky.graph.getBlocks( { limit: 1 }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetBlocks) }, ) const second = await agent.api.app.bsky.graph.getBlocks( { cursor: first.data.cursor }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetBlocks) }, ) const combined = [...first.data.blocks, ...second.data.blocks] expect(combined).toEqual(full.data.blocks) @@ -619,7 +795,12 @@ describe('pds views with blocking', () => { it('returns knownFollowers with blocks filtered', async () => { const carolForAlice = await agent.api.app.bsky.actor.getProfile( { actor: bob }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyActorGetProfile, + ), + }, ) const knownFollowers = carolForAlice.data.viewer?.knownFollowers diff --git a/packages/bsky/tests/views/follows.test.ts b/packages/bsky/tests/views/follows.test.ts index 2300a1b1ed8..83aa3a40dd6 100644 --- a/packages/bsky/tests/views/follows.test.ts +++ b/packages/bsky/tests/views/follows.test.ts @@ -1,6 +1,7 @@ import { AtpAgent } from '@atproto/api' import { TestNetwork, SeedClient, followsSeed } from '@atproto/dev-env' import { forSnapshot, paginateAll, stripViewer } from '../_util' +import { ids } from '../../src/lexicon/lexicons' describe('pds follow views', () => { let agent: AtpAgent @@ -31,35 +32,60 @@ describe('pds follow views', () => { it('fetches followers', async () => { const aliceFollowers = await agent.api.app.bsky.graph.getFollowers( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollowers, + ), + }, ) expect(forSnapshot(aliceFollowers.data)).toMatchSnapshot() const bobFollowers = await agent.api.app.bsky.graph.getFollowers( { actor: sc.dids.bob }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollowers, + ), + }, ) expect(forSnapshot(bobFollowers.data)).toMatchSnapshot() const carolFollowers = await agent.api.app.bsky.graph.getFollowers( { actor: sc.dids.carol }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollowers, + ), + }, ) expect(forSnapshot(carolFollowers.data)).toMatchSnapshot() const danFollowers = await agent.api.app.bsky.graph.getFollowers( { actor: sc.dids.dan }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollowers, + ), + }, ) expect(forSnapshot(danFollowers.data)).toMatchSnapshot() const eveFollowers = await agent.api.app.bsky.graph.getFollowers( { actor: sc.dids.eve }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollowers, + ), + }, ) expect(forSnapshot(eveFollowers.data)).toMatchSnapshot() @@ -68,11 +94,21 @@ describe('pds follow views', () => { it('fetches followers by handle', async () => { const byDid = await agent.api.app.bsky.graph.getFollowers( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollowers, + ), + }, ) const byHandle = await agent.api.app.bsky.graph.getFollowers( { actor: sc.accounts[alice].handle }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollowers, + ), + }, ) expect(byHandle.data).toEqual(byDid.data) }) @@ -86,7 +122,12 @@ describe('pds follow views', () => { cursor, limit: 2, }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollowers, + ), + }, ) return res.data } @@ -98,7 +139,12 @@ describe('pds follow views', () => { const full = await agent.api.app.bsky.graph.getFollowers( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollowers, + ), + }, ) expect(full.data.followers.length).toEqual(4) @@ -108,7 +154,12 @@ describe('pds follow views', () => { it('fetches followers unauthed', async () => { const { data: authed } = await agent.api.app.bsky.graph.getFollowers( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollowers, + ), + }, ) const { data: unauthed } = await agent.api.app.bsky.graph.getFollowers({ actor: sc.dids.alice, @@ -124,7 +175,12 @@ describe('pds follow views', () => { const aliceFollowers = await agent.api.app.bsky.graph.getFollowers( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollowers, + ), + }, ) expect(aliceFollowers.data.followers.map((f) => f.did)).not.toContain( @@ -139,35 +195,60 @@ describe('pds follow views', () => { it('fetches follows', async () => { const aliceFollowers = await agent.api.app.bsky.graph.getFollows( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollows, + ), + }, ) expect(forSnapshot(aliceFollowers.data)).toMatchSnapshot() const bobFollowers = await agent.api.app.bsky.graph.getFollows( { actor: sc.dids.bob }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollows, + ), + }, ) expect(forSnapshot(bobFollowers.data)).toMatchSnapshot() const carolFollowers = await agent.api.app.bsky.graph.getFollows( { actor: sc.dids.carol }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollows, + ), + }, ) expect(forSnapshot(carolFollowers.data)).toMatchSnapshot() const danFollowers = await agent.api.app.bsky.graph.getFollows( { actor: sc.dids.dan }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollows, + ), + }, ) expect(forSnapshot(danFollowers.data)).toMatchSnapshot() const eveFollowers = await agent.api.app.bsky.graph.getFollows( { actor: sc.dids.eve }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollows, + ), + }, ) expect(forSnapshot(eveFollowers.data)).toMatchSnapshot() @@ -176,11 +257,21 @@ describe('pds follow views', () => { it('fetches follows by handle', async () => { const byDid = await agent.api.app.bsky.graph.getFollows( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollows, + ), + }, ) const byHandle = await agent.api.app.bsky.graph.getFollows( { actor: sc.accounts[alice].handle }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollows, + ), + }, ) expect(byHandle.data).toEqual(byDid.data) }) @@ -194,7 +285,12 @@ describe('pds follow views', () => { cursor, limit: 2, }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollows, + ), + }, ) return res.data } @@ -206,7 +302,12 @@ describe('pds follow views', () => { const full = await agent.api.app.bsky.graph.getFollows( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollows, + ), + }, ) expect(full.data.follows.length).toEqual(4) @@ -216,7 +317,12 @@ describe('pds follow views', () => { it('fetches follows unauthed', async () => { const { data: authed } = await agent.api.app.bsky.graph.getFollows( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollows, + ), + }, ) const { data: unauthed } = await agent.api.app.bsky.graph.getFollows({ actor: sc.dids.alice, @@ -232,7 +338,12 @@ describe('pds follow views', () => { const aliceFollows = await agent.api.app.bsky.graph.getFollows( { actor: sc.dids.alice }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetFollows, + ), + }, ) expect(aliceFollows.data.follows.map((f) => f.did)).not.toContain( diff --git a/packages/bsky/tests/views/known-followers.test.ts b/packages/bsky/tests/views/known-followers.test.ts index 0959d36ecca..c6e4297c05c 100644 --- a/packages/bsky/tests/views/known-followers.test.ts +++ b/packages/bsky/tests/views/known-followers.test.ts @@ -2,6 +2,7 @@ import { TestNetwork, SeedClient } from '@atproto/dev-env' import { AtpAgent } from '@atproto/api' import { knownFollowersSeed } from '../seed/known-followers' +import { ids } from '../../src/lexicon/lexicons' describe('known followers (social proof)', () => { let network: TestNetwork @@ -70,7 +71,12 @@ describe('known followers (social proof)', () => { it('basic profile views do not return knownFollowers', async () => { const { data } = await agent.api.app.bsky.graph.getFollows( { actor: dids.base_res_1 }, - { headers: await network.serviceHeaders(dids.base_view) }, + { + headers: await network.serviceHeaders( + dids.base_view, + ids.AppBskyGraphGetFollows, + ), + }, ) const follow = data.follows[0] @@ -80,7 +86,12 @@ describe('known followers (social proof)', () => { it('getKnownFollowers: returns data', async () => { const { data } = await agent.api.app.bsky.graph.getKnownFollowers( { actor: dids.base_sub }, - { headers: await network.serviceHeaders(dids.base_view) }, + { + headers: await network.serviceHeaders( + dids.base_view, + ids.AppBskyGraphGetKnownFollowers, + ), + }, ) expect(data.subject.did).toBe(dids.base_sub) @@ -91,7 +102,12 @@ describe('known followers (social proof)', () => { it('getProfile: returns knownFollowers', async () => { const { data } = await agent.api.app.bsky.actor.getProfile( { actor: dids.base_sub }, - { headers: await network.serviceHeaders(dids.base_view) }, + { + headers: await network.serviceHeaders( + dids.base_view, + ids.AppBskyActorGetProfile, + ), + }, ) const knownFollowers = data.viewer?.knownFollowers @@ -103,7 +119,12 @@ describe('known followers (social proof)', () => { it('getProfile: filters 1st-party blocks', async () => { const { data } = await agent.api.app.bsky.actor.getProfile( { actor: dids.fp_block_sub }, - { headers: await network.serviceHeaders(dids.fp_block_view) }, + { + headers: await network.serviceHeaders( + dids.fp_block_view, + ids.AppBskyActorGetProfile, + ), + }, ) const knownFollowers = data.viewer?.knownFollowers @@ -114,7 +135,12 @@ describe('known followers (social proof)', () => { it('getProfile: filters second-party blocks', async () => { const result = await agent.api.app.bsky.actor.getProfile( { actor: dids.sp_block_sub }, - { headers: await network.serviceHeaders(dids.sp_block_view) }, + { + headers: await network.serviceHeaders( + dids.sp_block_view, + ids.AppBskyActorGetProfile, + ), + }, ) const knownFollowers = result.data.viewer?.knownFollowers @@ -125,7 +151,12 @@ describe('known followers (social proof)', () => { it('getProfiles: filters second-party blocks', async () => { const result = await agent.api.app.bsky.actor.getProfiles( { actors: [dids.sp_block_sub] }, - { headers: await network.serviceHeaders(dids.sp_block_view) }, + { + headers: await network.serviceHeaders( + dids.sp_block_view, + ids.AppBskyActorGetProfiles, + ), + }, ) expect(result.data.profiles).toHaveLength(1) @@ -138,7 +169,12 @@ describe('known followers (social proof)', () => { it('getProfiles: mix of results', async () => { const result = await agent.api.app.bsky.actor.getProfiles( { actors: [dids.mix_sub_1, dids.mix_sub_2, dids.mix_sub_3] }, - { headers: await network.serviceHeaders(dids.mix_view) }, + { + headers: await network.serviceHeaders( + dids.mix_view, + ids.AppBskyActorGetProfiles, + ), + }, ) expect(result.data.profiles).toHaveLength(3) diff --git a/packages/bsky/tests/views/labeler-service.test.ts b/packages/bsky/tests/views/labeler-service.test.ts index 96deab9d9e9..a979667aa6d 100644 --- a/packages/bsky/tests/views/labeler-service.test.ts +++ b/packages/bsky/tests/views/labeler-service.test.ts @@ -68,7 +68,12 @@ describe('labeler service views', () => { it('fetches labelers', async () => { const view = await agent.api.app.bsky.labeler.getServices( { dids: [alice, bob, 'did:example:missing'] }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyLabelerGetServices, + ), + }, ) expect(forSnapshot(view.data)).toMatchSnapshot() @@ -77,7 +82,12 @@ describe('labeler service views', () => { it('fetches labelers detailed', async () => { const view = await agent.api.app.bsky.labeler.getServices( { dids: [alice, bob, 'did:example:missing'], detailed: true }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyLabelerGetServices, + ), + }, ) expect(forSnapshot(view.data)).toMatchSnapshot() @@ -86,7 +96,12 @@ describe('labeler service views', () => { it('fetches labelers unauthed', async () => { const { data: authed } = await agent.api.app.bsky.labeler.getServices( { dids: [alice] }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyLabelerGetServices, + ), + }, ) const { data: unauthed } = await agent.api.app.bsky.labeler.getServices({ dids: [alice], @@ -99,7 +114,12 @@ describe('labeler service views', () => { { dids: [alice, bob, 'did:example:missing'], }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyLabelerGetServices, + ), + }, ) const { data: unauthed } = await agent.api.app.bsky.labeler.getServices({ dids: [alice, bob, 'did:example:missing'], @@ -138,7 +158,12 @@ describe('labeler service views', () => { it('renders profile as labeler in non-detailed profile views', async () => { const { data: res } = await agent.api.app.bsky.actor.searchActors( { q: sc.accounts[alice].handle }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyActorSearchActors, + ), + }, ) expect(res.actors.length).toBe(1) expect(res.actors[0].associated?.labeler).toBe(true) @@ -148,7 +173,12 @@ describe('labeler service views', () => { await network.bsky.ctx.dataplane.takedownActor({ did: alice }) const res = await agent.api.app.bsky.labeler.getServices( { dids: [alice, bob] }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyLabelerGetServices, + ), + }, ) expect(res.data.views.length).toBe(1) // @ts-ignore diff --git a/packages/bsky/tests/views/likes.test.ts b/packages/bsky/tests/views/likes.test.ts index 3f32afc56e9..cb26d6efb63 100644 --- a/packages/bsky/tests/views/likes.test.ts +++ b/packages/bsky/tests/views/likes.test.ts @@ -1,6 +1,7 @@ import { AtpAgent } from '@atproto/api' import { TestNetwork, SeedClient, likesSeed } from '@atproto/dev-env' import { constantDate, forSnapshot, paginateAll, stripViewer } from '../_util' +import { ids } from '../../src/lexicon/lexicons' describe('pds like views', () => { let network: TestNetwork @@ -38,7 +39,7 @@ describe('pds like views', () => { it('fetches post likes', async () => { const alicePost = await agent.api.app.bsky.feed.getLikes( { uri: sc.posts[alice][1].ref.uriStr }, - { headers: await network.serviceHeaders(alice) }, + { headers: await network.serviceHeaders(alice, ids.AppBskyFeedGetLikes) }, ) expect(forSnapshot(alicePost.data)).toMatchSnapshot() @@ -50,7 +51,7 @@ describe('pds like views', () => { it('fetches reply likes', async () => { const bobReply = await agent.api.app.bsky.feed.getLikes( { uri: sc.replies[bob][0].ref.uriStr }, - { headers: await network.serviceHeaders(alice) }, + { headers: await network.serviceHeaders(alice, ids.AppBskyFeedGetLikes) }, ) expect(forSnapshot(bobReply.data)).toMatchSnapshot() @@ -68,7 +69,9 @@ describe('pds like views', () => { cursor, limit: 2, }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders(alice, ids.AppBskyFeedGetLikes), + }, ) return res.data } @@ -80,7 +83,7 @@ describe('pds like views', () => { const full = await agent.api.app.bsky.feed.getLikes( { uri: sc.posts[alice][1].ref.uriStr }, - { headers: await network.serviceHeaders(alice) }, + { headers: await network.serviceHeaders(alice, ids.AppBskyFeedGetLikes) }, ) expect(full.data.likes.length).toEqual(4) @@ -90,7 +93,7 @@ describe('pds like views', () => { it('fetches post likes unauthed', async () => { const { data: authed } = await agent.api.app.bsky.feed.getLikes( { uri: sc.posts[alice][1].ref.uriStr }, - { headers: await network.serviceHeaders(alice) }, + { headers: await network.serviceHeaders(alice, ids.AppBskyFeedGetLikes) }, ) const { data: unauthed } = await agent.api.app.bsky.feed.getLikes({ uri: sc.posts[alice][1].ref.uriStr, diff --git a/packages/bsky/tests/views/list-feed.test.ts b/packages/bsky/tests/views/list-feed.test.ts index 593123f6acd..843df63d45f 100644 --- a/packages/bsky/tests/views/list-feed.test.ts +++ b/packages/bsky/tests/views/list-feed.test.ts @@ -6,6 +6,7 @@ import { stripViewer, stripViewerFromPost, } from '../_util' +import { ids } from '../../src/lexicon/lexicons' describe('list feed views', () => { let network: TestNetwork @@ -42,7 +43,12 @@ describe('list feed views', () => { it('fetches list feed', async () => { const res = await agent.api.app.bsky.feed.getListFeed( { list: listRef.uriStr }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetListFeed, + ), + }, ) expect(forSnapshot(res.data.feed)).toMatchSnapshot() @@ -61,7 +67,12 @@ describe('list feed views', () => { cursor, limit: 2, }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetListFeed, + ), + }, ) return res.data } @@ -73,7 +84,12 @@ describe('list feed views', () => { const full = await agent.api.app.bsky.feed.getListFeed( { list: listRef.uriStr }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetListFeed, + ), + }, ) expect(full.data.feed.length).toEqual(7) @@ -83,7 +99,12 @@ describe('list feed views', () => { it('fetches results unauthed', async () => { const { data: authed } = await agent.api.app.bsky.feed.getListFeed( { list: listRef.uriStr }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetListFeed, + ), + }, ) const { data: unauthed } = await agent.api.app.bsky.feed.getListFeed({ list: listRef.uriStr, diff --git a/packages/bsky/tests/views/lists.test.ts b/packages/bsky/tests/views/lists.test.ts index 3f786996fd1..e1156cf51a9 100644 --- a/packages/bsky/tests/views/lists.test.ts +++ b/packages/bsky/tests/views/lists.test.ts @@ -1,6 +1,7 @@ import { AtpAgent } from '@atproto/api' import { TestNetwork, SeedClient, basicSeed } from '@atproto/dev-env' import { forSnapshot } from '../_util' +import { ids } from '../../src/lexicon/lexicons' describe('bsky actor likes feed views', () => { let network: TestNetwork @@ -66,10 +67,10 @@ describe('bsky actor likes feed views', () => { it('does not include users with creator block relationship in reference lists for non-creator, in-list viewers', async () => { const view = await agent.api.app.bsky.graph.getList( + { list: referenceList }, { - list: referenceList, + headers: await network.serviceHeaders(frankie, ids.AppBskyGraphGetList), }, - { headers: await network.serviceHeaders(frankie) }, ) expect(view.data.items.length).toBe(2) expect(forSnapshot(view.data.items)).toMatchSnapshot() @@ -77,10 +78,8 @@ describe('bsky actor likes feed views', () => { it('does not include users with creator block relationship in reference lists for non-creator, not-in-list viewers', async () => { const view = await agent.api.app.bsky.graph.getList( - { - list: referenceList, - }, - { headers: await network.serviceHeaders(greta) }, + { list: referenceList }, + { headers: await network.serviceHeaders(greta, ids.AppBskyGraphGetList) }, ) expect(view.data.items.length).toBe(2) expect(forSnapshot(view.data.items)).toMatchSnapshot() @@ -96,10 +95,8 @@ describe('bsky actor likes feed views', () => { it('does include users with creator block relationship in reference lists for creator', async () => { const view = await agent.api.app.bsky.graph.getList( - { - list: referenceList, - }, - { headers: await network.serviceHeaders(eve) }, + { list: referenceList }, + { headers: await network.serviceHeaders(eve, ids.AppBskyGraphGetList) }, ) expect(view.data.items.length).toBe(3) expect(forSnapshot(view.data.items)).toMatchSnapshot() diff --git a/packages/bsky/tests/views/mute-lists.test.ts b/packages/bsky/tests/views/mute-lists.test.ts index ee052e49f00..c06712de4d7 100644 --- a/packages/bsky/tests/views/mute-lists.test.ts +++ b/packages/bsky/tests/views/mute-lists.test.ts @@ -1,6 +1,7 @@ import { AtpAgent, AtUri } from '@atproto/api' import { TestNetwork, SeedClient, RecordRef, basicSeed } from '@atproto/dev-env' import { forSnapshot } from '../_util' +import { ids } from '../../src/lexicon/lexicons' describe('bsky views with mutes from mute lists', () => { let network: TestNetwork @@ -98,7 +99,10 @@ describe('bsky views with mutes from mute lists', () => { }, { encoding: 'application/json', - headers: await network.serviceHeaders(dan), + headers: await network.serviceHeaders( + dan, + ids.AppBskyGraphMuteActorList, + ), }, ) }) @@ -106,7 +110,12 @@ describe('bsky views with mutes from mute lists', () => { 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: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(res.data.thread)).toMatchSnapshot() }) @@ -117,7 +126,12 @@ describe('bsky views with mutes from mute lists', () => { const res = await agent.api.app.bsky.feed.getAuthorFeed( { actor: alice }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) expect( res.data.feed.some((post) => [bob, carol].includes(post.post.author.did)), @@ -127,7 +141,9 @@ describe('bsky views with mutes from mute lists', () => { it('removes content from muted users on getTimeline', async () => { const res = await agent.api.app.bsky.feed.getTimeline( { limit: 100 }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders(dan, ids.AppBskyFeedGetTimeline), + }, ) expect( res.data.feed.some((post) => [bob, carol].includes(post.post.author.did)), @@ -141,7 +157,12 @@ describe('bsky views with mutes from mute lists', () => { await sc.addToList(alice, dan, listRef) const res = await agent.api.app.bsky.feed.getListFeed( { list: listRef.uriStr }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetListFeed, + ), + }, ) expect( res.data.feed.some((post) => [bob, carol].includes(post.post.author.did)), @@ -151,7 +172,9 @@ describe('bsky views with mutes from mute lists', () => { it('returns mute status on getProfile', async () => { const res = await agent.api.app.bsky.actor.getProfile( { actor: carol }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders(dan, ids.AppBskyActorGetProfile), + }, ) expect(res.data.viewer?.muted).toBe(true) expect(res.data.viewer?.mutedByList?.uri).toBe(listUri) @@ -160,7 +183,9 @@ describe('bsky views with mutes from mute lists', () => { it('returns mute status on getProfiles', async () => { const res = await agent.api.app.bsky.actor.getProfiles( { actors: [alice, carol] }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders(dan, ids.AppBskyActorGetProfiles), + }, ) expect(res.data.profiles[0].viewer?.muted).toBe(false) expect(res.data.profiles[0].viewer?.mutedByList).toBeUndefined() @@ -171,7 +196,9 @@ describe('bsky views with mutes from mute lists', () => { it('ignores self-mutes', async () => { const res = await agent.api.app.bsky.actor.getProfile( { actor: dan }, // dan subscribes to list that contains himself - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders(dan, ids.AppBskyActorGetProfile), + }, ) expect(res.data.viewer?.muted).toBe(false) expect(res.data.viewer?.mutedByList).toBeUndefined() @@ -182,7 +209,12 @@ describe('bsky views with mutes from mute lists', () => { { limit: 100, }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyNotificationListNotifications, + ), + }, ) expect( res.data.notifications.some((notif) => @@ -200,7 +232,12 @@ describe('bsky views with mutes from mute lists', () => { { limit: 100, }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyActorGetSuggestions, + ), + }, ) for (const actor of res.data.actors) { if ([bob, carol].includes(actor.did)) { @@ -216,7 +253,7 @@ describe('bsky views with mutes from mute lists', () => { it('returns the contents of a list', async () => { const res = await agent.api.app.bsky.graph.getList( { list: listUri }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetList) }, ) expect(forSnapshot(res.data)).toMatchSnapshot() }) @@ -224,15 +261,15 @@ describe('bsky views with mutes from mute lists', () => { it('paginates getList', async () => { const full = await agent.api.app.bsky.graph.getList( { list: listUri }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetList) }, ) const first = await agent.api.app.bsky.graph.getList( { list: listUri, limit: 1 }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetList) }, ) const second = await agent.api.app.bsky.graph.getList( { list: listUri, cursor: first.data.cursor }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetList) }, ) const combined = [...first.data.items, ...second.data.items] expect(combined).toEqual(full.data.items) @@ -257,7 +294,7 @@ describe('bsky views with mutes from mute lists', () => { const res = await agent.api.app.bsky.graph.getLists( { actor: alice }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetLists) }, ) expect(forSnapshot(res.data)).toMatchSnapshot() }) @@ -265,15 +302,15 @@ describe('bsky views with mutes from mute lists', () => { it('paginates getLists', async () => { const full = await agent.api.app.bsky.graph.getLists( { actor: alice }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetLists) }, ) const first = await agent.api.app.bsky.graph.getLists( { actor: alice, limit: 1 }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetLists) }, ) const second = await agent.api.app.bsky.graph.getLists( { actor: alice, cursor: first.data.cursor }, - { headers: await network.serviceHeaders(dan) }, + { headers: await network.serviceHeaders(dan, ids.AppBskyGraphGetLists) }, ) const combined = [...first.data.lists, ...second.data.lists] expect(combined).toEqual(full.data.lists) @@ -286,13 +323,21 @@ describe('bsky views with mutes from mute lists', () => { }, { encoding: 'application/json', - headers: await network.serviceHeaders(dan), + headers: await network.serviceHeaders( + dan, + ids.AppBskyGraphMuteActorList, + ), }, ) const res = await agent.api.app.bsky.graph.getListMutes( {}, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyGraphGetListMutes, + ), + }, ) expect(forSnapshot(res.data)).toMatchSnapshot() }) @@ -300,15 +345,30 @@ describe('bsky views with mutes from mute lists', () => { it('paginates getListMutes', async () => { const full = await agent.api.app.bsky.graph.getListMutes( {}, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyGraphGetListMutes, + ), + }, ) const first = await agent.api.app.bsky.graph.getListMutes( { limit: 1 }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyGraphGetListMutes, + ), + }, ) const second = await agent.api.app.bsky.graph.getListMutes( { cursor: first.data.cursor }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyGraphGetListMutes, + ), + }, ) const combined = [...first.data.lists, ...second.data.lists] expect(combined).toEqual(full.data.lists) @@ -321,13 +381,21 @@ describe('bsky views with mutes from mute lists', () => { }, { encoding: 'application/json', - headers: await network.serviceHeaders(dan), + headers: await network.serviceHeaders( + dan, + ids.AppBskyGraphUnmuteActorList, + ), }, ) const res = await agent.api.app.bsky.graph.getListMutes( {}, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders( + dan, + ids.AppBskyGraphGetListMutes, + ), + }, ) expect(res.data.lists.length).toBe(1) }) @@ -353,7 +421,7 @@ describe('bsky views with mutes from mute lists', () => { const got = await agent.api.app.bsky.graph.getList( { list: listUri }, - { headers: await network.serviceHeaders(alice) }, + { headers: await network.serviceHeaders(alice, ids.AppBskyGraphGetList) }, ) expect(got.data.list.name).toBe('updated alice mutes') expect(got.data.list.description).toBe('new descript') @@ -372,7 +440,7 @@ describe('bsky views with mutes from mute lists', () => { await network.processAll() const res = await agent.api.app.bsky.feed.getPosts( { uris: [postRef.ref.uriStr] }, - { headers: await network.serviceHeaders(alice) }, + { headers: await network.serviceHeaders(alice, ids.AppBskyFeedGetPosts) }, ) expect(res.data.posts.length).toBe(1) expect(forSnapshot(res.data.posts[0])).toMatchSnapshot() @@ -397,7 +465,9 @@ describe('bsky views with mutes from mute lists', () => { const res = await agent.api.app.bsky.feed.getTimeline( { limit: 100 }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders(dan, ids.AppBskyFeedGetTimeline), + }, ) expect( res.data.feed.some((post) => [bob, carol].includes(post.post.author.did)), @@ -417,7 +487,9 @@ describe('bsky views with mutes from mute lists', () => { const res = await agent.api.app.bsky.feed.getTimeline( { limit: 100 }, - { headers: await network.serviceHeaders(dan) }, + { + headers: await network.serviceHeaders(dan, ids.AppBskyFeedGetTimeline), + }, ) expect( res.data.feed.some((post) => [bob, carol].includes(post.post.author.did)), diff --git a/packages/bsky/tests/views/mutes.test.ts b/packages/bsky/tests/views/mutes.test.ts index 49219dc1a46..5536a115d68 100644 --- a/packages/bsky/tests/views/mutes.test.ts +++ b/packages/bsky/tests/views/mutes.test.ts @@ -6,6 +6,7 @@ import { usersBulkSeed, } from '@atproto/dev-env' import { forSnapshot, paginateAll } from '../_util' +import { ids } from '../../src/lexicon/lexicons' describe('mute views', () => { let network: TestNetwork @@ -45,7 +46,10 @@ describe('mute views', () => { await agent.api.app.bsky.graph.muteActor( { actor: did }, { - headers: await network.serviceHeaders(alice), + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphMuteActor, + ), encoding: 'application/json', }, ) @@ -59,7 +63,12 @@ describe('mute views', () => { 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: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(res.data.thread)).toMatchSnapshot() }) @@ -71,7 +80,12 @@ describe('mute views', () => { const res = await agent.api.app.bsky.feed.getAuthorFeed( { actor: dan }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) expect( res.data.feed.some((post) => [bob, carol].includes(post.post.author.did)), @@ -81,7 +95,12 @@ describe('mute views', () => { it('removes content from muted users on getTimeline', async () => { const res = await agent.api.app.bsky.feed.getTimeline( { limit: 100 }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetTimeline, + ), + }, ) expect( res.data.feed.some((post) => [bob, carol].includes(post.post.author.did)), @@ -95,7 +114,12 @@ describe('mute views', () => { await sc.addToList(alice, dan, listRef) const res = await agent.api.app.bsky.feed.getListFeed( { list: listRef.uriStr }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetListFeed, + ), + }, ) expect( res.data.feed.some((post) => [bob, carol].includes(post.post.author.did)), @@ -105,7 +129,12 @@ describe('mute views', () => { it('returns mute status on getProfile', async () => { const res = await agent.api.app.bsky.actor.getProfile( { actor: bob }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyActorGetProfile, + ), + }, ) expect(res.data.viewer?.muted).toBe(true) }) @@ -113,7 +142,12 @@ describe('mute views', () => { it('returns mute status on getProfiles', async () => { const res = await agent.api.app.bsky.actor.getProfiles( { actors: [bob, carol, dan] }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyActorGetProfiles, + ), + }, ) expect(res.data.profiles[0].viewer?.muted).toBe(true) expect(res.data.profiles[1].viewer?.muted).toBe(true) @@ -125,7 +159,12 @@ describe('mute views', () => { { limit: 100, }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationListNotifications, + ), + }, ) expect( res.data.notifications.some((notif) => @@ -145,7 +184,12 @@ describe('mute views', () => { { limit: 100, }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyActorGetSuggestions, + ), + }, ) for (const actor of res.data.actors) { if (mutes.includes(actor.did) || mutes.includes(actor.handle)) { @@ -159,7 +203,9 @@ describe('mute views', () => { it('fetches mutes for the logged-in user.', async () => { const { data: view } = await agent.api.app.bsky.graph.getMutes( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders(alice, ids.AppBskyGraphGetMutes), + }, ) expect(forSnapshot(view.mutes)).toMatchSnapshot() }) @@ -169,7 +215,12 @@ describe('mute views', () => { const paginator = async (cursor?: string) => { const { data: view } = await agent.api.app.bsky.graph.getMutes( { cursor, limit: 2 }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphGetMutes, + ), + }, ) return view } @@ -181,7 +232,9 @@ describe('mute views', () => { const full = await agent.api.app.bsky.graph.getMutes( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders(alice, ids.AppBskyGraphGetMutes), + }, ) expect(full.data.mutes.length).toEqual(8) @@ -191,7 +244,9 @@ describe('mute views', () => { it('removes mute.', async () => { const { data: initial } = await agent.api.app.bsky.graph.getMutes( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders(alice, ids.AppBskyGraphGetMutes), + }, ) expect(initial.mutes.length).toEqual(8) expect(initial.mutes.map((m) => m.handle)).toContain('elta48.test') @@ -199,14 +254,19 @@ describe('mute views', () => { await agent.api.app.bsky.graph.unmuteActor( { actor: sc.dids['elta48.test'] }, { - headers: await network.serviceHeaders(alice), + headers: await network.serviceHeaders( + alice, + ids.AppBskyGraphUnmuteActor, + ), encoding: 'application/json', }, ) const { data: final } = await agent.api.app.bsky.graph.getMutes( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders(alice, ids.AppBskyGraphGetMutes), + }, ) expect(final.mutes.length).toEqual(7) expect(final.mutes.map((m) => m.handle)).not.toContain('elta48.test') @@ -214,7 +274,7 @@ describe('mute views', () => { await agent.api.app.bsky.graph.muteActor( { actor: sc.dids['elta48.test'] }, { - headers: await network.serviceHeaders(alice), + headers: await network.serviceHeaders(alice, ids.AppBskyGraphMuteActor), encoding: 'application/json', }, ) @@ -224,7 +284,7 @@ describe('mute views', () => { const promise = agent.api.app.bsky.graph.muteActor( { actor: alice }, { - headers: await network.serviceHeaders(alice), + headers: await network.serviceHeaders(alice, ids.AppBskyGraphMuteActor), encoding: 'application/json', }, ) diff --git a/packages/bsky/tests/views/notifications.test.ts b/packages/bsky/tests/views/notifications.test.ts index 511941eb14a..560916fd687 100644 --- a/packages/bsky/tests/views/notifications.test.ts +++ b/packages/bsky/tests/views/notifications.test.ts @@ -2,6 +2,7 @@ import { AtpAgent } from '@atproto/api' import { TestNetwork, SeedClient, basicSeed } from '@atproto/dev-env' import { forSnapshot, paginateAll } from '../_util' import { Notification } from '../../src/lexicon/types/app/bsky/notification/listNotifications' +import { ids } from '../../src/lexicon/lexicons' describe('notification views', () => { let network: TestNetwork @@ -48,14 +49,24 @@ describe('notification views', () => { const notifCountAlice = await agent.api.app.bsky.notification.getUnreadCount( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationGetUnreadCount, + ), + }, ) expect(notifCountAlice.data.count).toBe(12) const notifCountBob = await agent.api.app.bsky.notification.getUnreadCount( {}, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyNotificationGetUnreadCount, + ), + }, ) expect(notifCountBob.data.count).toBeGreaterThanOrEqual(3) @@ -75,14 +86,24 @@ describe('notification views', () => { const notifCountAlice = await agent.api.app.bsky.notification.getUnreadCount( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationGetUnreadCount, + ), + }, ) expect(notifCountAlice.data.count).toBe(13) const notifCountBob = await agent.api.app.bsky.notification.getUnreadCount( {}, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyNotificationGetUnreadCount, + ), + }, ) expect(notifCountBob.data.count).toBeGreaterThanOrEqual(4) @@ -97,7 +118,12 @@ describe('notification views', () => { const notifsAlice = await agent.api.app.bsky.notification.listNotifications( {}, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyNotificationListNotifications, + ), + }, ) const hasNotif = notifsAlice.data.notifications.some( (notif) => notif.uri === second.ref.uriStr, @@ -114,7 +140,12 @@ describe('notification views', () => { // Dan was quoted by alice const notifsDan = await agent.api.app.bsky.notification.listNotifications( {}, - { headers: await network.serviceHeaders(sc.dids.dan) }, + { + headers: await network.serviceHeaders( + sc.dids.dan, + ids.AppBskyNotificationListNotifications, + ), + }, ) expect(forSnapshot(sort(notifsDan.data.notifications))).toMatchSnapshot() }) @@ -122,7 +153,12 @@ describe('notification views', () => { it('fetches notifications without a last-seen', async () => { const notifRes = await agent.api.app.bsky.notification.listNotifications( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationListNotifications, + ), + }, ) const notifs = notifRes.data.notifications @@ -140,7 +176,12 @@ describe('notification views', () => { const paginator = async (cursor?: string) => { const res = await agent.api.app.bsky.notification.listNotifications( { cursor, limit: 6 }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationListNotifications, + ), + }, ) return res.data } @@ -152,7 +193,12 @@ describe('notification views', () => { const full = await agent.api.app.bsky.notification.listNotifications( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationListNotifications, + ), + }, ) expect(full.data.notifications.length).toEqual(13) @@ -162,26 +208,44 @@ describe('notification views', () => { it('fetches notification count with a last-seen', async () => { const full = await agent.api.app.bsky.notification.listNotifications( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationListNotifications, + ), + }, ) const seenAt = full.data.notifications[3].indexedAt await agent.api.app.bsky.notification.updateSeen( { seenAt }, { - headers: await network.serviceHeaders(alice), + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationUpdateSeen, + ), encoding: 'application/json', }, ) const full2 = await agent.api.app.bsky.notification.listNotifications( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationListNotifications, + ), + }, ) expect(full2.data.notifications.length).toBe(full.data.notifications.length) expect(full2.data.seenAt).toEqual(seenAt) const notifCount = await agent.api.app.bsky.notification.getUnreadCount( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationGetUnreadCount, + ), + }, ) expect(notifCount.data.count).toBe( @@ -193,7 +257,10 @@ describe('notification views', () => { await agent.api.app.bsky.notification.updateSeen( { seenAt: new Date(0).toISOString() }, { - headers: await network.serviceHeaders(alice), + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationUpdateSeen, + ), encoding: 'application/json', }, ) @@ -202,19 +269,32 @@ describe('notification views', () => { it('fetches notifications with a last-seen', async () => { const full = await agent.api.app.bsky.notification.listNotifications( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationListNotifications, + ), + }, ) const seenAt = full.data.notifications[3].indexedAt await agent.api.app.bsky.notification.updateSeen( { seenAt }, { - headers: await network.serviceHeaders(alice), + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationUpdateSeen, + ), encoding: 'application/json', }, ) const notifRes = await agent.api.app.bsky.notification.listNotifications( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationListNotifications, + ), + }, ) const notifs = notifRes.data.notifications @@ -226,7 +306,10 @@ describe('notification views', () => { await agent.api.app.bsky.notification.updateSeen( { seenAt: new Date(0).toISOString() }, { - headers: await network.serviceHeaders(alice), + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationUpdateSeen, + ), encoding: 'application/json', }, ) @@ -245,11 +328,21 @@ describe('notification views', () => { const notifRes = await agent.api.app.bsky.notification.listNotifications( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationListNotifications, + ), + }, ) const notifCount = await agent.api.app.bsky.notification.getUnreadCount( {}, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationGetUnreadCount, + ), + }, ) const notifs = sort(notifRes.data.notifications) @@ -270,7 +363,12 @@ describe('notification views', () => { it('fetches notifications with explicit priority', async () => { const priority = await agent.api.app.bsky.notification.listNotifications( { priority: true }, - { headers: await network.serviceHeaders(sc.dids.carol) }, + { + headers: await network.serviceHeaders( + sc.dids.carol, + ids.AppBskyNotificationListNotifications, + ), + }, ) // only notifs from follow (alice) expect( @@ -281,7 +379,12 @@ describe('notification views', () => { expect(forSnapshot(priority.data)).toMatchSnapshot() const noPriority = await agent.api.app.bsky.notification.listNotifications( { priority: false }, - { headers: await network.serviceHeaders(sc.dids.carol) }, + { + headers: await network.serviceHeaders( + sc.dids.carol, + ids.AppBskyNotificationListNotifications, + ), + }, ) expect(forSnapshot(noPriority.data)).toMatchSnapshot() }) @@ -291,13 +394,21 @@ describe('notification views', () => { { priority: true }, { encoding: 'application/json', - headers: await network.serviceHeaders(sc.dids.carol), + headers: await network.serviceHeaders( + sc.dids.carol, + ids.AppBskyNotificationPutPreferences, + ), }, ) await network.processAll() const notifs = await agent.api.app.bsky.notification.listNotifications( {}, - { headers: await network.serviceHeaders(sc.dids.carol) }, + { + headers: await network.serviceHeaders( + sc.dids.carol, + ids.AppBskyNotificationListNotifications, + ), + }, ) // only notifs from follow (alice) expect( @@ -312,7 +423,12 @@ describe('notification views', () => { const { data: notifs } = await agent.api.app.bsky.notification.listNotifications( { cursor: '90210::bafycid' }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyNotificationListNotifications, + ), + }, ) expect(notifs).toMatchObject({ notifications: [] }) }) diff --git a/packages/bsky/tests/views/posts.test.ts b/packages/bsky/tests/views/posts.test.ts index 8f0fb7c4103..83614c74b55 100644 --- a/packages/bsky/tests/views/posts.test.ts +++ b/packages/bsky/tests/views/posts.test.ts @@ -1,6 +1,7 @@ import { AppBskyFeedPost, AtpAgent } from '@atproto/api' import { TestNetwork, SeedClient, basicSeed } from '@atproto/dev-env' import { forSnapshot, stripViewerFromPost } from '../_util' +import { ids } from '../../src/lexicon/lexicons' describe('pds posts views', () => { let network: TestNetwork @@ -34,7 +35,12 @@ describe('pds posts views', () => { ] const posts = await agent.api.app.bsky.feed.getPosts( { uris }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetPosts, + ), + }, ) expect(posts.data.posts.length).toBe(uris.length) @@ -53,7 +59,12 @@ describe('pds posts views', () => { const authed = await agent.api.app.bsky.feed.getPosts( { uris }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetPosts, + ), + }, ) const unauthed = await agent.api.app.bsky.feed.getPosts({ uris, diff --git a/packages/bsky/tests/views/profile.test.ts b/packages/bsky/tests/views/profile.test.ts index b65cda53d75..fb0923e9143 100644 --- a/packages/bsky/tests/views/profile.test.ts +++ b/packages/bsky/tests/views/profile.test.ts @@ -38,7 +38,12 @@ describe('pds profile views', () => { it('fetches own profile', async () => { const aliceForAlice = await agent.api.app.bsky.actor.getProfile( { actor: alice }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyActorGetProfile, + ), + }, ) expect(forSnapshot(aliceForAlice.data)).toMatchSnapshot() @@ -47,7 +52,9 @@ describe('pds profile views', () => { it('reflects self-labels', async () => { const aliceForBob = await agent.api.app.bsky.actor.getProfile( { actor: alice }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders(bob, ids.AppBskyActorGetProfile), + }, ) const labels = aliceForBob.data.labels @@ -61,7 +68,9 @@ describe('pds profile views', () => { it("fetches other's profile, with a follow", async () => { const aliceForBob = await agent.api.app.bsky.actor.getProfile( { actor: alice }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders(bob, ids.AppBskyActorGetProfile), + }, ) expect(forSnapshot(aliceForBob.data)).toMatchSnapshot() @@ -70,7 +79,9 @@ describe('pds profile views', () => { it("fetches other's profile, without a follow", async () => { const danForBob = await agent.api.app.bsky.actor.getProfile( { actor: dan }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders(bob, ids.AppBskyActorGetProfile), + }, ) expect(forSnapshot(danForBob.data)).toMatchSnapshot() @@ -90,7 +101,9 @@ describe('pds profile views', () => { 'missing.test', ], }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders(bob, ids.AppBskyActorGetProfiles), + }, ) expect(profiles.map((p) => p.handle)).toEqual([ @@ -135,7 +148,12 @@ describe('pds profile views', () => { const aliceForAlice = await agent.api.app.bsky.actor.getProfile( { actor: alice }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyActorGetProfile, + ), + }, ) expect(forSnapshot(aliceForAlice.data)).toMatchSnapshot() @@ -145,13 +163,15 @@ describe('pds profile views', () => { const byDid = await agent.api.app.bsky.actor.getProfile( { actor: alice }, { - headers: await network.serviceHeaders(bob), + headers: await network.serviceHeaders(bob, ids.AppBskyActorGetProfile), }, ) const byHandle = await agent.api.app.bsky.actor.getProfile( { actor: sc.accounts[alice].handle }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders(bob, ids.AppBskyActorGetProfile), + }, ) expect(byHandle.data).toEqual(byDid.data) @@ -160,7 +180,9 @@ describe('pds profile views', () => { it('fetches profile unauthed', async () => { const { data: authed } = await agent.api.app.bsky.actor.getProfile( { actor: alice }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders(bob, ids.AppBskyActorGetProfile), + }, ) const { data: unauthed } = await agent.api.app.bsky.actor.getProfile({ actor: alice, @@ -173,7 +195,9 @@ describe('pds profile views', () => { { actors: [alice, 'bob.test', 'missing.test'], }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders(bob, ids.AppBskyActorGetProfiles), + }, ) const { data: unauthed } = await agent.api.app.bsky.actor.getProfiles({ actors: [alice, 'bob.test', 'missing.test'], @@ -188,7 +212,9 @@ describe('pds profile views', () => { }) const promise = agent.api.app.bsky.actor.getProfile( { actor: alice }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders(bob, ids.AppBskyActorGetProfile), + }, ) await expect(promise).rejects.toThrow('Account has been suspended') diff --git a/packages/bsky/tests/views/reposts.test.ts b/packages/bsky/tests/views/reposts.test.ts index 0ae2eea8773..ad03455e78c 100644 --- a/packages/bsky/tests/views/reposts.test.ts +++ b/packages/bsky/tests/views/reposts.test.ts @@ -1,6 +1,7 @@ import { AtpAgent } from '@atproto/api' import { TestNetwork, SeedClient, repostsSeed } from '@atproto/dev-env' import { forSnapshot, paginateAll, stripViewer } from '../_util' +import { ids } from '../../src/lexicon/lexicons' describe('pds repost views', () => { let network: TestNetwork @@ -30,7 +31,12 @@ describe('pds repost views', () => { 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: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetRepostedBy, + ), + }, ) expect(view.data.uri).toEqual(sc.posts[sc.dids.alice][2].ref.uriStr) expect(forSnapshot(view.data.repostedBy)).toMatchSnapshot() @@ -39,7 +45,12 @@ describe('pds repost views', () => { 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: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetRepostedBy, + ), + }, ) expect(view.data.uri).toEqual(sc.replies[sc.dids.bob][0].ref.uriStr) expect(forSnapshot(view.data.repostedBy)).toMatchSnapshot() @@ -54,7 +65,12 @@ describe('pds repost views', () => { cursor, limit: 2, }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetRepostedBy, + ), + }, ) return res.data } @@ -66,7 +82,12 @@ describe('pds repost views', () => { const full = await agent.api.app.bsky.feed.getRepostedBy( { uri: sc.posts[alice][2].ref.uriStr }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetRepostedBy, + ), + }, ) expect(full.data.repostedBy.length).toEqual(4) @@ -76,7 +97,12 @@ describe('pds repost views', () => { it('fetches reposted-by unauthed', async () => { const { data: authed } = await agent.api.app.bsky.feed.getRepostedBy( { uri: sc.posts[alice][2].ref.uriStr }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetRepostedBy, + ), + }, ) const { data: unauthed } = await agent.api.app.bsky.feed.getRepostedBy({ uri: sc.posts[alice][2].ref.uriStr, diff --git a/packages/bsky/tests/views/starter-packs.test.ts b/packages/bsky/tests/views/starter-packs.test.ts index 7a78a2d6c55..9e844b7d155 100644 --- a/packages/bsky/tests/views/starter-packs.test.ts +++ b/packages/bsky/tests/views/starter-packs.test.ts @@ -3,6 +3,7 @@ import { TestNetwork, SeedClient, RecordRef, basicSeed } from '@atproto/dev-env' import { isRecord as isProfile } from '../../src/lexicon/types/app/bsky/actor/profile' import { forSnapshot } from '../_util' import assert from 'assert' +import { ids } from '../../src/lexicon/lexicons' describe('starter packs', () => { let network: TestNetwork @@ -126,7 +127,12 @@ describe('starter packs', () => { data: { notifications }, } = await agent.api.app.bsky.notification.listNotifications( { limit: 3 }, // three most recent - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyNotificationListNotifications, + ), + }, ) expect(notifications).toHaveLength(3) notifications.forEach((notif) => { @@ -144,7 +150,12 @@ describe('starter packs', () => { { starterPack: sp3.uriStr, }, - { headers: await network.serviceHeaders(sc.dids.frankie) }, + { + headers: await network.serviceHeaders( + sc.dids.frankie, + ids.AppBskyGraphGetStarterPack, + ), + }, ) expect(view.data.starterPack.listItemsSample?.length).toBe(2) expect(forSnapshot(view.data.starterPack.listItemsSample)).toMatchSnapshot() @@ -155,7 +166,12 @@ describe('starter packs', () => { { starterPack: sp3.uriStr, }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyGraphGetStarterPack, + ), + }, ) expect(view.data.starterPack.listItemsSample?.length).toBe(2) expect(forSnapshot(view.data.starterPack.listItemsSample)).toMatchSnapshot() @@ -171,10 +187,13 @@ describe('starter packs', () => { it('does include users with creator block relationship in list sample for creator', async () => { const view = await agent.api.app.bsky.graph.getStarterPack( + { starterPack: sp3.uriStr }, { - starterPack: sp3.uriStr, + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyGraphGetStarterPack, + ), }, - { headers: await network.serviceHeaders(sc.dids.alice) }, ) expect(view.data.starterPack.listItemsSample?.length).toBe(3) expect(forSnapshot(view.data.starterPack.listItemsSample)).toMatchSnapshot() diff --git a/packages/bsky/tests/views/suggested-follows.test.ts b/packages/bsky/tests/views/suggested-follows.test.ts index 27e6c09864e..eb647bd2343 100644 --- a/packages/bsky/tests/views/suggested-follows.test.ts +++ b/packages/bsky/tests/views/suggested-follows.test.ts @@ -1,5 +1,6 @@ import { AtpAgent, AtUri } from '@atproto/api' import { TestNetwork, SeedClient, likesSeed } from '@atproto/dev-env' +import { ids } from '../../src/lexicon/lexicons' describe('suggested follows', () => { let network: TestNetwork @@ -40,7 +41,12 @@ describe('suggested follows', () => { { actor: sc.dids.alice, }, - { headers: await network.serviceHeaders(sc.dids.carol) }, + { + headers: await network.serviceHeaders( + sc.dids.carol, + ids.AppBskyGraphGetSuggestedFollowsByActor, + ), + }, ) expect(result.data.suggestions.length).toBe(4) // backfilled with 2 NPCs @@ -56,7 +62,12 @@ describe('suggested follows', () => { { actor: sc.dids.alice, }, - { headers: await network.serviceHeaders(sc.dids.fred) }, + { + headers: await network.serviceHeaders( + sc.dids.fred, + ids.AppBskyGraphGetSuggestedFollowsByActor, + ), + }, ) expect(result.data.suggestions.length).toBe(4) // backfilled with 2 NPCs @@ -76,7 +87,12 @@ describe('suggested follows', () => { { actor: sc.dids.alice, }, - { headers: await network.serviceHeaders(sc.dids.carol) }, + { + headers: await network.serviceHeaders( + sc.dids.carol, + ids.AppBskyGraphGetSuggestedFollowsByActor, + ), + }, ) expect( @@ -101,7 +117,12 @@ describe('suggested follows', () => { { actor: sc.dids.alice, }, - { headers: await network.serviceHeaders(sc.dids.carol) }, + { + headers: await network.serviceHeaders( + sc.dids.carol, + ids.AppBskyGraphGetSuggestedFollowsByActor, + ), + }, ) expect( @@ -126,7 +147,12 @@ describe('suggested follows', () => { { actor: sc.dids.alice, }, - { headers: await network.serviceHeaders(sc.dids.carol) }, + { + headers: await network.serviceHeaders( + sc.dids.carol, + ids.AppBskyGraphGetSuggestedFollowsByActor, + ), + }, ) expect( diff --git a/packages/bsky/tests/views/suggestions.test.ts b/packages/bsky/tests/views/suggestions.test.ts index 050c2e01f67..c7628fda04d 100644 --- a/packages/bsky/tests/views/suggestions.test.ts +++ b/packages/bsky/tests/views/suggestions.test.ts @@ -1,6 +1,7 @@ import { AtpAgent } from '@atproto/api' import { TestNetwork, SeedClient, basicSeed } from '@atproto/dev-env' import { stripViewer } from '../_util' +import { ids } from '../../src/lexicon/lexicons' describe('pds user search views', () => { let network: TestNetwork @@ -36,7 +37,12 @@ describe('pds user search views', () => { it('actor suggestion gives users', async () => { const result = await agent.api.app.bsky.actor.getSuggestions( {}, - { headers: await network.serviceHeaders(sc.dids.carol) }, + { + headers: await network.serviceHeaders( + sc.dids.carol, + ids.AppBskyActorGetSuggestions, + ), + }, ) // does not include carol, because she is requesting @@ -50,7 +56,12 @@ describe('pds user search views', () => { it('does not suggest followed users', async () => { const result = await agent.api.app.bsky.actor.getSuggestions( {}, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyActorGetSuggestions, + ), + }, ) // alice follows everyone @@ -60,21 +71,36 @@ describe('pds user search views', () => { it('paginates', async () => { const result1 = await agent.api.app.bsky.actor.getSuggestions( { limit: 2 }, - { headers: await network.serviceHeaders(sc.dids.carol) }, + { + headers: await network.serviceHeaders( + sc.dids.carol, + ids.AppBskyActorGetSuggestions, + ), + }, ) expect(result1.data.actors.length).toBe(1) expect(result1.data.actors[0].handle).toEqual('bob.test') const result2 = await agent.api.app.bsky.actor.getSuggestions( { limit: 2, cursor: result1.data.cursor }, - { headers: await network.serviceHeaders(sc.dids.carol) }, + { + headers: await network.serviceHeaders( + sc.dids.carol, + ids.AppBskyActorGetSuggestions, + ), + }, ) expect(result2.data.actors.length).toBe(1) expect(result2.data.actors[0].handle).toEqual('dan.test') const result3 = await agent.api.app.bsky.actor.getSuggestions( { limit: 2, cursor: result2.data.cursor }, - { headers: await network.serviceHeaders(sc.dids.carol) }, + { + headers: await network.serviceHeaders( + sc.dids.carol, + ids.AppBskyActorGetSuggestions, + ), + }, ) expect(result3.data.actors.length).toBe(0) expect(result3.data.cursor).toBeUndefined() @@ -83,7 +109,12 @@ describe('pds user search views', () => { it('fetches suggestions unauthed', async () => { const { data: authed } = await agent.api.app.bsky.actor.getSuggestions( {}, - { headers: await network.serviceHeaders(sc.dids.carol) }, + { + headers: await network.serviceHeaders( + sc.dids.carol, + ids.AppBskyActorGetSuggestions, + ), + }, ) const { data: unauthed } = await agent.api.app.bsky.actor.getSuggestions({}) const omitViewerFollows = ({ did }) => { diff --git a/packages/bsky/tests/views/thread.test.ts b/packages/bsky/tests/views/thread.test.ts index f68898ea5bc..6cf095d795d 100644 --- a/packages/bsky/tests/views/thread.test.ts +++ b/packages/bsky/tests/views/thread.test.ts @@ -5,6 +5,7 @@ import { forSnapshot, stripViewerFromThread, } from '../_util' +import { ids } from '../../src/lexicon/lexicons' describe('pds thread views', () => { let network: TestNetwork @@ -41,7 +42,12 @@ describe('pds thread views', () => { it('fetches deep post thread', async () => { const thread = await agent.api.app.bsky.feed.getPostThread( { uri: sc.posts[alice][1].ref.uriStr }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(thread.data.thread)).toMatchSnapshot() @@ -50,7 +56,12 @@ describe('pds thread views', () => { 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: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(thread.data.thread)).toMatchSnapshot() @@ -65,7 +76,12 @@ describe('pds thread views', () => { `at://${sc.accounts[alice].handle}`, ), }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(thread.data.thread)).toMatchSnapshot() @@ -74,7 +90,12 @@ describe('pds thread views', () => { it('fetches ancestors', async () => { const thread = await agent.api.app.bsky.feed.getPostThread( { depth: 1, uri: sc.replies[alice][0].ref.uriStr }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(thread.data.thread)).toMatchSnapshot() @@ -83,7 +104,12 @@ describe('pds thread views', () => { 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: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) await expect(promise).rejects.toThrow( @@ -94,7 +120,12 @@ describe('pds thread views', () => { it('fetches post thread unauthed', async () => { const { data: authed } = await agent.api.app.bsky.feed.getPostThread( { uri: sc.posts[alice][1].ref.uriStr }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) const { data: unauthed } = await agent.api.app.bsky.feed.getPostThread({ uri: sc.posts[alice][1].ref.uriStr, @@ -133,7 +164,12 @@ describe('pds thread views', () => { const thread1 = await agent.api.app.bsky.feed.getPostThread( { uri: sc.posts[alice][indexes.aliceRoot].ref.uriStr }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(thread1.data.thread)).toMatchSnapshot() @@ -142,13 +178,23 @@ describe('pds thread views', () => { const thread2 = await agent.api.app.bsky.feed.getPostThread( { uri: sc.posts[alice][indexes.aliceRoot].ref.uriStr }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(thread2.data.thread)).toMatchSnapshot() const thread3 = await agent.api.app.bsky.feed.getPostThread( { uri: sc.replies[alice][indexes.aliceReplyReply].ref.uriStr }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(thread3.data.thread)).toMatchSnapshot() }) @@ -179,7 +225,12 @@ describe('pds thread views', () => { const { data: goodReply1Thread } = await agent.api.app.bsky.feed.getPostThread( { uri: goodReply1.ref.uriStr }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assertIsThreadViewPost(goodReply1Thread.thread) assertIsThreadViewPost(goodReply1Thread.thread.parent) @@ -197,7 +248,12 @@ describe('pds thread views', () => { const { data: badReplyThread } = await agent.api.app.bsky.feed.getPostThread( { uri: badReply.ref.uriStr }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assertIsThreadViewPost(badReplyThread.thread) expect(badReplyThread.thread.parent).toBeUndefined() // is not goodReply1 @@ -206,7 +262,12 @@ describe('pds thread views', () => { it('reflects self-labels', async () => { const { data: thread } = await agent.api.app.bsky.feed.getPostThread( { uri: sc.posts[alice][0].ref.uriStr }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) assertIsThreadViewPost(thread.thread) @@ -236,7 +297,12 @@ describe('pds thread views', () => { // 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: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) await expect(promise).rejects.toThrow( @@ -257,7 +323,12 @@ describe('pds thread views', () => { // 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: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(thread.data.thread)).toMatchSnapshot() @@ -276,7 +347,12 @@ describe('pds thread views', () => { // 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: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(thread.data.thread)).toMatchSnapshot() @@ -295,7 +371,12 @@ describe('pds thread views', () => { const promise = agent.api.app.bsky.feed.getPostThread( { depth: 1, uri: postRef.uriStr }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) await expect(promise).rejects.toThrow( @@ -311,7 +392,12 @@ describe('pds thread views', () => { it('blocks ancestors by record', async () => { const threadPreTakedown = await agent.api.app.bsky.feed.getPostThread( { depth: 1, uri: sc.replies[alice][0].ref.uriStr }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) const parent = threadPreTakedown.data.thread.parent?.['post'] @@ -323,7 +409,12 @@ describe('pds thread views', () => { // 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: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(thread.data.thread)).toMatchSnapshot() @@ -337,7 +428,12 @@ describe('pds thread views', () => { it('blocks replies by record', async () => { const threadPreTakedown = await agent.api.app.bsky.feed.getPostThread( { uri: sc.posts[alice][1].ref.uriStr }, - { headers: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) const post1 = threadPreTakedown.data.thread.replies?.[0].post const post2 = threadPreTakedown.data.thread.replies?.[1].replies[0].post @@ -353,7 +449,12 @@ describe('pds thread views', () => { // 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: await network.serviceHeaders(bob) }, + { + headers: await network.serviceHeaders( + bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) expect(forSnapshot(thread.data.thread)).toMatchSnapshot() diff --git a/packages/bsky/tests/views/threadgating.test.ts b/packages/bsky/tests/views/threadgating.test.ts index 4d0027c5e35..02b332b6795 100644 --- a/packages/bsky/tests/views/threadgating.test.ts +++ b/packages/bsky/tests/views/threadgating.test.ts @@ -6,6 +6,7 @@ import { isThreadViewPost, } from '../../src/lexicon/types/app/bsky/feed/defs' import { forSnapshot } from '../_util' +import { ids } from '../../src/lexicon/lexicons' describe('views with thread gating', () => { let network: TestNetwork @@ -36,7 +37,7 @@ describe('views with thread gating', () => { ) => { const res = await agent.api.app.bsky.feed.getPosts( { uris: [uri] }, - { headers: await network.serviceHeaders(user) }, + { headers: await network.serviceHeaders(user, ids.AppBskyFeedGetPosts) }, ) expect(res.data.posts[0].viewer?.replyDisabled).toBe(blocked) } @@ -55,7 +56,12 @@ describe('views with thread gating', () => { data: { thread }, } = await agent.api.app.bsky.feed.getPostThread( { uri: post.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(thread)) expect(forSnapshot(thread.post.threadgate)).toMatchSnapshot() @@ -83,7 +89,12 @@ describe('views with thread gating', () => { data: { notifications }, } = await agent.api.app.bsky.notification.listNotifications( {}, - { headers: await network.serviceHeaders(sc.dids.carol) }, + { + headers: await network.serviceHeaders( + sc.dids.carol, + ids.AppBskyNotificationListNotifications, + ), + }, ) const notificationFromReply = notifications.find( (notif) => notif.uri === reply.ref.uriStr, @@ -137,7 +148,12 @@ describe('views with thread gating', () => { data: { thread: aliceThread }, } = await agent.api.app.bsky.feed.getPostThread( { uri: post.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(aliceThread)) expect(aliceThread.post.viewer?.replyDisabled).toBe(true) @@ -146,7 +162,12 @@ describe('views with thread gating', () => { data: { thread: danThread }, } = await agent.api.app.bsky.feed.getPostThread( { uri: post.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.dan) }, + { + headers: await network.serviceHeaders( + sc.dids.dan, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(danThread)) expect(forSnapshot(danThread.post.threadgate)).toMatchSnapshot() @@ -188,7 +209,12 @@ describe('views with thread gating', () => { data: { thread: danThread }, } = await agent.api.app.bsky.feed.getPostThread( { uri: post.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.dan) }, + { + headers: await network.serviceHeaders( + sc.dids.dan, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(danThread)) expect(danThread.post.viewer?.replyDisabled).toBe(true) @@ -197,7 +223,12 @@ describe('views with thread gating', () => { data: { thread: aliceThread }, } = await agent.api.app.bsky.feed.getPostThread( { uri: post.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(aliceThread)) expect(forSnapshot(aliceThread.post.threadgate)).toMatchSnapshot() @@ -280,7 +311,12 @@ describe('views with thread gating', () => { data: { thread: bobThread }, } = await agent.api.app.bsky.feed.getPostThread( { uri: post.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(bobThread)) expect(bobThread.post.viewer?.replyDisabled).toBe(true) @@ -289,7 +325,12 @@ describe('views with thread gating', () => { data: { thread: aliceThread }, } = await agent.api.app.bsky.feed.getPostThread( { uri: post.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(aliceThread)) expect(aliceThread.post.viewer?.replyDisabled).toBe(false) @@ -298,7 +339,12 @@ describe('views with thread gating', () => { data: { thread: danThread }, } = await agent.api.app.bsky.feed.getPostThread( { uri: post.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.dan) }, + { + headers: await network.serviceHeaders( + sc.dids.dan, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(danThread)) expect(forSnapshot(danThread.post.threadgate)).toMatchSnapshot() @@ -341,7 +387,12 @@ describe('views with thread gating', () => { data: { thread }, } = await agent.api.app.bsky.feed.getPostThread( { uri: post.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(thread)) expect(forSnapshot(thread.post.threadgate)).toMatchSnapshot() @@ -391,7 +442,12 @@ describe('views with thread gating', () => { data: { thread: bobThread }, } = await agent.api.app.bsky.feed.getPostThread( { uri: post.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.bob) }, + { + headers: await network.serviceHeaders( + sc.dids.bob, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(bobThread)) expect(bobThread.post.viewer?.replyDisabled).toBe(true) @@ -400,7 +456,12 @@ describe('views with thread gating', () => { data: { thread: aliceThread }, } = await agent.api.app.bsky.feed.getPostThread( { uri: post.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(aliceThread)) expect(aliceThread.post.viewer?.replyDisabled).toBe(false) @@ -409,7 +470,12 @@ describe('views with thread gating', () => { data: { thread: danThread }, } = await agent.api.app.bsky.feed.getPostThread( { uri: post.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.dan) }, + { + headers: await network.serviceHeaders( + sc.dids.dan, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(danThread)) expect(forSnapshot(danThread.post.threadgate)).toMatchSnapshot() @@ -443,7 +509,12 @@ describe('views with thread gating', () => { data: { thread }, } = await agent.api.app.bsky.feed.getPostThread( { uri: post.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(thread)) expect(forSnapshot(thread.post.threadgate)).toMatchSnapshot() @@ -497,7 +568,12 @@ describe('views with thread gating', () => { data: { thread: danThread }, } = await agent.api.app.bsky.feed.getPostThread( { uri: orphanedReply.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.dan) }, + { + headers: await network.serviceHeaders( + sc.dids.dan, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(danThread)) expect(danThread.post.viewer?.replyDisabled).toBe(true) @@ -506,7 +582,12 @@ describe('views with thread gating', () => { data: { thread: aliceThread }, } = await agent.api.app.bsky.feed.getPostThread( { uri: orphanedReply.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(aliceThread)) assert( @@ -541,7 +622,12 @@ describe('views with thread gating', () => { data: { thread }, } = await agent.api.app.bsky.feed.getPostThread( { uri: post.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.carol) }, + { + headers: await network.serviceHeaders( + sc.dids.carol, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(thread)) expect(forSnapshot(thread.post.threadgate)).toMatchSnapshot() @@ -580,7 +666,12 @@ describe('views with thread gating', () => { data: { thread }, } = await agent.api.app.bsky.feed.getPostThread( { uri: badReply.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(thread)) expect(thread.post.viewer?.replyDisabled).toBe(true) // nobody can reply to this, not even alice. @@ -593,7 +684,12 @@ describe('views with thread gating', () => { data: { feed }, } = await agent.api.app.bsky.feed.getAuthorFeed( { actor: sc.dids.dan }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetAuthorFeed, + ), + }, ) const [feedItem] = feed expect(feedItem.post.uri).toEqual(badReply.ref.uriStr) @@ -617,7 +713,12 @@ describe('views with thread gating', () => { data: { thread: threadA }, } = await agent.api.app.bsky.feed.getPostThread( { uri: postA.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(threadA)) expect(threadA.post.threadgate).toBeUndefined() @@ -628,7 +729,12 @@ describe('views with thread gating', () => { data: { thread: threadB }, } = await agent.api.app.bsky.feed.getPostThread( { uri: postB.ref.uriStr }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyFeedGetPostThread, + ), + }, ) assert(isThreadViewPost(threadB)) expect(threadB.post.threadgate).toBeUndefined() diff --git a/packages/bsky/tests/views/timeline.test.ts b/packages/bsky/tests/views/timeline.test.ts index 0be6162f029..53e6d2d2250 100644 --- a/packages/bsky/tests/views/timeline.test.ts +++ b/packages/bsky/tests/views/timeline.test.ts @@ -9,6 +9,7 @@ import { import { forSnapshot, getOriginator, paginateAll } from '../_util' import { FeedViewPost } from '../../src/lexicon/types/app/bsky/feed/defs' import { Database } from '../../src' +import { ids } from '../../src/lexicon/lexicons' const REVERSE_CHRON = 'reverse-chronological' @@ -68,7 +69,10 @@ describe('timeline views', () => { const aliceTL = await agent.api.app.bsky.feed.getTimeline( { algorithm: REVERSE_CHRON }, { - headers: await network.serviceHeaders(alice), + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetTimeline, + ), }, ) @@ -78,7 +82,7 @@ describe('timeline views', () => { const bobTL = await agent.api.app.bsky.feed.getTimeline( { algorithm: REVERSE_CHRON }, { - headers: await network.serviceHeaders(bob), + headers: await network.serviceHeaders(bob, ids.AppBskyFeedGetTimeline), }, ) @@ -88,7 +92,10 @@ describe('timeline views', () => { const carolTL = await agent.api.app.bsky.feed.getTimeline( { algorithm: REVERSE_CHRON }, { - headers: await network.serviceHeaders(carol), + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetTimeline, + ), }, ) @@ -98,7 +105,7 @@ describe('timeline views', () => { const danTL = await agent.api.app.bsky.feed.getTimeline( { algorithm: REVERSE_CHRON }, { - headers: await network.serviceHeaders(dan), + headers: await network.serviceHeaders(dan, ids.AppBskyFeedGetTimeline), }, ) @@ -110,13 +117,19 @@ describe('timeline views', () => { const defaultTL = await agent.api.app.bsky.feed.getTimeline( {}, { - headers: await network.serviceHeaders(alice), + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetTimeline, + ), }, ) const reverseChronologicalTL = await agent.api.app.bsky.feed.getTimeline( { algorithm: REVERSE_CHRON }, { - headers: await network.serviceHeaders(alice), + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetTimeline, + ), }, ) expect(defaultTL.data.feed).toEqual(reverseChronologicalTL.data.feed) @@ -131,7 +144,12 @@ describe('timeline views', () => { cursor, limit: 4, }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetTimeline, + ), + }, ) return res.data } @@ -145,7 +163,12 @@ describe('timeline views', () => { { algorithm: REVERSE_CHRON, }, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetTimeline, + ), + }, ) expect(full.data.feed.length).toEqual(7) @@ -155,11 +178,21 @@ describe('timeline views', () => { it('agrees what the first item is for limit=1 and other limits', async () => { const { data: timeline } = await agent.api.app.bsky.feed.getTimeline( { limit: 10 }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetTimeline, + ), + }, ) const { data: timelineLimit1 } = await agent.api.app.bsky.feed.getTimeline( { limit: 1 }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetTimeline, + ), + }, ) expect(timeline.feed.length).toBeGreaterThan(1) expect(timelineLimit1.feed.length).toEqual(1) @@ -169,7 +202,12 @@ describe('timeline views', () => { it('reflects self-labels', async () => { const carolTL = await agent.api.app.bsky.feed.getTimeline( {}, - { headers: await network.serviceHeaders(carol) }, + { + headers: await network.serviceHeaders( + carol, + ids.AppBskyFeedGetTimeline, + ), + }, ) const alicePost = carolTL.data.feed.find( @@ -201,7 +239,12 @@ describe('timeline views', () => { const aliceTL = await agent.api.app.bsky.feed.getTimeline( { algorithm: REVERSE_CHRON }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetTimeline, + ), + }, ) expect(forSnapshot(aliceTL.data.feed)).toMatchSnapshot() @@ -227,7 +270,12 @@ describe('timeline views', () => { const aliceTL = await agent.api.app.bsky.feed.getTimeline( { algorithm: REVERSE_CHRON }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetTimeline, + ), + }, ) expect(forSnapshot(aliceTL.data.feed)).toMatchSnapshot() @@ -245,7 +293,12 @@ describe('timeline views', () => { it('fails open on clearly bad cursor.', async () => { const { data: timeline } = await agent.api.app.bsky.feed.getTimeline( { cursor: '90210::bafycid' }, - { headers: await network.serviceHeaders(alice) }, + { + headers: await network.serviceHeaders( + alice, + ids.AppBskyFeedGetTimeline, + ), + }, ) expect(timeline).toEqual({ feed: [] }) }) diff --git a/packages/dev-env/src/moderator-client.ts b/packages/dev-env/src/moderator-client.ts index 81a024b4ae2..3c1a7798aa1 100644 --- a/packages/dev-env/src/moderator-client.ts +++ b/packages/dev-env/src/moderator-client.ts @@ -21,7 +21,10 @@ export class ModeratorClient { const result = await this.agent.tools.ozone.moderation.getEvent( { id }, { - headers: await this.ozone.modHeaders(role), + headers: await this.ozone.modHeaders( + 'tools.ozone.moderation.getEvent', + role, + ), }, ) return result.data @@ -31,7 +34,10 @@ export class ModeratorClient { const result = await this.agent.tools.ozone.moderation.queryStatuses( input, { - headers: await this.ozone.modHeaders(role), + headers: await this.ozone.modHeaders( + 'tools.ozone.moderation.queryStatuses', + role, + ), }, ) return result.data @@ -39,7 +45,10 @@ export class ModeratorClient { async queryEvents(input: QueryEventsParams, role?: ModLevel) { const result = await this.agent.tools.ozone.moderation.queryEvents(input, { - headers: await this.ozone.modHeaders(role), + headers: await this.ozone.modHeaders( + 'tools.ozone.moderation.queryEvents', + role, + ), }) return result.data } @@ -66,7 +75,10 @@ export class ModeratorClient { { event, subject, subjectBlobCids, createdBy, reason }, { encoding: 'application/json', - headers: await this.ozone.modHeaders(role), + headers: await this.ozone.modHeaders( + 'tools.ozone.moderation.emitEvent', + role, + ), }, ) return result.data @@ -93,7 +105,10 @@ export class ModeratorClient { }, { encoding: 'application/json', - headers: await this.ozone.modHeaders(role), + headers: await this.ozone.modHeaders( + 'tools.ozone.moderation.emitEvent', + role, + ), }, ) return result.data diff --git a/packages/dev-env/src/network.ts b/packages/dev-env/src/network.ts index 4477c5d270b..6eff9045e59 100644 --- a/packages/dev-env/src/network.ts +++ b/packages/dev-env/src/network.ts @@ -157,12 +157,12 @@ export class TestNetwork extends TestNetworkNoAppView { await this.bsky.sub.background.processAll() } - async serviceHeaders(did: string, aud?: string) { + async serviceHeaders(did: string, lxm: string, aud?: string) { const keypair = await this.pds.ctx.actorStore.keypair(did) const jwt = await createServiceJwt({ iss: did, aud: aud ?? this.bsky.ctx.cfg.serverDid, - lxm: null, + lxm, keypair, }) return { authorization: `Bearer ${jwt}` } diff --git a/packages/dev-env/src/ozone.ts b/packages/dev-env/src/ozone.ts index 56a56775be2..fabe73f5a51 100644 --- a/packages/dev-env/src/ozone.ts +++ b/packages/dev-env/src/ozone.ts @@ -143,7 +143,10 @@ export class TestOzone { this.ctx.cfg.access.triage.push(did) } - async modHeaders(role: 'admin' | 'moderator' | 'triage' = 'moderator') { + async modHeaders( + lxm: string, + role: 'admin' | 'moderator' | 'triage' = 'moderator', + ) { const account = role === 'admin' ? this.adminAccnt @@ -153,7 +156,7 @@ export class TestOzone { const jwt = await createServiceJwt({ iss: account.did, aud: this.ctx.cfg.service.did, - lxm: null, + lxm, keypair: account.key, }) return { authorization: `Bearer ${jwt}` } diff --git a/packages/ozone/src/api/chat/getActorMetadata.ts b/packages/ozone/src/api/chat/getActorMetadata.ts index ce3e13a991f..6bb7d86de6a 100644 --- a/packages/ozone/src/api/chat/getActorMetadata.ts +++ b/packages/ozone/src/api/chat/getActorMetadata.ts @@ -1,6 +1,7 @@ import { Server } from '../../lexicon' import AppContext from '../../context' import { InvalidRequestError } from '@atproto/xrpc-server' +import { ids } from '../../lexicon/lexicons' export default function (server: Server, ctx: AppContext) { server.chat.bsky.moderation.getActorMetadata({ @@ -11,7 +12,7 @@ export default function (server: Server, ctx: AppContext) { } const res = await ctx.chatAgent.api.chat.bsky.moderation.getActorMetadata( params, - await ctx.chatAuth(), + await ctx.chatAuth(ids.ChatBskyModerationGetActorMetadata), ) return { encoding: 'application/json', diff --git a/packages/ozone/src/api/chat/getMessageContext.ts b/packages/ozone/src/api/chat/getMessageContext.ts index e5a0c8ffc15..d19e8ebfc3f 100644 --- a/packages/ozone/src/api/chat/getMessageContext.ts +++ b/packages/ozone/src/api/chat/getMessageContext.ts @@ -1,6 +1,7 @@ import { Server } from '../../lexicon' import AppContext from '../../context' import { InvalidRequestError } from '@atproto/xrpc-server' +import { ids } from '../../lexicon/lexicons' export default function (server: Server, ctx: AppContext) { server.chat.bsky.moderation.getMessageContext({ @@ -28,7 +29,7 @@ export default function (server: Server, ctx: AppContext) { const res = await ctx.chatAgent.api.chat.bsky.moderation.getMessageContext( { ...params, before, after }, - await ctx.chatAuth(), + await ctx.chatAuth(ids.ChatBskyModerationGetMessageContext), ) return { encoding: 'application/json', diff --git a/packages/ozone/src/api/moderation/searchRepos.ts b/packages/ozone/src/api/moderation/searchRepos.ts index f29e1abc49c..50a6062db08 100644 --- a/packages/ozone/src/api/moderation/searchRepos.ts +++ b/packages/ozone/src/api/moderation/searchRepos.ts @@ -1,6 +1,7 @@ import { Server } from '../../lexicon' import AppContext from '../../context' import { mapDefined } from '@atproto/common' +import { ids } from '../../lexicon/lexicons' export default function (server: Server, ctx: AppContext) { server.tools.ozone.moderation.searchRepos({ @@ -25,7 +26,7 @@ export default function (server: Server, ctx: AppContext) { const res = await ctx.appviewAgent.api.app.bsky.actor.searchActors( params, - await ctx.appviewAuth(), + await ctx.appviewAuth(ids.AppBskyActorSearchActors), ) const repoMap = await modService.views.repos( res.data.actors.map((a) => a.did), diff --git a/packages/ozone/src/api/proxied.ts b/packages/ozone/src/api/proxied.ts index c4c4b355471..2b55a0c499a 100644 --- a/packages/ozone/src/api/proxied.ts +++ b/packages/ozone/src/api/proxied.ts @@ -1,5 +1,6 @@ import { Server } from '../lexicon' import AppContext from '../context' +import { ids } from '../lexicon/lexicons' export default function (server: Server, ctx: AppContext) { server.app.bsky.actor.getProfile({ @@ -7,7 +8,7 @@ export default function (server: Server, ctx: AppContext) { handler: async (request) => { const res = await ctx.appviewAgent.api.app.bsky.actor.getProfile( request.params, - await ctx.appviewAuth(), + await ctx.appviewAuth(ids.AppBskyActorGetProfile), ) return { encoding: 'application/json', @@ -21,7 +22,7 @@ export default function (server: Server, ctx: AppContext) { handler: async (request) => { const res = await ctx.appviewAgent.api.app.bsky.actor.getProfiles( request.params, - await ctx.appviewAuth(), + await ctx.appviewAuth(ids.AppBskyActorGetProfiles), ) return { encoding: 'application/json', @@ -35,7 +36,7 @@ export default function (server: Server, ctx: AppContext) { handler: async (request) => { const res = await ctx.appviewAgent.api.app.bsky.feed.getAuthorFeed( request.params, - await ctx.appviewAuth(), + await ctx.appviewAuth(ids.AppBskyFeedGetAuthorFeed), ) return { encoding: 'application/json', @@ -49,7 +50,7 @@ export default function (server: Server, ctx: AppContext) { handler: async (request) => { const res = await ctx.appviewAgent.api.app.bsky.feed.searchPosts( request.params, - await ctx.appviewAuth(), + await ctx.appviewAuth(ids.AppBskyFeedSearchPosts), ) return { encoding: 'application/json', @@ -63,7 +64,7 @@ export default function (server: Server, ctx: AppContext) { handler: async (request) => { const res = await ctx.appviewAgent.api.app.bsky.feed.getPostThread( request.params, - await ctx.appviewAuth(), + await ctx.appviewAuth(ids.AppBskyFeedGetPostThread), ) return { encoding: 'application/json', @@ -77,7 +78,7 @@ export default function (server: Server, ctx: AppContext) { handler: async (request) => { const res = await ctx.appviewAgent.api.app.bsky.feed.getFeedGenerator( request.params, - await ctx.appviewAuth(), + await ctx.appviewAuth(ids.AppBskyFeedGetFeedGenerator), ) return { encoding: 'application/json', @@ -91,7 +92,7 @@ export default function (server: Server, ctx: AppContext) { handler: async (request) => { const res = await ctx.appviewAgent.api.app.bsky.graph.getFollows( request.params, - await ctx.appviewAuth(), + await ctx.appviewAuth(ids.AppBskyGraphGetFollows), ) return { encoding: 'application/json', @@ -105,7 +106,7 @@ export default function (server: Server, ctx: AppContext) { handler: async (request) => { const res = await ctx.appviewAgent.api.app.bsky.graph.getFollowers( request.params, - await ctx.appviewAuth(), + await ctx.appviewAuth(ids.AppBskyGraphGetFollowers), ) return { encoding: 'application/json', @@ -119,7 +120,7 @@ export default function (server: Server, ctx: AppContext) { handler: async (request) => { const res = await ctx.appviewAgent.api.app.bsky.graph.getList( request.params, - await ctx.appviewAuth(), + await ctx.appviewAuth(ids.AppBskyGraphGetList), ) return { encoding: 'application/json', @@ -133,7 +134,7 @@ export default function (server: Server, ctx: AppContext) { handler: async (request) => { const res = await ctx.appviewAgent.api.app.bsky.graph.getLists( request.params, - await ctx.appviewAuth(), + await ctx.appviewAuth(ids.AppBskyGraphGetLists), ) return { encoding: 'application/json', @@ -150,7 +151,7 @@ export default function (server: Server, ctx: AppContext) { } const res = await ctx.pdsAgent.api.com.atproto.admin.searchAccounts( request.params, - await ctx.pdsAuth(), + await ctx.pdsAuth(ids.ComAtprotoAdminSearchAccounts), ) return { encoding: 'application/json', @@ -164,7 +165,7 @@ export default function (server: Server, ctx: AppContext) { handler: async (request) => { const res = await ctx.appviewAgent.api.app.bsky.graph.getStarterPack( request.params, - await ctx.appviewAuth(), + await ctx.appviewAuth(ids.AppBskyGraphGetStarterPack), ) return { encoding: 'application/json', @@ -178,7 +179,7 @@ export default function (server: Server, ctx: AppContext) { handler: async (request) => { const res = await ctx.appviewAgent.api.app.bsky.graph.getStarterPacks( request.params, - await ctx.appviewAuth(), + await ctx.appviewAuth(ids.AppBskyGraphGetStarterPacks), ) return { encoding: 'application/json', @@ -193,7 +194,7 @@ export default function (server: Server, ctx: AppContext) { const res = await ctx.appviewAgent.api.app.bsky.graph.getActorStarterPacks( request.params, - await ctx.appviewAuth(), + await ctx.appviewAuth(ids.AppBskyGraphGetActorStarterPacks), ) return { encoding: 'application/json', @@ -207,7 +208,7 @@ export default function (server: Server, ctx: AppContext) { handler: async (request) => { const res = await ctx.appviewAgent.api.app.bsky.feed.getLikes( request.params, - await ctx.appviewAuth(), + await ctx.appviewAuth(ids.AppBskyFeedGetLikes), ) return { encoding: 'application/json', @@ -221,7 +222,7 @@ export default function (server: Server, ctx: AppContext) { handler: async (request) => { const res = await ctx.appviewAgent.api.app.bsky.feed.getRepostedBy( request.params, - await ctx.appviewAuth(), + await ctx.appviewAuth(ids.AppBskyFeedGetRepostedBy), ) return { encoding: 'application/json', @@ -236,7 +237,7 @@ export default function (server: Server, ctx: AppContext) { const res = await ctx.appviewAgent.api.app.bsky.actor.searchActorsTypeahead( request.params, - await ctx.appviewAuth(), + await ctx.appviewAuth(ids.AppBskyActorSearchActorsTypeahead), ) return { encoding: 'application/json', diff --git a/packages/ozone/src/api/util.ts b/packages/ozone/src/api/util.ts index a93d5735fa6..957cbd5649c 100644 --- a/packages/ozone/src/api/util.ts +++ b/packages/ozone/src/api/util.ts @@ -26,6 +26,7 @@ import { ROLEMODERATOR, ROLETRIAGE, } from '../lexicon/types/tools/ozone/team/defs' +import { ids } from '../lexicon/lexicons' export const getPdsAccountInfo = async ( ctx: AppContext, @@ -33,7 +34,7 @@ export const getPdsAccountInfo = async ( ): Promise => { const agent = ctx.pdsAgent if (!agent) return null - const auth = await ctx.pdsAuth() + const auth = await ctx.pdsAuth(ids.ComAtprotoAdminGetAccountInfo) if (!auth) return null try { const res = await agent.api.com.atproto.admin.getAccountInfo({ did }, auth) diff --git a/packages/ozone/src/auth-verifier.ts b/packages/ozone/src/auth-verifier.ts index 7cb8f532ec1..3fd64048320 100644 --- a/packages/ozone/src/auth-verifier.ts +++ b/packages/ozone/src/auth-verifier.ts @@ -1,7 +1,11 @@ import express from 'express' import * as ui8 from 'uint8arrays' import { IdResolver } from '@atproto/identity' -import { AuthRequiredError, verifyJwt } from '@atproto/xrpc-server' +import { + AuthRequiredError, + parseReqNsid, + verifyJwt, +} from '@atproto/xrpc-server' import { TeamService } from './team' type ReqCtx = { @@ -106,10 +110,11 @@ export class AuthVerifier { if (!jwtStr) { throw new AuthRequiredError('missing jwt', 'MissingJwt') } + const nsid = parseReqNsid(reqCtx.req) const payload = await verifyJwt( jwtStr, this.serviceDid, - null, + nsid, getSigningKey, ) const iss = payload.iss diff --git a/packages/ozone/src/context.ts b/packages/ozone/src/context.ts index 749e162da3e..c120adce5f3 100644 --- a/packages/ozone/src/context.ts +++ b/packages/ozone/src/context.ts @@ -84,11 +84,11 @@ export class AppContext { didCache, }) - const createAuthHeaders = (aud: string) => + const createAuthHeaders = (aud: string, lxm: string) => createServiceAuthHeaders({ iss: `${cfg.service.did}#atproto_labeler`, aud, - lxm: null, + lxm, keypair: signingKey, }) @@ -226,32 +226,32 @@ export class AppContext { return this.opts.authVerifier } - async serviceAuthHeaders(aud: string) { + async serviceAuthHeaders(aud: string, lxm: string) { const iss = `${this.cfg.service.did}#atproto_labeler` return createServiceAuthHeaders({ iss, aud, - lxm: null, + lxm, keypair: this.signingKey, }) } - async pdsAuth() { + async pdsAuth(lxm: string) { if (!this.cfg.pds) { return undefined } - return this.serviceAuthHeaders(this.cfg.pds.did) + return this.serviceAuthHeaders(this.cfg.pds.did, lxm) } - async appviewAuth() { - return this.serviceAuthHeaders(this.cfg.appview.did) + async appviewAuth(lxm: string) { + return this.serviceAuthHeaders(this.cfg.appview.did, lxm) } - async chatAuth() { + async chatAuth(lxm: string) { if (!this.cfg.chat) { throw new Error('No chat service configured') } - return this.serviceAuthHeaders(this.cfg.chat.did) + return this.serviceAuthHeaders(this.cfg.chat.did, lxm) } devOverride(overrides: Partial) { diff --git a/packages/ozone/src/daemon/context.ts b/packages/ozone/src/daemon/context.ts index 14ea9abf9ed..d81f3f4d78b 100644 --- a/packages/ozone/src/daemon/context.ts +++ b/packages/ozone/src/daemon/context.ts @@ -39,11 +39,11 @@ export class DaemonContext { }) const appviewAgent = new AtpAgent({ service: cfg.appview.url }) - const createAuthHeaders = (aud: string) => + const createAuthHeaders = (aud: string, lxm: string) => createServiceAuthHeaders({ iss: `${cfg.service.did}#atproto_labeler`, aud, - lxm: null, + lxm, keypair: signingKey, }) diff --git a/packages/ozone/src/daemon/event-pusher.ts b/packages/ozone/src/daemon/event-pusher.ts index 61882a66ec3..c423eef9ac1 100644 --- a/packages/ozone/src/daemon/event-pusher.ts +++ b/packages/ozone/src/daemon/event-pusher.ts @@ -8,6 +8,7 @@ import { dbLogger } from '../logger' import { InputSchema } from '../lexicon/types/com/atproto/admin/updateSubjectStatus' import { BlobPushEvent } from '../db/schema/blob_push_event' import { Insertable, Selectable } from 'kysely' +import { ids } from '../lexicon/lexicons' type EventSubject = InputSchema['subject'] @@ -45,7 +46,10 @@ export class EventPusher { constructor( public db: Database, - public createAuthHeaders: (aud: string) => Promise, + public createAuthHeaders: ( + aud: string, + method: string, + ) => Promise, services: { appview?: { url: string @@ -162,7 +166,10 @@ export class EventPusher { subject: EventSubject, takedownRef: string | null, ): Promise { - const auth = await this.createAuthHeaders(service.did) + const auth = await this.createAuthHeaders( + service.did, + ids.ComAtprotoAdminUpdateSubjectStatus, + ) try { await retryHttp(() => service.agent.com.atproto.admin.updateSubjectStatus( diff --git a/packages/ozone/src/mod-service/index.ts b/packages/ozone/src/mod-service/index.ts index 1721068e55d..65c44768aaa 100644 --- a/packages/ozone/src/mod-service/index.ts +++ b/packages/ozone/src/mod-service/index.ts @@ -49,6 +49,7 @@ import { ImageInvalidator } from '../image-invalidator' import { httpLogger as log } from '../logger' import { OzoneConfig } from '../config' import { LABELER_HEADER_NAME, ParsedLabelers } from '../util' +import { ids } from '../lexicon/lexicons' export type ModerationServiceCreator = (db: Database) => ModerationService @@ -62,7 +63,10 @@ export class ModerationService { public idResolver: IdResolver, public eventPusher: EventPusher, public appviewAgent: AtpAgent, - private createAuthHeaders: (aud: string) => Promise, + private createAuthHeaders: ( + aud: string, + method: string, + ) => Promise, public imgInvalidator?: ImageInvalidator, ) {} @@ -74,7 +78,7 @@ export class ModerationService { idResolver: IdResolver, eventPusher: EventPusher, appviewAgent: AtpAgent, - createAuthHeaders: (aud: string) => Promise, + createAuthHeaders: (aud: string, method: string) => Promise, imgInvalidator?: ImageInvalidator, ) { return (db: Database) => @@ -97,8 +101,11 @@ export class ModerationService { this.signingKey, this.signingKeyId, this.appviewAgent, - async (labelers?: ParsedLabelers) => { - const authHeaders = await this.createAuthHeaders(this.cfg.appview.did) + async (method: string, labelers?: ParsedLabelers) => { + const authHeaders = await this.createAuthHeaders( + this.cfg.appview.did, + method, + ) if (labelers?.dids?.length) { authHeaders.headers[LABELER_HEADER_NAME] = labelers.dids.join(', ') } @@ -936,7 +943,10 @@ export class ModerationService { }, { encoding: 'application/json', - ...(await this.createAuthHeaders(serverInfo.did)), + ...(await this.createAuthHeaders( + serverInfo.did, + ids.ComAtprotoAdminSendEmail, + )), }, ) if (!delivery.sent) { diff --git a/packages/ozone/src/mod-service/views.ts b/packages/ozone/src/mod-service/views.ts index 0b51e04d189..f795a34ef9c 100644 --- a/packages/ozone/src/mod-service/views.ts +++ b/packages/ozone/src/mod-service/views.ts @@ -29,6 +29,7 @@ import { LabelRow } from '../db/schema/label' import { dbLogger } from '../logger' import { httpLogger } from '../logger' import { ParsedLabelers } from '../util' +import { ids } from '../lexicon/lexicons' export type AuthHeaders = { headers: { @@ -43,12 +44,12 @@ export class ModerationViews { private signingKey: Keypair, private signingKeyId: number, private appviewAgent: AtpAgent, - private appviewAuth: () => Promise, + private appviewAuth: (method: string) => Promise, ) {} async getAccoutInfosByDid(dids: string[]): Promise> { if (dids.length === 0) return new Map() - const auth = await this.appviewAuth() + const auth = await this.appviewAuth(ids.ComAtprotoAdminGetAccountInfos) if (!auth) return new Map() try { const res = await this.appviewAgent.api.com.atproto.admin.getAccountInfos( @@ -236,7 +237,7 @@ export class ModerationViews { async fetchRecords( subjects: RecordSubject[], ): Promise> { - const auth = await this.appviewAuth() + const auth = await this.appviewAuth(ids.ComAtprotoRepoGetRecord) if (!auth) return new Map() const fetched = await Promise.all( subjects.map(async (subject) => { @@ -547,7 +548,7 @@ export class ModerationViews { async fetchAuthorFeed( actor: string, ): Promise { - const auth = await this.appviewAuth() + const auth = await this.appviewAuth(ids.AppBskyFeedGetAuthorFeed) if (!auth) return [] const { data: { feed }, diff --git a/packages/ozone/src/team/index.ts b/packages/ozone/src/team/index.ts index d1f009d9f02..9629d165022 100644 --- a/packages/ozone/src/team/index.ts +++ b/packages/ozone/src/team/index.ts @@ -7,6 +7,7 @@ import { InvalidRequestError } from '@atproto/xrpc-server' import { chunkArray } from '@atproto/common' import AppContext from '../context' import { httpLogger } from '../logger' +import { ids } from '../lexicon/lexicons' export type TeamServiceCreator = (db: Database) => TeamService @@ -168,7 +169,7 @@ export class TeamService { const profiles = new Map() try { - const headers = await ctx.appviewAuth() + const headers = await ctx.appviewAuth(ids.AppBskyActorGetProfiles) for (const actors of chunkArray(dids, 25)) { const { data } = await ctx.appviewAgent.api.app.bsky.actor.getProfiles( diff --git a/packages/ozone/tests/3p-labeler.test.ts b/packages/ozone/tests/3p-labeler.test.ts index 99739162e67..bd0541a6dca 100644 --- a/packages/ozone/tests/3p-labeler.test.ts +++ b/packages/ozone/tests/3p-labeler.test.ts @@ -9,6 +9,7 @@ import { import { AtpAgent } from '@atproto/api' import { Secp256k1Keypair } from '@atproto/crypto' import { LABELER_HEADER_NAME } from '../src/util' +import { ids } from '../src/lexicon/lexicons' describe('labels from 3p labelers', () => { let network: TestNetwork @@ -170,11 +171,17 @@ describe('labels from 3p labelers', () => { ] = await Promise.all([ agent.api.tools.ozone.moderation.getRecord( { uri: sc.posts[sc.dids.alice][0].ref.uriStr }, - { headers: await ozone.modHeaders() }, + { + headers: await ozone.modHeaders(ids.ToolsOzoneModerationGetRecord), + }, ), thirdPartyAgent.api.tools.ozone.moderation.getRecord( { uri: sc.posts[sc.dids.alice][0].ref.uriStr }, - { headers: await thirdPartyLabeler.modHeaders() }, + { + headers: await thirdPartyLabeler.modHeaders( + ids.ToolsOzoneModerationGetRecord, + ), + }, ), ]) @@ -189,7 +196,9 @@ describe('labels from 3p labelers', () => { }) it('includes labels from all authorities requested via header', async () => { - const authHeaders = await ozone.modHeaders() + const authHeaders = await ozone.modHeaders( + ids.ToolsOzoneModerationGetRecord, + ) const { data: recordIncludingExternalLabels } = await agent.api.tools.ozone.moderation.getRecord( { uri: sc.posts[sc.dids.alice][0].ref.uriStr }, @@ -225,11 +234,17 @@ describe('labels from 3p labelers', () => { await Promise.all([ agent.api.tools.ozone.moderation.getRepo( { did: sc.dids.alice }, - { headers: await ozone.modHeaders() }, + { + headers: await ozone.modHeaders(ids.ToolsOzoneModerationGetRepo), + }, ), thirdPartyAgent.api.tools.ozone.moderation.getRepo( { did: sc.dids.alice }, - { headers: await thirdPartyLabeler.modHeaders() }, + { + headers: await thirdPartyLabeler.modHeaders( + ids.ToolsOzoneModerationGetRepo, + ), + }, ), ]) @@ -244,7 +259,9 @@ describe('labels from 3p labelers', () => { }) it('includes labels from all authorities requested via header', async () => { - const authHeaders = await ozone.modHeaders() + const authHeaders = await ozone.modHeaders( + ids.ToolsOzoneModerationGetRepo, + ) const { data: recordIncludingExternalLabels } = await agent.api.tools.ozone.moderation.getRepo( { did: sc.dids.alice }, diff --git a/packages/ozone/tests/communication-templates.test.ts b/packages/ozone/tests/communication-templates.test.ts index 0c396156faa..39fe12259b7 100644 --- a/packages/ozone/tests/communication-templates.test.ts +++ b/packages/ozone/tests/communication-templates.test.ts @@ -1,5 +1,6 @@ import { TestNetwork, SeedClient, basicSeed } from '@atproto/dev-env' import { AtpAgent } from '@atproto/api' +import { ids } from '../src/lexicon/lexicons' describe('communication-templates', () => { let network: TestNetwork @@ -30,7 +31,10 @@ describe('communication-templates', () => { const { data } = await agent.api.tools.ozone.communication.listTemplates( {}, { - headers: await network.ozone.modHeaders('moderator'), + headers: await network.ozone.modHeaders( + ids.ToolsOzoneCommunicationListTemplates, + 'moderator', + ), }, ) return data.communicationTemplates @@ -42,7 +46,10 @@ describe('communication-templates', () => { { ...templateOne, createdBy: sc.dids.bob }, { encoding: 'application/json', - headers: await network.ozone.modHeaders('triage'), + headers: await network.ozone.modHeaders( + ids.ToolsOzoneCommunicationCreateTemplate, + 'triage', + ), }, ) await expect(moderatorReq).rejects.toThrow( @@ -52,7 +59,10 @@ describe('communication-templates', () => { { ...templateOne, createdBy: sc.dids.bob }, { encoding: 'application/json', - headers: await network.ozone.modHeaders('admin'), + headers: await network.ozone.modHeaders( + ids.ToolsOzoneCommunicationCreateTemplate, + 'admin', + ), }, ) @@ -76,7 +86,10 @@ describe('communication-templates', () => { { ...templateTwo, createdBy: sc.dids.bob }, { encoding: 'application/json', - headers: await network.ozone.modHeaders('admin'), + headers: await network.ozone.modHeaders( + ids.ToolsOzoneCommunicationCreateTemplate, + 'admin', + ), }, ) @@ -91,7 +104,10 @@ describe('communication-templates', () => { { id: '1', updatedBy: sc.dids.bob, name: '1 Test template' }, { encoding: 'application/json', - headers: await network.ozone.modHeaders('admin'), + headers: await network.ozone.modHeaders( + ids.ToolsOzoneCommunicationUpdateTemplate, + 'admin', + ), }, ) @@ -105,7 +121,10 @@ describe('communication-templates', () => { { id: '1' }, { encoding: 'application/json', - headers: await network.ozone.modHeaders('triage'), + headers: await network.ozone.modHeaders( + ids.ToolsOzoneCommunicationDeleteTemplate, + 'triage', + ), }, ) @@ -117,7 +136,10 @@ describe('communication-templates', () => { { id: '1' }, { encoding: 'application/json', - headers: await network.ozone.modHeaders('moderator'), + headers: await network.ozone.modHeaders( + ids.ToolsOzoneCommunicationDeleteTemplate, + 'moderator', + ), }, ) const list = await listTemplates() diff --git a/packages/ozone/tests/get-config.test.ts b/packages/ozone/tests/get-config.test.ts index 9dec6ba5d1b..51648e2d213 100644 --- a/packages/ozone/tests/get-config.test.ts +++ b/packages/ozone/tests/get-config.test.ts @@ -1,6 +1,7 @@ import { TestNetwork, SeedClient, basicSeed } from '@atproto/dev-env' import { AtpAgent } from '@atproto/api' import { TOOLS_OZONE_TEAM } from '../src/lexicon' +import { ids } from '../src/lexicon/lexicons' describe('get-config', () => { let network: TestNetwork @@ -25,7 +26,10 @@ describe('get-config', () => { const { data } = await agent.api.tools.ozone.server.getConfig( {}, { - headers: await network.ozone.modHeaders(role), + headers: await network.ozone.modHeaders( + ids.ToolsOzoneServerGetConfig, + role, + ), }, ) return data diff --git a/packages/ozone/tests/get-lists.test.ts b/packages/ozone/tests/get-lists.test.ts index 38e08b52b7c..c717c82a119 100644 --- a/packages/ozone/tests/get-lists.test.ts +++ b/packages/ozone/tests/get-lists.test.ts @@ -8,6 +8,7 @@ import { } from '@atproto/dev-env' import { AtpAgent, BSKY_LABELER_DID } from '@atproto/api' import { TAKEDOWN_LABEL } from '../src/mod-service' +import { ids } from '../src/lexicon/lexicons' describe('admin get lists', () => { let network: TestNetwork @@ -42,7 +43,7 @@ describe('admin get lists', () => { const [{ data: fromOzone }, { data: fromAppview }] = await Promise.all([ agent.api.app.bsky.graph.getLists( { actor: sc.dids.alice }, - { headers: await ozone.modHeaders() }, + { headers: await ozone.modHeaders(ids.AppBskyGraphGetLists) }, ), appviewAgent.api.app.bsky.graph.getLists({ actor: sc.dids.alice }), ]) diff --git a/packages/ozone/tests/get-record.test.ts b/packages/ozone/tests/get-record.test.ts index 6201521129e..8d4dbbb2527 100644 --- a/packages/ozone/tests/get-record.test.ts +++ b/packages/ozone/tests/get-record.test.ts @@ -12,6 +12,7 @@ import { REASONSPAM, } from '../src/lexicon/types/com/atproto/moderation/defs' import { forSnapshot } from './_util' +import { ids } from '../src/lexicon/lexicons' describe('admin get record view', () => { let network: TestNetwork @@ -72,7 +73,7 @@ describe('admin get record view', () => { it('gets a record by uri, even when taken down.', async () => { const result = await agent.api.tools.ozone.moderation.getRecord( { uri: sc.posts[sc.dids.alice][0].ref.uriStr }, - { headers: await ozone.modHeaders() }, + { headers: await ozone.modHeaders(ids.ToolsOzoneModerationGetRecord) }, ) expect(forSnapshot(result.data)).toMatchSnapshot() }) @@ -83,7 +84,7 @@ describe('admin get record view', () => { uri: sc.posts[sc.dids.alice][0].ref.uriStr, cid: sc.posts[sc.dids.alice][0].ref.cidStr, }, - { headers: await ozone.modHeaders() }, + { headers: await ozone.modHeaders(ids.ToolsOzoneModerationGetRecord) }, ) expect(forSnapshot(result.data)).toMatchSnapshot() }) @@ -97,7 +98,7 @@ describe('admin get record view', () => { 'badrkey', ).toString(), }, - { headers: await ozone.modHeaders() }, + { headers: await ozone.modHeaders(ids.ToolsOzoneModerationGetRecord) }, ) await expect(promise).rejects.toThrow('Record not found') }) @@ -108,7 +109,7 @@ describe('admin get record view', () => { uri: sc.posts[sc.dids.alice][0].ref.uriStr, cid: sc.posts[sc.dids.alice][1].ref.cidStr, // Mismatching cid }, - { headers: await ozone.modHeaders() }, + { headers: await ozone.modHeaders(ids.ToolsOzoneModerationGetRecord) }, ) await expect(promise).rejects.toThrow('Record not found') }) diff --git a/packages/ozone/tests/get-repo.test.ts b/packages/ozone/tests/get-repo.test.ts index 260d29c690b..c122dc3a001 100644 --- a/packages/ozone/tests/get-repo.test.ts +++ b/packages/ozone/tests/get-repo.test.ts @@ -11,6 +11,7 @@ import { REASONSPAM, } from '../src/lexicon/types/com/atproto/moderation/defs' import { forSnapshot } from './_util' +import { ids } from '../src/lexicon/lexicons' describe('admin get repo view', () => { let network: TestNetwork @@ -78,7 +79,7 @@ describe('admin get repo view', () => { it('gets a repo by did, even when taken down.', async () => { const result = await agent.api.tools.ozone.moderation.getRepo( { did: sc.dids.alice }, - { headers: await ozone.modHeaders() }, + { headers: await ozone.modHeaders(ids.ToolsOzoneModerationGetRepo) }, ) expect(forSnapshot(result.data)).toMatchSnapshot() }) @@ -86,15 +87,25 @@ describe('admin get repo view', () => { it('does not include account emails for triage mods.', async () => { const { data: admin } = await agent.api.tools.ozone.moderation.getRepo( { did: sc.dids.bob }, - { headers: await ozone.modHeaders() }, + { headers: await ozone.modHeaders(ids.ToolsOzoneModerationGetRepo) }, ) const { data: moderator } = await agent.api.tools.ozone.moderation.getRepo( { did: sc.dids.bob }, - { headers: await ozone.modHeaders('moderator') }, + { + headers: await ozone.modHeaders( + ids.ToolsOzoneModerationGetRepo, + 'moderator', + ), + }, ) const { data: triage } = await agent.api.tools.ozone.moderation.getRepo( { did: sc.dids.bob }, - { headers: await ozone.modHeaders('triage') }, + { + headers: await ozone.modHeaders( + ids.ToolsOzoneModerationGetRepo, + 'triage', + ), + }, ) expect(admin.email).toEqual('bob@test.com') expect(moderator.email).toEqual('bob@test.com') @@ -106,7 +117,7 @@ describe('admin get repo view', () => { const { data: beforeEmailVerification } = await agent.api.tools.ozone.moderation.getRepo( { did: sc.dids.bob }, - { headers: await ozone.modHeaders() }, + { headers: await ozone.modHeaders(ids.ToolsOzoneModerationGetRepo) }, ) expect(beforeEmailVerification.emailConfirmedAt).toBeUndefined() @@ -128,7 +139,7 @@ describe('admin get repo view', () => { const { data: afterEmailVerification } = await agent.api.tools.ozone.moderation.getRepo( { did: sc.dids.bob }, - { headers: await ozone.modHeaders() }, + { headers: await ozone.modHeaders(ids.ToolsOzoneModerationGetRepo) }, ) expect(afterEmailVerification.emailConfirmedAt).toBeTruthy() @@ -140,7 +151,7 @@ describe('admin get repo view', () => { it('returns deactivation state', async () => { const res = await agent.api.tools.ozone.moderation.getRepo( { did: sc.dids.dan }, - { headers: await ozone.modHeaders() }, + { headers: await ozone.modHeaders(ids.ToolsOzoneModerationGetRepo) }, ) expect(res.data.deactivatedAt).toBeDefined() @@ -149,7 +160,7 @@ describe('admin get repo view', () => { it('fails when repo does not exist.', async () => { const promise = agent.api.tools.ozone.moderation.getRepo( { did: 'did:plc:doesnotexist' }, - { headers: await ozone.modHeaders() }, + { headers: await ozone.modHeaders(ids.ToolsOzoneModerationGetRepo) }, ) await expect(promise).rejects.toThrow('Repo not found') }) diff --git a/packages/ozone/tests/get-starter-pack.test.ts b/packages/ozone/tests/get-starter-pack.test.ts index 73f4417e10b..b506f21e292 100644 --- a/packages/ozone/tests/get-starter-pack.test.ts +++ b/packages/ozone/tests/get-starter-pack.test.ts @@ -8,6 +8,7 @@ import { import { AtpAgent } from '@atproto/api' import { forSnapshot } from './_util' import { TAKEDOWN_LABEL } from '../src/mod-service' +import { ids } from '../src/lexicon/lexicons' describe('admin get starter pack view', () => { let network: TestNetwork @@ -51,7 +52,7 @@ describe('admin get starter pack view', () => { it('gets a starterpack by uri', async () => { const result = await agent.api.app.bsky.graph.getStarterPack( { starterPack: sp1.uriStr }, - { headers: await ozone.modHeaders() }, + { headers: await ozone.modHeaders(ids.AppBskyGraphGetStarterPack) }, ) expect(forSnapshot(result.data)).toMatchSnapshot() }) @@ -62,7 +63,12 @@ describe('admin get starter pack view', () => { const beforeTakedownFromAppview = await appviewAgent.api.app.bsky.graph.getStarterPack( { starterPack: sp1.uriStr }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyGraphGetStarterPack, + ), + }, ) expect( @@ -84,7 +90,7 @@ describe('admin get starter pack view', () => { const afterTakedownFromOzone = await agent.api.app.bsky.graph.getStarterPack( { starterPack: sp1.uriStr }, - { headers: await ozone.modHeaders() }, + { headers: await ozone.modHeaders(ids.AppBskyGraphGetStarterPack) }, ) // validate that ozone returns starterpacks after takedown @@ -96,7 +102,12 @@ describe('admin get starter pack view', () => { await expect( appviewAgent.api.app.bsky.graph.getStarterPack( { starterPack: sp1.uriStr }, - { headers: await network.serviceHeaders(sc.dids.alice) }, + { + headers: await network.serviceHeaders( + sc.dids.alice, + ids.AppBskyGraphGetStarterPack, + ), + }, ), ).rejects.toThrow('Starter pack not found') }) diff --git a/packages/ozone/tests/moderation.test.ts b/packages/ozone/tests/moderation.test.ts index 0206c93a54b..9f4937935df 100644 --- a/packages/ozone/tests/moderation.test.ts +++ b/packages/ozone/tests/moderation.test.ts @@ -23,6 +23,7 @@ import { import { EventReverser } from '../src' import { ImageInvalidator } from '../src/image-invalidator' import { TAKEDOWN_LABEL } from '../src/mod-service' +import { ids } from '../src/lexicon/lexicons' describe('moderation', () => { let network: TestNetwork @@ -741,7 +742,11 @@ describe('moderation', () => { async function getRecordLabels(uri: string) { const result = await agent.api.tools.ozone.moderation.getRecord( { uri }, - { headers: await network.ozone.modHeaders() }, + { + headers: await network.ozone.modHeaders( + ids.ToolsOzoneModerationGetRecord, + ), + }, ) const labels = result.data.labels ?? [] return labels.map((l) => l.val) @@ -750,7 +755,11 @@ describe('moderation', () => { async function getRepoLabels(did: string) { const result = await agent.api.tools.ozone.moderation.getRepo( { did }, - { headers: await network.ozone.modHeaders() }, + { + headers: await network.ozone.modHeaders( + ids.ToolsOzoneModerationGetRepo, + ), + }, ) const labels = result.data.labels ?? [] return labels.map((l) => l.val) diff --git a/packages/ozone/tests/repo-search.test.ts b/packages/ozone/tests/repo-search.test.ts index c5d98467265..26aa3e45344 100644 --- a/packages/ozone/tests/repo-search.test.ts +++ b/packages/ozone/tests/repo-search.test.ts @@ -6,6 +6,7 @@ import { } from '@atproto/dev-env' import { AtpAgent } from '@atproto/api' import { paginateAll } from './_util' +import { ids } from '../src/lexicon/lexicons' describe('admin repo search view', () => { let network: TestNetwork @@ -22,7 +23,9 @@ describe('admin repo search view', () => { sc = network.getSeedClient() modClient = network.ozone.getModClient() await usersBulkSeed(sc) - headers = await network.ozone.modHeaders() + headers = await network.ozone.modHeaders( + ids.ToolsOzoneModerationSearchRepos, + ) await network.processAll() }) diff --git a/packages/pds/src/api/com/atproto/server/getServiceAuth.ts b/packages/pds/src/api/com/atproto/server/getServiceAuth.ts index e13a82b57f5..6436db59f9f 100644 --- a/packages/pds/src/api/com/atproto/server/getServiceAuth.ts +++ b/packages/pds/src/api/com/atproto/server/getServiceAuth.ts @@ -1,14 +1,15 @@ import { InvalidRequestError, createServiceJwt } from '@atproto/xrpc-server' -import { MINUTE } from '@atproto/common' +import { HOUR, MINUTE } from '@atproto/common' import AppContext from '../../../../context' import { Server } from '../../../../lexicon' +import { PRIVILEGED_METHODS } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { server.com.atproto.server.getServiceAuth({ - auth: ctx.authVerifier.accessPrivileged(), + auth: ctx.authVerifier.accessStandard(), handler: async ({ params, auth }) => { const did = auth.credentials.did - const keypair = await ctx.actorStore.keypair(did) + const { aud, lxm = null } = params const exp = params.exp ? params.exp * 1000 : undefined if (exp) { const diff = exp - Date.now() @@ -17,19 +18,34 @@ export default function (server: Server, ctx: AppContext) { 'expiration is in past', 'BadExpiration', ) - } else if (diff > MINUTE) { + } else if (diff > HOUR) { + throw new InvalidRequestError( + 'cannot request a token with an expiration more than an hour in the future', + 'BadExpiration', + ) + } else if (!lxm && diff > MINUTE) { throw new InvalidRequestError( - 'cannot request a token with an expiration more than a minute in the future', + 'cannot request a method-less token with an expiration more than a minute in the future', 'BadExpiration', ) } } + if ( + !auth.credentials.isPrivileged && + lxm && + PRIVILEGED_METHODS.has(lxm) + ) { + throw new InvalidRequestError( + `cannot request a service auth token for the following method with an app password: ${lxm}`, + ) + } + const keypair = await ctx.actorStore.keypair(did) const token = await createServiceJwt({ iss: did, - aud: params.aud, - lxm: params.lxm ?? null, + aud, exp, + lxm, keypair, }) return { diff --git a/packages/pds/src/auth-verifier.ts b/packages/pds/src/auth-verifier.ts index 90b82a9a5ed..3b764e4e91b 100644 --- a/packages/pds/src/auth-verifier.ts +++ b/packages/pds/src/auth-verifier.ts @@ -15,6 +15,7 @@ import { InvalidRequestError, StreamAuthVerifierContext, XRPCError, + parseReqNsid, verifyJwt as verifyServiceJwt, } from '@atproto/xrpc-server' import * as jose from 'jose' @@ -574,10 +575,11 @@ export class AuthVerifier { if (!jwtStr) { throw new AuthRequiredError('missing jwt', 'MissingJwt') } + const nsid = parseReqNsid(ctx.req) const payload = await verifyServiceJwt( jwtStr, opts.aud, - null, + nsid, getSigningKey, ) return { iss: payload.iss, aud: payload.aud } diff --git a/packages/pds/src/pipethrough.ts b/packages/pds/src/pipethrough.ts index 5a85d62b3e0..f48b182194c 100644 --- a/packages/pds/src/pipethrough.ts +++ b/packages/pds/src/pipethrough.ts @@ -8,13 +8,13 @@ import { CatchallHandler, HandlerPipeThrough, InvalidRequestError, + parseReqNsid, } from '@atproto/xrpc-server' import { ResponseType, XRPCError } from '@atproto/xrpc' +import { getServiceEndpoint, noUndefinedVals } from '@atproto/common' import { ids, lexicons } from './lexicon/lexicons' import { httpLogger } from './logger' -import { getServiceEndpoint, noUndefinedVals } from '@atproto/common' import AppContext from './context' -import { parseReqNsid } from '@atproto/xrpc-server' export const proxyHandler = (ctx: AppContext): CatchallHandler => { const accessStandard = ctx.authVerifier.accessStandard() diff --git a/packages/pds/tests/account-migration.test.ts b/packages/pds/tests/account-migration.test.ts index 5de1ef29b05..7da6cb76dba 100644 --- a/packages/pds/tests/account-migration.test.ts +++ b/packages/pds/tests/account-migration.test.ts @@ -7,6 +7,7 @@ import { } from '@atproto/dev-env' import { readCar } from '@atproto/repo' import assert from 'assert' +import { ids } from '../src/lexicon/lexicons' describe('account migration', () => { let network: TestNetworkNoAppView @@ -80,6 +81,7 @@ describe('account migration', () => { const serviceJwtRes = await oldAgent.com.atproto.server.getServiceAuth({ aud: newServerDid, + lxm: ids.ComAtprotoServerCreateAccount, }) const serviceJwt = serviceJwtRes.data.token diff --git a/packages/pds/tests/app-passwords.test.ts b/packages/pds/tests/app-passwords.test.ts index 5104e5542bf..00130dc6a55 100644 --- a/packages/pds/tests/app-passwords.test.ts +++ b/packages/pds/tests/app-passwords.test.ts @@ -103,15 +103,29 @@ describe('app_passwords', () => { }) it('restricts privileged app password actions', async () => { + const attempt = appAgent.api.chat.bsky.convo.listConvos({}) + await expect(attempt).rejects.toThrow('Bad token method') + }) + + it('restricts privileged app password actions', async () => { + const attempt = appAgent.api.chat.bsky.convo.listConvos() + await expect(attempt).rejects.toThrow('Bad token method') + }) + + it('restricts service auth token methods for non-privileged access tokens', async () => { const attempt = appAgent.api.com.atproto.server.getServiceAuth({ aud: 'did:example:test', + lxm: 'com.atproto.server.createAccount', }) - await expect(attempt).rejects.toThrow('Bad token scope') + await expect(attempt).rejects.toThrow( + /cannot request a service auth token for the following method with an app password/, + ) }) - it('allows privileged actions with a privileged app password', async () => { + it('allows privileged service auth token scopes for privileged access tokens', async () => { await priviAgent.api.com.atproto.server.getServiceAuth({ aud: 'did:example:test', + lxm: 'com.atproto.server.createAccount', }) }) @@ -142,8 +156,11 @@ describe('app_passwords', () => { // allows privileged app passwords or higher const priviAttempt = appAgent.api.com.atproto.server.getServiceAuth({ aud: 'did:example:test', + lxm: 'com.atproto.server.createAccount', }) - await expect(priviAttempt).rejects.toThrow('Bad token scope') + await expect(priviAttempt).rejects.toThrow( + /cannot request a service auth token for the following method with an app password/, + ) // allows only full access auth const fullAttempt = appAgent.api.com.atproto.server.createAppPassword( diff --git a/packages/pds/tests/moderator-auth.test.ts b/packages/pds/tests/moderator-auth.test.ts index aba04a93273..3330ba28f81 100644 --- a/packages/pds/tests/moderator-auth.test.ts +++ b/packages/pds/tests/moderator-auth.test.ts @@ -5,6 +5,7 @@ import { createServiceAuthHeaders } from '@atproto/xrpc-server' import * as plc from '@did-plc/lib' import usersSeed from './seeds/users' import { RepoRef } from '../src/lexicon/types/com/atproto/admin/defs' +import { ids } from '../src/lexicon/lexicons' describe('moderator auth', () => { let network: TestNetworkNoAppView @@ -73,10 +74,10 @@ describe('moderator auth', () => { }) it('allows service auth requests from the configured appview did', async () => { - const headers = await createServiceAuthHeaders({ + const updateHeaders = await createServiceAuthHeaders({ iss: modServiceDid, aud: pdsDid, - lxm: null, + lxm: ids.ComAtprotoAdminUpdateSubjectStatus, keypair: modServiceKey, }) await agent.api.com.atproto.admin.updateSubjectStatus( @@ -85,16 +86,22 @@ describe('moderator auth', () => { takedown: { applied: true, ref: 'test-repo' }, }, { - ...headers, + ...updateHeaders, encoding: 'application/json', }, ) + const getHeaders = await createServiceAuthHeaders({ + iss: modServiceDid, + aud: pdsDid, + lxm: ids.ComAtprotoAdminGetSubjectStatus, + keypair: modServiceKey, + }) const res = await agent.api.com.atproto.admin.getSubjectStatus( { did: repoSubject.did, }, - headers, + getHeaders, ) expect(res.data.subject.did).toBe(repoSubject.did) expect(res.data.takedown?.applied).toBe(true) @@ -104,7 +111,7 @@ describe('moderator auth', () => { const headers = await createServiceAuthHeaders({ iss: altModDid, aud: pdsDid, - lxm: null, + lxm: ids.ComAtprotoAdminUpdateSubjectStatus, keypair: modServiceKey, }) const attempt = agent.api.com.atproto.admin.updateSubjectStatus( @@ -125,7 +132,7 @@ describe('moderator auth', () => { const headers = await createServiceAuthHeaders({ iss: modServiceDid, aud: pdsDid, - lxm: null, + lxm: ids.ComAtprotoAdminUpdateSubjectStatus, keypair: badKey, }) const attempt = agent.api.com.atproto.admin.updateSubjectStatus( @@ -148,7 +155,7 @@ describe('moderator auth', () => { const headers = await createServiceAuthHeaders({ iss: modServiceDid, aud: sc.dids.alice, - lxm: null, + lxm: ids.ComAtprotoAdminUpdateSubjectStatus, keypair: modServiceKey, }) const attempt = agent.api.com.atproto.admin.updateSubjectStatus( diff --git a/packages/pds/tests/proxied/notif.test.ts b/packages/pds/tests/proxied/notif.test.ts index 5b86b87ac09..1d244be81bd 100644 --- a/packages/pds/tests/proxied/notif.test.ts +++ b/packages/pds/tests/proxied/notif.test.ts @@ -69,7 +69,7 @@ describe('notif service proxy', () => { const auth = await verifyJwt( spy.current?.['jwt'] as string, notifDid, - null, + 'app.bsky.notification.registerPush', async (did) => { const keypair = await network.pds.ctx.actorStore.keypair(did) return keypair.did() diff --git a/packages/pds/tests/proxied/proxy-header.test.ts b/packages/pds/tests/proxied/proxy-header.test.ts index 9eb9842c1ee..bad27f0bd64 100644 --- a/packages/pds/tests/proxied/proxy-header.test.ts +++ b/packages/pds/tests/proxied/proxy-header.test.ts @@ -66,7 +66,7 @@ describe('proxy header', () => { const verified = await verifyJwt( req.auth.replace('Bearer ', ''), proxyServer.did, - null, + 'app.bsky.actor.getProfile', (iss) => network.pds.ctx.idResolver.did.resolveAtprotoKey(iss, true), ) expect(verified.aud).toBe(proxyServer.did) diff --git a/packages/xrpc-server/src/auth.ts b/packages/xrpc-server/src/auth.ts index 548579d1eb3..596927df327 100644 --- a/packages/xrpc-server/src/auth.ts +++ b/packages/xrpc-server/src/auth.ts @@ -77,10 +77,12 @@ export const verifyJwt = async ( 'BadJwtAudience', ) } - if (lxm !== null && lxm !== payload.lxm) { + if (lxm !== null && payload.lxm !== lxm) { throw new AuthRequiredError( - `missing jwt lexicon method ("lxm"): ${lxm}`, - 'MissingJwtMethod', + payload.lxm !== undefined + ? `bad jwt lexicon method ("lxm"). must match: ${lxm}` + : `missing jwt lexicon method ("lxm"). must match: ${lxm}`, + 'BadJwtLexiconMethod', ) } diff --git a/packages/xrpc-server/src/util.ts b/packages/xrpc-server/src/util.ts index 47f9a5e764f..31d3c4ae03e 100644 --- a/packages/xrpc-server/src/util.ts +++ b/packages/xrpc-server/src/util.ts @@ -1,5 +1,6 @@ import assert from 'assert' import { Readable, Transform } from 'stream' +import { IncomingMessage } from 'http' import { createDeflate, createGunzip } from 'zlib' import express from 'express' import mime from 'mime-types' @@ -307,7 +308,11 @@ export interface ServerTiming { description?: string } -export const parseReqNsid = (req: express.Request): string => { - const nsid = req.originalUrl.split('?')[0].replace('/xrpc/', '') +export const parseReqNsid = ( + req: express.Request | IncomingMessage, +): string => { + const originalUrl = + ('originalUrl' in req && req.originalUrl) || req.url || '/' + const nsid = originalUrl.split('?')[0].replace('/xrpc/', '') return nsid.endsWith('/') ? nsid.slice(0, -1) : nsid // trim trailing slash } diff --git a/packages/xrpc-server/tests/auth.test.ts b/packages/xrpc-server/tests/auth.test.ts index 6e16e9a0227..5cbdd579ece 100644 --- a/packages/xrpc-server/tests/auth.test.ts +++ b/packages/xrpc-server/tests/auth.test.ts @@ -238,7 +238,7 @@ describe('Auth', () => { return keypair.did() }, ) - await expect(tryVerify).rejects.toThrow(/missing jwt lexicon method/) + await expect(tryVerify).rejects.toThrow(/bad jwt lexicon method/) }) it('fails on null lxm when lxm is required', async () => {