b934b396b1
* feat(api): support creation of oauth based AtpAgents * oauth: misc fixes for confidential clients * fix(xprc): remove ReadableStream.from polyfill * OAuth docs tweaks (#2679) * OAuth: clarification about client_name being shown * OAuth: re-write handle resolution privacy concern * avoid relying on ReadableStream.from in xrpc-server tests * feat(oauth-types): expose "ALLOW_UNSECURE_ORIGINS" constant * feat(handle-resolver): expose "AtprotoIdentityDidMethods" type * fix(oauth-client): ensure that the oauth metadata document contains client_id_metadata_document_supported * fix(oauth-types): prevent unknown query string in loopback client id * fix(identity-resolver): check that handle is in did doc's "alsoKnownAs" * feat(oauth-client:oauth-resolver): allow logging in using either the PDS URL or Entryway URL * fix(oauth-client): return better error in case of invalid "oauth-protected-resource" status code * refactor(did): group atproto specific checks in own * feat(api): relax typing of "appLabelers" and "labelers" AtpClient properties * allow any did as labeller (for tests mainly) * fix(api): allow to override "atproto-proxy" on a per-request basis * remove release candidate versions from changelog * update changeset for api and xrpc packages * Add missing changeset * revert RC versions * Proper wording in OAUTH.md api example * remove "pre" changeset file * xrpc: restore original behavior of setHEader and unsetHeader * docs: add comment for XrpcClient 's constructor arg * feat(api): expose "schemas" publicly * feat(api): allow customizing the whatwg fetch function of the AtpAgent * docs(api): improve migration docs * docs: change reference to BskyAgent to AtpAgent * docs: mention the breaking change regarding setSessionPersistHandler * fix(api): better split AtpClient concerns * fix(xrpc): remove unused import * refactor(api): simplify class hierarchu by removeing AtpClient * fix(api): mock proper method for facets detection * restore ability to restore session asynchronously * feat(api): allow instantiating Agent with same argument as super class * docs(api): properly extend Agent class * style(xrpc): var name * docs(api): remove "async" to header getter --------- Co-authored-by: Devin Ivy <devinivy@gmail.com> Co-authored-by: bnewbold <bnewbold@robocracy.org> Co-authored-by: Hailey <me@haileyok.com>
608 lines
15 KiB
TypeScript
608 lines
15 KiB
TypeScript
import { AtpAgent, AtUri } from '@atproto/api'
|
|
import { TestNetwork, SeedClient } from '@atproto/dev-env'
|
|
import basicSeed from '../seeds/basic'
|
|
import { forSnapshot } from '../_util'
|
|
|
|
describe('proxies view requests', () => {
|
|
let network: TestNetwork
|
|
let agent: AtpAgent
|
|
let sc: SeedClient
|
|
|
|
let alice: string
|
|
let bob: string
|
|
let carol: string
|
|
let dan: string
|
|
|
|
beforeAll(async () => {
|
|
network = await TestNetwork.create({
|
|
dbPostgresSchema: 'proxy_views',
|
|
})
|
|
agent = network.pds.getClient()
|
|
sc = network.getSeedClient()
|
|
await basicSeed(sc, { addModLabels: network.bsky })
|
|
alice = sc.dids.alice
|
|
bob = sc.dids.bob
|
|
carol = sc.dids.carol
|
|
dan = sc.dids.dan
|
|
const listRef = await sc.createList(alice, 'test list', 'curate')
|
|
await sc.addToList(alice, alice, listRef)
|
|
await sc.addToList(alice, bob, listRef)
|
|
await network.processAll()
|
|
})
|
|
|
|
beforeAll(async () => {
|
|
await agent.api.app.bsky.feed.generator.create(
|
|
{ repo: alice, rkey: 'all' },
|
|
{
|
|
did: 'did:example:feedgen',
|
|
displayName: 'All',
|
|
description: 'Provides all feed candidates',
|
|
createdAt: new Date().toISOString(),
|
|
},
|
|
sc.getHeaders(alice),
|
|
)
|
|
await network.processAll()
|
|
})
|
|
|
|
afterAll(async () => {
|
|
await network.close()
|
|
})
|
|
|
|
it('actor.getProfile', async () => {
|
|
const res = await agent.api.app.bsky.actor.getProfile(
|
|
{
|
|
actor: bob,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect(forSnapshot(res.data)).toMatchSnapshot()
|
|
})
|
|
|
|
it('actor.getProfiles', async () => {
|
|
const res = await agent.api.app.bsky.actor.getProfiles(
|
|
{
|
|
actors: [alice, bob],
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect(forSnapshot(res.data)).toMatchSnapshot()
|
|
})
|
|
|
|
it('actor.getSuggestions', async () => {
|
|
// mock some suggestions
|
|
const suggestions = [
|
|
{ did: sc.dids.bob, order: 1 },
|
|
{ did: sc.dids.carol, order: 2 },
|
|
{ did: sc.dids.dan, order: 3 },
|
|
]
|
|
await network.bsky.db.db
|
|
.insertInto('suggested_follow')
|
|
.values(suggestions)
|
|
.execute()
|
|
|
|
const res = await agent.api.app.bsky.actor.getSuggestions(
|
|
{},
|
|
{
|
|
headers: { ...sc.getHeaders(carol) },
|
|
},
|
|
)
|
|
expect(forSnapshot(res.data)).toMatchSnapshot()
|
|
const pt1 = await agent.api.app.bsky.actor.getSuggestions(
|
|
{
|
|
limit: 1,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(carol) },
|
|
},
|
|
)
|
|
const pt2 = await agent.api.app.bsky.actor.getSuggestions(
|
|
{
|
|
cursor: pt1.data.cursor,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(carol) },
|
|
},
|
|
)
|
|
expect([...pt1.data.actors, ...pt2.data.actors]).toEqual(res.data.actors)
|
|
})
|
|
|
|
it('actor.searchActor', async () => {
|
|
const res = await agent.api.app.bsky.actor.searchActors(
|
|
{
|
|
term: '.test',
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
// sort because pagination is done off of did
|
|
const sortedFull = res.data.actors.sort((a, b) =>
|
|
a.handle > b.handle ? 1 : -1,
|
|
)
|
|
expect(forSnapshot(sortedFull)).toMatchSnapshot()
|
|
const pt1 = await agent.api.app.bsky.actor.searchActors(
|
|
{
|
|
term: '.test',
|
|
limit: 1,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
const pt2 = await agent.api.app.bsky.actor.searchActors(
|
|
{
|
|
term: '.test',
|
|
cursor: pt1.data.cursor,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
const sortedPaginated = [...pt1.data.actors, ...pt2.data.actors].sort(
|
|
(a, b) => (a.handle > b.handle ? 1 : -1),
|
|
)
|
|
expect(sortedPaginated).toEqual(sortedFull)
|
|
})
|
|
|
|
it('actor.searchActorTypeahead', async () => {
|
|
const res = await agent.api.app.bsky.actor.searchActorsTypeahead(
|
|
{
|
|
term: '.test',
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
const sorted = res.data.actors.sort((a, b) =>
|
|
a.handle > b.handle ? 1 : -1,
|
|
)
|
|
expect(forSnapshot(sorted)).toMatchSnapshot()
|
|
})
|
|
|
|
it('feed.getAuthorFeed', async () => {
|
|
const res = await agent.api.app.bsky.feed.getAuthorFeed(
|
|
{
|
|
actor: bob,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect(forSnapshot(res.data)).toMatchSnapshot()
|
|
const pt1 = await agent.api.app.bsky.feed.getAuthorFeed(
|
|
{
|
|
actor: bob,
|
|
limit: 1,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
const pt2 = await agent.api.app.bsky.feed.getAuthorFeed(
|
|
{
|
|
actor: bob,
|
|
cursor: pt1.data.cursor,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect([...pt1.data.feed, ...pt2.data.feed]).toEqual(res.data.feed)
|
|
})
|
|
|
|
it('feed.getListFeed', async () => {
|
|
const list = Object.values(sc.lists[alice])[0].ref.uriStr
|
|
const res = await agent.api.app.bsky.feed.getListFeed(
|
|
{
|
|
list,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice), 'x-appview-proxy': 'true' },
|
|
},
|
|
)
|
|
expect(forSnapshot(res.data)).toMatchSnapshot()
|
|
const pt1 = await agent.api.app.bsky.feed.getListFeed(
|
|
{
|
|
list,
|
|
limit: 1,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice), 'x-appview-proxy': 'true' },
|
|
},
|
|
)
|
|
const pt2 = await agent.api.app.bsky.feed.getListFeed(
|
|
{
|
|
list,
|
|
cursor: pt1.data.cursor,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice), 'x-appview-proxy': 'true' },
|
|
},
|
|
)
|
|
expect([...pt1.data.feed, ...pt2.data.feed]).toEqual(res.data.feed)
|
|
})
|
|
|
|
it('feed.getLikes', async () => {
|
|
const postUri = sc.posts[carol][0].ref.uriStr
|
|
const res = await agent.api.app.bsky.feed.getLikes(
|
|
{
|
|
uri: postUri,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect(forSnapshot(res.data)).toMatchSnapshot()
|
|
const pt1 = await agent.api.app.bsky.feed.getLikes(
|
|
{
|
|
uri: postUri,
|
|
limit: 1,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
const pt2 = await agent.api.app.bsky.feed.getLikes(
|
|
{
|
|
uri: postUri,
|
|
cursor: pt1.data.cursor,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect([...pt1.data.likes, ...pt2.data.likes]).toEqual(res.data.likes)
|
|
})
|
|
|
|
it('feed.getRepostedBy', async () => {
|
|
const postUri = sc.posts[dan][1].ref.uriStr
|
|
const res = await agent.api.app.bsky.feed.getRepostedBy(
|
|
{
|
|
uri: postUri,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect(forSnapshot(res.data)).toMatchSnapshot()
|
|
const pt1 = await agent.api.app.bsky.feed.getRepostedBy(
|
|
{
|
|
uri: postUri,
|
|
limit: 1,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
const pt2 = await agent.api.app.bsky.feed.getRepostedBy(
|
|
{
|
|
uri: postUri,
|
|
cursor: pt1.data.cursor,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect([...pt1.data.repostedBy, ...pt2.data.repostedBy]).toEqual(
|
|
res.data.repostedBy,
|
|
)
|
|
})
|
|
|
|
it('feed.getPosts', async () => {
|
|
const uris = [sc.posts[bob][0].ref.uriStr, sc.posts[carol][0].ref.uriStr]
|
|
const res = await agent.api.app.bsky.feed.getPosts(
|
|
{
|
|
uris,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect(forSnapshot(res.data)).toMatchSnapshot()
|
|
})
|
|
|
|
it('feed.getTimeline', async () => {
|
|
const res = await agent.api.app.bsky.feed.getTimeline(
|
|
{},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
|
|
expect(forSnapshot(res.data)).toMatchSnapshot()
|
|
const pt1 = await agent.api.app.bsky.feed.getTimeline(
|
|
{
|
|
limit: 2,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
const pt2 = await agent.api.app.bsky.feed.getTimeline(
|
|
{
|
|
cursor: pt1.data.cursor,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect([...pt1.data.feed, ...pt2.data.feed]).toEqual(res.data.feed)
|
|
})
|
|
|
|
// @TODO disabled during appview v2 buildout
|
|
it.skip('unspecced.getPopularFeedGenerators', async () => {
|
|
const res = await agent.api.app.bsky.unspecced.getPopularFeedGenerators(
|
|
{},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect(forSnapshot(res.data)).toMatchSnapshot()
|
|
})
|
|
|
|
let feedUri: string
|
|
it('feed.getFeedGenerator', async () => {
|
|
feedUri = AtUri.make(
|
|
sc.dids.alice,
|
|
'app.bsky.feed.generator',
|
|
'my-feed',
|
|
).toString()
|
|
const gen = await network.createFeedGen({
|
|
[feedUri]: async () => {
|
|
return {
|
|
encoding: 'application/json',
|
|
body: { feed: [] },
|
|
}
|
|
},
|
|
})
|
|
await agent.api.app.bsky.feed.generator.create(
|
|
{ repo: sc.dids.alice, rkey: 'my-feed' },
|
|
{
|
|
did: gen.did,
|
|
displayName: 'MyFeed',
|
|
createdAt: new Date().toISOString(),
|
|
},
|
|
sc.getHeaders(sc.dids.alice),
|
|
)
|
|
await network.processAll()
|
|
const res = await agent.api.app.bsky.feed.getFeedGenerator(
|
|
{ feed: feedUri },
|
|
{
|
|
headers: { ...sc.getHeaders(sc.dids.alice) },
|
|
},
|
|
)
|
|
expect(forSnapshot(res.data)).toMatchSnapshot()
|
|
})
|
|
|
|
it('feed.getFeedGenerators', async () => {
|
|
const res = await agent.api.app.bsky.feed.getFeedGenerators(
|
|
{ feeds: [feedUri.toString()] },
|
|
{
|
|
headers: { ...sc.getHeaders(sc.dids.alice) },
|
|
},
|
|
)
|
|
expect(forSnapshot(res.data)).toMatchSnapshot()
|
|
})
|
|
|
|
it('graph.getBlocks', async () => {
|
|
await sc.block(alice, bob)
|
|
await sc.block(alice, carol)
|
|
await network.processAll()
|
|
const res = await agent.api.app.bsky.graph.getBlocks(
|
|
{},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect(forSnapshot(res.data)).toMatchSnapshot()
|
|
const pt1 = await agent.api.app.bsky.graph.getBlocks(
|
|
{
|
|
limit: 1,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
const pt2 = await agent.api.app.bsky.graph.getBlocks(
|
|
{
|
|
cursor: pt1.data.cursor,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect([...pt1.data.blocks, ...pt2.data.blocks]).toEqual(res.data.blocks)
|
|
await sc.unblock(alice, bob)
|
|
await sc.unblock(alice, carol)
|
|
await network.processAll()
|
|
})
|
|
|
|
it('graph.getFollows', async () => {
|
|
const res = await agent.api.app.bsky.graph.getFollows(
|
|
{ actor: bob },
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect(forSnapshot(res.data)).toMatchSnapshot()
|
|
const pt1 = await agent.api.app.bsky.graph.getFollows(
|
|
{
|
|
actor: bob,
|
|
limit: 1,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
const pt2 = await agent.api.app.bsky.graph.getFollows(
|
|
{
|
|
actor: bob,
|
|
cursor: pt1.data.cursor,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect([...pt1.data.follows, ...pt2.data.follows]).toEqual(res.data.follows)
|
|
})
|
|
|
|
it('graph.getFollowers', async () => {
|
|
const res = await agent.api.app.bsky.graph.getFollowers(
|
|
{ actor: bob },
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect(forSnapshot(res.data)).toMatchSnapshot()
|
|
const pt1 = await agent.api.app.bsky.graph.getFollowers(
|
|
{
|
|
actor: bob,
|
|
limit: 1,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
const pt2 = await agent.api.app.bsky.graph.getFollowers(
|
|
{
|
|
actor: bob,
|
|
cursor: pt1.data.cursor,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect([...pt1.data.followers, ...pt2.data.followers]).toEqual(
|
|
res.data.followers,
|
|
)
|
|
})
|
|
|
|
let listUri: string
|
|
|
|
it('graph.getList', async () => {
|
|
const bobList = await agent.api.app.bsky.graph.list.create(
|
|
{ repo: bob },
|
|
{
|
|
name: 'bob mutes',
|
|
purpose: 'app.bsky.graph.defs#modlist',
|
|
description: "bob's list of mutes",
|
|
createdAt: new Date().toISOString(),
|
|
},
|
|
sc.getHeaders(bob),
|
|
)
|
|
listUri = bobList.uri.toString()
|
|
await agent.api.app.bsky.graph.list.create(
|
|
{ repo: bob },
|
|
{
|
|
name: 'another list',
|
|
purpose: 'app.bsky.graph.defs#modlist',
|
|
description: 'a second list',
|
|
createdAt: new Date().toISOString(),
|
|
},
|
|
sc.getHeaders(bob),
|
|
)
|
|
await agent.api.app.bsky.graph.listitem.create(
|
|
{ repo: bob },
|
|
{
|
|
subject: alice,
|
|
list: listUri,
|
|
createdAt: new Date().toISOString(),
|
|
},
|
|
sc.getHeaders(bob),
|
|
)
|
|
await agent.api.app.bsky.graph.listitem.create(
|
|
{ repo: bob },
|
|
{
|
|
subject: carol,
|
|
list: listUri,
|
|
createdAt: new Date().toISOString(),
|
|
},
|
|
sc.getHeaders(bob),
|
|
)
|
|
await network.processAll()
|
|
|
|
const res = await agent.api.app.bsky.graph.getList(
|
|
{ list: listUri },
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect(forSnapshot(res.data)).toMatchSnapshot()
|
|
const pt1 = await agent.api.app.bsky.graph.getList(
|
|
{
|
|
list: listUri,
|
|
limit: 1,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
const pt2 = await agent.api.app.bsky.graph.getList(
|
|
{
|
|
list: listUri,
|
|
cursor: pt1.data.cursor,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect([...pt1.data.items, ...pt2.data.items]).toEqual(res.data.items)
|
|
})
|
|
|
|
it('graph.getLists', async () => {
|
|
const res = await agent.api.app.bsky.graph.getLists(
|
|
{ actor: bob },
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect(forSnapshot(res.data)).toMatchSnapshot()
|
|
const pt1 = await agent.api.app.bsky.graph.getLists(
|
|
{
|
|
actor: bob,
|
|
limit: 1,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
const pt2 = await agent.api.app.bsky.graph.getLists(
|
|
{
|
|
actor: bob,
|
|
cursor: pt1.data.cursor,
|
|
},
|
|
{
|
|
headers: { ...sc.getHeaders(alice) },
|
|
},
|
|
)
|
|
expect([...pt1.data.lists, ...pt2.data.lists]).toEqual(res.data.lists)
|
|
})
|
|
|
|
it('graph.getListBlocks', async () => {
|
|
await agent.api.app.bsky.graph.listblock.create(
|
|
{ repo: bob },
|
|
{
|
|
subject: listUri,
|
|
createdAt: new Date().toISOString(),
|
|
},
|
|
sc.getHeaders(bob),
|
|
)
|
|
await network.processAll()
|
|
const pt1 = await agent.api.app.bsky.graph.getListBlocks(
|
|
{},
|
|
{ headers: sc.getHeaders(bob) },
|
|
)
|
|
expect(forSnapshot(pt1.data)).toMatchSnapshot()
|
|
const pt2 = await agent.api.app.bsky.graph.getListBlocks(
|
|
{ cursor: pt1.data.cursor },
|
|
{ headers: sc.getHeaders(bob) },
|
|
)
|
|
expect(pt2.data.lists).toEqual([])
|
|
expect(pt2.data.cursor).not.toBeDefined()
|
|
})
|
|
})
|