Refactor @atproto/api to the AtpAgent
interface (#529)
* Refactor @atproto/api to use the simplified AtpAgent API * xrpc package: Export the defaultFetchHandler to reuse in api * api package: Use the defaultFetchHandler defined in xrpc * Update all usages of the api for the new AtpAgent * Clear promise on thrown codepath * Avoid updating the atpagent session until ready to return
This commit is contained in:
parent
0c58a937c9
commit
2242e8a313
packages
api
dev-env/src
lex-cli/src/codegen
pds/tests
account-deletion.test.tsaccount.test.tsauth.test.tscrud.test.ts
event-stream
file-uploads.test.tsmigrations
moderation.test.tsseeds
server.test.tssync.test.tsviews
admin
get-moderation-action.test.tsget-moderation-actions.test.tsget-moderation-report.test.tsget-moderation-reports.test.tsget-record.test.tsget-repo.test.tsrepo-search.test.ts
author-feed.test.tsfollows.test.tsmutes.test.tsnotifications.test.tsprofile.test.tsreposts.test.tssuggestions.test.tsthread.test.tstimeline.test.tsuser-search.test.tsvotes.test.tsxrpc/src
@ -3,12 +3,50 @@
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import API from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
|
||||
const client = API.service('http://example.com')
|
||||
const agent = new AtpAgent({service: 'https://example.com'})
|
||||
|
||||
// provide a custom fetch implementation (shouldnt be needed in node or the browser)
|
||||
import {AtpAgentFetchHeaders, AtpAgentFetchHandlerResponse} from '@atproto/api'
|
||||
AtpAgent.configure({
|
||||
async fetch(
|
||||
httpUri: string,
|
||||
httpMethod: string,
|
||||
httpHeaders: AtpAgentFetchHeaders,
|
||||
httpReqBody: any,
|
||||
): Promise<AtpAgentFetchHandlerResponse> {
|
||||
// insert definition here...
|
||||
return {status: 200, /*...*/}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Session management
|
||||
|
||||
```typescript
|
||||
import AtpAgent, {AtpSessionEvent, AtpSessionData} from '@atproto/api'
|
||||
const agent = new AtpAgent({
|
||||
service: 'https://example.com',
|
||||
persistSession: (evt: AtpSessionEvent, sess?: AtpSessionData) {
|
||||
// store the session-data for reuse
|
||||
}
|
||||
})
|
||||
|
||||
await agent.login({identifier: 'alice@mail.com', password: 'hunter2'})
|
||||
await agent.resumeSession(savedSessionData)
|
||||
await agent.createAccount({
|
||||
email: 'alice@mail.com',
|
||||
password: 'hunter2',
|
||||
handle: 'alice.example.com'
|
||||
})
|
||||
```
|
||||
|
||||
### API calls
|
||||
|
||||
```typescript
|
||||
// xrpc methods
|
||||
const res1 = await client.com.atproto.repo.createRecord(
|
||||
const res1 = await agent.api.com.atproto.repo.createRecord(
|
||||
{
|
||||
did: alice.did,
|
||||
collection: 'app.bsky.feed.post',
|
||||
@ -19,14 +57,14 @@ const res1 = await client.com.atproto.repo.createRecord(
|
||||
}
|
||||
}
|
||||
)
|
||||
const res2 = await client.com.atproto.repo.listRecords({did: alice.did, type: 'app.bsky.feed.post'})
|
||||
const res2 = await agent.api.com.atproto.repo.listRecords({did: alice.did, type: 'app.bsky.feed.post'})
|
||||
|
||||
// repo record methods
|
||||
const res3 = await client.app.bsky.feed.post.create({did: alice.did}, {
|
||||
const res3 = await agent.api.app.bsky.feed.post.create({did: alice.did}, {
|
||||
text: 'Hello, world!',
|
||||
createdAt: (new Date()).toISOString()
|
||||
})
|
||||
const res4 = await client.app.bsky.feed.post.list({did: alice.did})
|
||||
const res4 = await agent.api.app.bsky.feed.post.list({did: alice.did})
|
||||
```
|
||||
|
||||
## License
|
||||
|
305
packages/api/src/agent.ts
Normal file
305
packages/api/src/agent.ts
Normal file
@ -0,0 +1,305 @@
|
||||
import { ErrorResponseBody, errorResponseBody } from '@atproto/xrpc'
|
||||
import { defaultFetchHandler } from '@atproto/xrpc'
|
||||
import {
|
||||
AtpBaseClient,
|
||||
AtpServiceClient,
|
||||
ComAtprotoAccountCreate,
|
||||
ComAtprotoSessionCreate,
|
||||
ComAtprotoSessionGet,
|
||||
ComAtprotoSessionRefresh,
|
||||
} from './client'
|
||||
import {
|
||||
AtpSessionData,
|
||||
AtpAgentCreateAccountOpts,
|
||||
AtpAgentLoginOpts,
|
||||
AptAgentFetchHandler,
|
||||
AtpAgentFetchHandlerResponse,
|
||||
AtpAgentGlobalOpts,
|
||||
AtpPersistSessionHandler,
|
||||
AtpAgentOpts,
|
||||
} from './types'
|
||||
|
||||
const REFRESH_SESSION = 'com.atproto.session.refresh'
|
||||
|
||||
/**
|
||||
* An ATP "Agent"
|
||||
* Manages session token lifecycles and provides convenience methods.
|
||||
*/
|
||||
export class AtpAgent {
|
||||
service: URL
|
||||
api: AtpServiceClient
|
||||
session?: AtpSessionData
|
||||
|
||||
private _baseClient: AtpBaseClient
|
||||
private _persistSession?: AtpPersistSessionHandler
|
||||
private _refreshSessionPromise: Promise<void> | undefined
|
||||
|
||||
/**
|
||||
* The `fetch` implementation; must be implemented for your platform.
|
||||
*/
|
||||
static fetch: AptAgentFetchHandler | undefined = defaultFetchHandler
|
||||
|
||||
/**
|
||||
* Configures the API globally.
|
||||
*/
|
||||
static configure(opts: AtpAgentGlobalOpts) {
|
||||
AtpAgent.fetch = opts.fetch
|
||||
}
|
||||
|
||||
constructor(opts: AtpAgentOpts) {
|
||||
this.service =
|
||||
opts.service instanceof URL ? opts.service : new URL(opts.service)
|
||||
this._persistSession = opts.persistSession
|
||||
|
||||
// create an ATP client instance for this agent
|
||||
this._baseClient = new AtpBaseClient()
|
||||
this._baseClient.xrpc.fetch = this._fetch.bind(this) // patch its fetch implementation
|
||||
this.api = this._baseClient.service(opts.service)
|
||||
}
|
||||
|
||||
/**
|
||||
* Is there any active session?
|
||||
*/
|
||||
get hasSession() {
|
||||
return !!this.session
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the "Persist Session" method which can be used to store access tokens
|
||||
* as they change.
|
||||
*/
|
||||
setPersistSessionHandler(handler?: AtpPersistSessionHandler) {
|
||||
this._persistSession = handler
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new account and hydrate its session in this agent.
|
||||
*/
|
||||
async createAccount(
|
||||
opts: AtpAgentCreateAccountOpts,
|
||||
): Promise<ComAtprotoAccountCreate.Response> {
|
||||
try {
|
||||
const res = await this.api.com.atproto.account.create({
|
||||
handle: opts.handle,
|
||||
password: opts.password,
|
||||
email: opts.email,
|
||||
inviteCode: opts.inviteCode,
|
||||
})
|
||||
this.session = {
|
||||
accessJwt: res.data.accessJwt,
|
||||
refreshJwt: res.data.refreshJwt,
|
||||
handle: res.data.handle,
|
||||
did: res.data.did,
|
||||
}
|
||||
return res
|
||||
} catch (e) {
|
||||
this.session = undefined
|
||||
throw e
|
||||
} finally {
|
||||
if (this.session) {
|
||||
this._persistSession?.('create', this.session)
|
||||
} else {
|
||||
this._persistSession?.('create-failed', undefined)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new session with this agent.
|
||||
*/
|
||||
async login(
|
||||
opts: AtpAgentLoginOpts,
|
||||
): Promise<ComAtprotoSessionCreate.Response> {
|
||||
try {
|
||||
const res = await this.api.com.atproto.session.create({
|
||||
identifier: opts.identifier,
|
||||
password: opts.password,
|
||||
})
|
||||
this.session = {
|
||||
accessJwt: res.data.accessJwt,
|
||||
refreshJwt: res.data.refreshJwt,
|
||||
handle: res.data.handle,
|
||||
did: res.data.did,
|
||||
}
|
||||
return res
|
||||
} catch (e) {
|
||||
this.session = undefined
|
||||
throw e
|
||||
} finally {
|
||||
if (this.session) {
|
||||
this._persistSession?.('create', this.session)
|
||||
} else {
|
||||
this._persistSession?.('create-failed', undefined)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume a pre-existing session with this agent.
|
||||
*/
|
||||
async resumeSession(
|
||||
session: AtpSessionData,
|
||||
): Promise<ComAtprotoSessionGet.Response> {
|
||||
try {
|
||||
this.session = session
|
||||
const res = await this.api.com.atproto.session.get()
|
||||
if (!res.success || res.data.did !== this.session.did) {
|
||||
throw new Error('Invalid session')
|
||||
}
|
||||
return res
|
||||
} catch (e) {
|
||||
this.session = undefined
|
||||
throw e
|
||||
} finally {
|
||||
if (this.session) {
|
||||
this._persistSession?.('create', this.session)
|
||||
} else {
|
||||
this._persistSession?.('create-failed', undefined)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal helper to add authorization headers to requests.
|
||||
*/
|
||||
private _addAuthHeader(reqHeaders: Record<string, string>) {
|
||||
if (!reqHeaders.authorization && this.session?.accessJwt) {
|
||||
return {
|
||||
...reqHeaders,
|
||||
authorization: `Bearer ${this.session.accessJwt}`,
|
||||
}
|
||||
}
|
||||
return reqHeaders
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal fetch handler which adds access-token management
|
||||
*/
|
||||
private async _fetch(
|
||||
reqUri: string,
|
||||
reqMethod: string,
|
||||
reqHeaders: Record<string, string>,
|
||||
reqBody: any,
|
||||
): Promise<AtpAgentFetchHandlerResponse> {
|
||||
if (!AtpAgent.fetch) {
|
||||
throw new Error('AtpAgent fetch() method not configured')
|
||||
}
|
||||
|
||||
// wait for any active session-refreshes to finish
|
||||
await this._refreshSessionPromise
|
||||
|
||||
// send the request
|
||||
let res = await AtpAgent.fetch(
|
||||
reqUri,
|
||||
reqMethod,
|
||||
this._addAuthHeader(reqHeaders),
|
||||
reqBody,
|
||||
)
|
||||
|
||||
// handle session-refreshes as needed
|
||||
if (isErrorResponse(res, ['ExpiredToken']) && this.session?.refreshJwt) {
|
||||
// attempt refresh
|
||||
await this._refreshSession()
|
||||
|
||||
// resend the request with the new access token
|
||||
res = await AtpAgent.fetch(
|
||||
reqUri,
|
||||
reqMethod,
|
||||
this._addAuthHeader(reqHeaders),
|
||||
reqBody,
|
||||
)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal helper to refresh sessions
|
||||
* - Wraps the actual implementation in a promise-guard to ensure only
|
||||
* one refresh is attempted at a time.
|
||||
*/
|
||||
private async _refreshSession() {
|
||||
if (this._refreshSessionPromise) {
|
||||
return this._refreshSessionPromise
|
||||
}
|
||||
this._refreshSessionPromise = this._refreshSessionInner()
|
||||
try {
|
||||
await this._refreshSessionPromise
|
||||
} finally {
|
||||
this._refreshSessionPromise = undefined
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal helper to refresh sessions (actual behavior)
|
||||
*/
|
||||
private async _refreshSessionInner() {
|
||||
if (!AtpAgent.fetch) {
|
||||
throw new Error('AtpAgent fetch() method not configured')
|
||||
}
|
||||
if (!this.session?.refreshJwt) {
|
||||
return
|
||||
}
|
||||
|
||||
// send the refresh request
|
||||
const url = new URL(this.service.origin)
|
||||
url.pathname = `/xrpc/${REFRESH_SESSION}`
|
||||
const res = await AtpAgent.fetch(
|
||||
url.toString(),
|
||||
'POST',
|
||||
{
|
||||
authorization: `Bearer ${this.session.refreshJwt}`,
|
||||
},
|
||||
undefined,
|
||||
)
|
||||
|
||||
if (isErrorResponse(res, ['ExpiredToken', 'InvalidToken'])) {
|
||||
// failed due to a bad refresh token
|
||||
this.session = undefined
|
||||
this._persistSession?.('expired', undefined)
|
||||
} else if (isNewSessionObject(this._baseClient, res.body)) {
|
||||
// succeeded, update the session
|
||||
this.session = {
|
||||
accessJwt: res.body.accessJwt,
|
||||
refreshJwt: res.body.refreshJwt,
|
||||
handle: res.body.handle,
|
||||
did: res.body.did,
|
||||
}
|
||||
this._persistSession?.('update', this.session)
|
||||
}
|
||||
// else: other failures should be ignored - the issue will
|
||||
// propagate in the _fetch() handler's second attempt to run
|
||||
// the request
|
||||
}
|
||||
}
|
||||
|
||||
function isErrorObject(v: unknown): v is ErrorResponseBody {
|
||||
return errorResponseBody.safeParse(v).success
|
||||
}
|
||||
|
||||
function isErrorResponse(
|
||||
res: AtpAgentFetchHandlerResponse,
|
||||
errorNames: string[],
|
||||
): boolean {
|
||||
if (res.status !== 400) {
|
||||
return false
|
||||
}
|
||||
if (!isErrorObject(res.body)) {
|
||||
return false
|
||||
}
|
||||
return (
|
||||
typeof res.body.error === 'string' && errorNames.includes(res.body.error)
|
||||
)
|
||||
}
|
||||
|
||||
function isNewSessionObject(
|
||||
client: AtpBaseClient,
|
||||
v: unknown,
|
||||
): v is ComAtprotoSessionRefresh.OutputSchema {
|
||||
try {
|
||||
client.xrpc.lex.assertValidXrpcOutput('com.atproto.session.refresh', v)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
@ -189,28 +189,25 @@ export const APP_BSKY_SYSTEM = {
|
||||
ActorUser: 'app.bsky.system.actorUser',
|
||||
}
|
||||
|
||||
export class Client {
|
||||
export class AtpBaseClient {
|
||||
xrpc: XrpcClient = new XrpcClient()
|
||||
|
||||
constructor() {
|
||||
this.xrpc.addLexicons(schemas)
|
||||
}
|
||||
|
||||
service(serviceUri: string | URL): ServiceClient {
|
||||
return new ServiceClient(this, this.xrpc.service(serviceUri))
|
||||
service(serviceUri: string | URL): AtpServiceClient {
|
||||
return new AtpServiceClient(this, this.xrpc.service(serviceUri))
|
||||
}
|
||||
}
|
||||
|
||||
const defaultInst = new Client()
|
||||
export default defaultInst
|
||||
|
||||
export class ServiceClient {
|
||||
_baseClient: Client
|
||||
export class AtpServiceClient {
|
||||
_baseClient: AtpBaseClient
|
||||
xrpc: XrpcServiceClient
|
||||
com: ComNS
|
||||
app: AppNS
|
||||
|
||||
constructor(baseClient: Client, xrpcService: XrpcServiceClient) {
|
||||
constructor(baseClient: AtpBaseClient, xrpcService: XrpcServiceClient) {
|
||||
this._baseClient = baseClient
|
||||
this.xrpc = xrpcService
|
||||
this.com = new ComNS(this)
|
||||
@ -223,17 +220,17 @@ export class ServiceClient {
|
||||
}
|
||||
|
||||
export class ComNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
atproto: AtprotoNS
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
this.atproto = new AtprotoNS(service)
|
||||
}
|
||||
}
|
||||
|
||||
export class AtprotoNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
account: AccountNS
|
||||
admin: AdminNS
|
||||
blob: BlobNS
|
||||
@ -244,7 +241,7 @@ export class AtprotoNS {
|
||||
session: SessionNS
|
||||
sync: SyncNS
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
this.account = new AccountNS(service)
|
||||
this.admin = new AdminNS(service)
|
||||
@ -259,9 +256,9 @@ export class AtprotoNS {
|
||||
}
|
||||
|
||||
export class AccountNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
|
||||
@ -344,9 +341,9 @@ export class AccountNS {
|
||||
}
|
||||
|
||||
export class AdminNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
|
||||
@ -462,9 +459,9 @@ export class AdminNS {
|
||||
}
|
||||
|
||||
export class BlobNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
|
||||
@ -481,9 +478,9 @@ export class BlobNS {
|
||||
}
|
||||
|
||||
export class HandleNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
|
||||
@ -500,9 +497,9 @@ export class HandleNS {
|
||||
}
|
||||
|
||||
export class RepoNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
|
||||
@ -585,9 +582,9 @@ export class RepoNS {
|
||||
}
|
||||
|
||||
export class ReportNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
|
||||
@ -604,9 +601,9 @@ export class ReportNS {
|
||||
}
|
||||
|
||||
export class ServerNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
|
||||
@ -623,9 +620,9 @@ export class ServerNS {
|
||||
}
|
||||
|
||||
export class SessionNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
|
||||
@ -675,9 +672,9 @@ export class SessionNS {
|
||||
}
|
||||
|
||||
export class SyncNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
|
||||
@ -738,17 +735,17 @@ export class SyncNS {
|
||||
}
|
||||
|
||||
export class AppNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
bsky: BskyNS
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
this.bsky = new BskyNS(service)
|
||||
}
|
||||
}
|
||||
|
||||
export class BskyNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
actor: ActorNS
|
||||
embed: EmbedNS
|
||||
feed: FeedNS
|
||||
@ -756,7 +753,7 @@ export class BskyNS {
|
||||
notification: NotificationNS
|
||||
system: SystemNS
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
this.actor = new ActorNS(service)
|
||||
this.embed = new EmbedNS(service)
|
||||
@ -768,10 +765,10 @@ export class BskyNS {
|
||||
}
|
||||
|
||||
export class ActorNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
profile: ProfileRecord
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
this.profile = new ProfileRecord(service)
|
||||
}
|
||||
@ -833,9 +830,9 @@ export class ActorNS {
|
||||
}
|
||||
|
||||
export class ProfileRecord {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
|
||||
@ -894,20 +891,20 @@ export class ProfileRecord {
|
||||
}
|
||||
|
||||
export class EmbedNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
}
|
||||
|
||||
export class FeedNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
post: PostRecord
|
||||
repost: RepostRecord
|
||||
vote: VoteRecord
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
this.post = new PostRecord(service)
|
||||
this.repost = new RepostRecord(service)
|
||||
@ -982,9 +979,9 @@ export class FeedNS {
|
||||
}
|
||||
|
||||
export class PostRecord {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
|
||||
@ -1043,9 +1040,9 @@ export class PostRecord {
|
||||
}
|
||||
|
||||
export class RepostRecord {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
|
||||
@ -1104,9 +1101,9 @@ export class RepostRecord {
|
||||
}
|
||||
|
||||
export class VoteRecord {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
|
||||
@ -1165,12 +1162,12 @@ export class VoteRecord {
|
||||
}
|
||||
|
||||
export class GraphNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
assertion: AssertionRecord
|
||||
confirmation: ConfirmationRecord
|
||||
follow: FollowRecord
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
this.assertion = new AssertionRecord(service)
|
||||
this.confirmation = new ConfirmationRecord(service)
|
||||
@ -1234,9 +1231,9 @@ export class GraphNS {
|
||||
}
|
||||
|
||||
export class AssertionRecord {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
|
||||
@ -1299,9 +1296,9 @@ export class AssertionRecord {
|
||||
}
|
||||
|
||||
export class ConfirmationRecord {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
|
||||
@ -1364,9 +1361,9 @@ export class ConfirmationRecord {
|
||||
}
|
||||
|
||||
export class FollowRecord {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
|
||||
@ -1425,9 +1422,9 @@ export class FollowRecord {
|
||||
}
|
||||
|
||||
export class NotificationNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
|
||||
@ -1466,19 +1463,19 @@ export class NotificationNS {
|
||||
}
|
||||
|
||||
export class SystemNS {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
declaration: DeclarationRecord
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
this.declaration = new DeclarationRecord(service)
|
||||
}
|
||||
}
|
||||
|
||||
export class DeclarationRecord {
|
||||
_service: ServiceClient
|
||||
_service: AtpServiceClient
|
||||
|
||||
constructor(service: ServiceClient) {
|
||||
constructor(service: AtpServiceClient) {
|
||||
this._service = service
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
export * from './types'
|
||||
export * from './client'
|
||||
export { default } from './client'
|
||||
export * from './session'
|
||||
export { default as sessionClient } from './session'
|
||||
export * from './agent'
|
||||
export { AtpAgent as default } from './agent'
|
||||
|
@ -1,194 +0,0 @@
|
||||
import {
|
||||
CallOptions,
|
||||
Client as XrpcClient,
|
||||
ServiceClient as XrpcServiceClient,
|
||||
QueryParams,
|
||||
ResponseType,
|
||||
XRPCError,
|
||||
XRPCResponse,
|
||||
} from '@atproto/xrpc'
|
||||
import EventEmitter from 'events'
|
||||
import TypedEmitter from 'typed-emitter'
|
||||
import { Client, ServiceClient } from './client'
|
||||
import * as CreateSession from './client/types/com/atproto/session/create'
|
||||
import * as RefreshSession from './client/types/com/atproto/session/refresh'
|
||||
import * as CreateAccount from './client/types/com/atproto/session/create'
|
||||
|
||||
const CREATE_SESSION = 'com.atproto.session.create'
|
||||
const REFRESH_SESSION = 'com.atproto.session.refresh'
|
||||
const DELETE_SESSION = 'com.atproto.session.delete'
|
||||
const CREATE_ACCOUNT = 'com.atproto.account.create'
|
||||
|
||||
export class SessionClient extends Client {
|
||||
service(serviceUri: string | URL): SessionServiceClient {
|
||||
const xrpcService = new SessionXrpcServiceClient(this.xrpc, serviceUri)
|
||||
return new SessionServiceClient(this, xrpcService)
|
||||
}
|
||||
}
|
||||
|
||||
const defaultInst = new SessionClient()
|
||||
export default defaultInst
|
||||
|
||||
export class SessionServiceClient extends ServiceClient {
|
||||
xrpc: SessionXrpcServiceClient
|
||||
sessionManager: SessionManager
|
||||
constructor(baseClient: Client, xrpcService: SessionXrpcServiceClient) {
|
||||
super(baseClient, xrpcService)
|
||||
this.sessionManager = this.xrpc.sessionManager
|
||||
}
|
||||
}
|
||||
|
||||
export class SessionXrpcServiceClient extends XrpcServiceClient {
|
||||
sessionManager = new SessionManager()
|
||||
refreshing?: Promise<XRPCResponse>
|
||||
|
||||
constructor(baseClient: XrpcClient, serviceUri: string | URL) {
|
||||
super(baseClient, serviceUri)
|
||||
this.sessionManager.on('session', () => {
|
||||
// Maintain access token headers when session changes
|
||||
const accessHeaders = this.sessionManager.accessHeaders()
|
||||
if (accessHeaders) {
|
||||
this.setHeader('authorization', accessHeaders.authorization)
|
||||
} else {
|
||||
this.unsetHeader('authorization')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async call(
|
||||
methodNsid: string,
|
||||
params?: QueryParams,
|
||||
data?: unknown,
|
||||
opts?: CallOptions,
|
||||
) {
|
||||
const original = (overrideOpts?: CallOptions) =>
|
||||
super.call(methodNsid, params, data, overrideOpts ?? opts)
|
||||
|
||||
// If someone is setting credentials manually, pass through as an escape hatch
|
||||
if (opts?.headers?.authorization) {
|
||||
return await original()
|
||||
}
|
||||
|
||||
// Manage concurrent refreshes on session refresh
|
||||
if (methodNsid === REFRESH_SESSION) {
|
||||
return await this.refresh(opts)
|
||||
}
|
||||
|
||||
// Complete any pending session refresh and then continue onto the original request with fresh credentials
|
||||
await this.refreshing
|
||||
|
||||
// Setup session on session or account creation
|
||||
if (methodNsid === CREATE_SESSION || methodNsid === CREATE_ACCOUNT) {
|
||||
const result = await original()
|
||||
const { accessJwt, refreshJwt } =
|
||||
result.data as CreateSession.OutputSchema & CreateAccount.OutputSchema
|
||||
this.sessionManager.set({ accessJwt, refreshJwt })
|
||||
return result
|
||||
}
|
||||
|
||||
// Clear session on session deletion
|
||||
if (methodNsid === DELETE_SESSION) {
|
||||
const result = await original({
|
||||
...opts,
|
||||
headers: {
|
||||
...opts?.headers,
|
||||
...this.sessionManager.refreshHeaders(),
|
||||
},
|
||||
})
|
||||
this.sessionManager.unset()
|
||||
return result
|
||||
}
|
||||
|
||||
// For all other requests, if failed due to an expired token, refresh and retry with fresh credentials
|
||||
try {
|
||||
return await original()
|
||||
} catch (err) {
|
||||
if (
|
||||
err instanceof XRPCError &&
|
||||
err.status === ResponseType.InvalidRequest &&
|
||||
err.error === 'ExpiredToken' &&
|
||||
this.sessionManager.active()
|
||||
) {
|
||||
await this.refresh(opts)
|
||||
return await original()
|
||||
}
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Ensures a single refresh request at a time, deduping concurrent requests.
|
||||
async refresh(opts?: CallOptions) {
|
||||
this.refreshing ??= this._refresh(opts)
|
||||
try {
|
||||
return await this.refreshing
|
||||
} finally {
|
||||
this.refreshing = undefined
|
||||
}
|
||||
}
|
||||
|
||||
private async _refresh(opts?: CallOptions) {
|
||||
try {
|
||||
const result = await super.call(REFRESH_SESSION, undefined, undefined, {
|
||||
...opts,
|
||||
headers: {
|
||||
...opts?.headers,
|
||||
...this.sessionManager.refreshHeaders(),
|
||||
},
|
||||
})
|
||||
const { accessJwt, refreshJwt } =
|
||||
result.data as RefreshSession.OutputSchema
|
||||
this.sessionManager.set({ accessJwt, refreshJwt })
|
||||
return result
|
||||
} catch (err) {
|
||||
if (
|
||||
err instanceof XRPCError &&
|
||||
err.status === ResponseType.InvalidRequest &&
|
||||
(err.error === 'ExpiredToken' || err.error === 'InvalidToken')
|
||||
) {
|
||||
this.sessionManager.unset()
|
||||
}
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SessionManager extends (EventEmitter as new () => TypedEmitter<SessionEvents>) {
|
||||
session?: Session
|
||||
get() {
|
||||
return this.session
|
||||
}
|
||||
set(session: Session) {
|
||||
this.session = session
|
||||
this.emit('session', session)
|
||||
}
|
||||
unset() {
|
||||
this.session = undefined
|
||||
this.emit('session', undefined)
|
||||
}
|
||||
active() {
|
||||
return !!this.session
|
||||
}
|
||||
accessHeaders() {
|
||||
return (
|
||||
this.session && {
|
||||
authorization: `Bearer ${this.session.accessJwt}`,
|
||||
}
|
||||
)
|
||||
}
|
||||
refreshHeaders() {
|
||||
return (
|
||||
this.session && {
|
||||
authorization: `Bearer ${this.session.refreshJwt}`,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export type Session = {
|
||||
refreshJwt: string
|
||||
accessJwt: string
|
||||
}
|
||||
|
||||
type SessionEvents = {
|
||||
session: (session?: Session) => void
|
||||
}
|
71
packages/api/src/types.ts
Normal file
71
packages/api/src/types.ts
Normal file
@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Used by the PersistSessionHandler to indicate what change occurred
|
||||
*/
|
||||
export type AtpSessionEvent = 'create' | 'create-failed' | 'update' | 'expired'
|
||||
|
||||
/**
|
||||
* Used by AtpAgent to store active sessions
|
||||
*/
|
||||
export interface AtpSessionData {
|
||||
refreshJwt: string
|
||||
accessJwt: string
|
||||
handle: string
|
||||
did: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler signature passed to AtpAgent to store session data
|
||||
*/
|
||||
export type AtpPersistSessionHandler = (
|
||||
evt: AtpSessionEvent,
|
||||
session: AtpSessionData | undefined,
|
||||
) => void | Promise<void>
|
||||
|
||||
/**
|
||||
* AtpAgent constructor() opts
|
||||
*/
|
||||
export interface AtpAgentOpts {
|
||||
service: string | URL
|
||||
persistSession?: AtpPersistSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* AtpAgent createAccount() opts
|
||||
*/
|
||||
export interface AtpAgentCreateAccountOpts {
|
||||
email: string
|
||||
password: string
|
||||
handle: string
|
||||
inviteCode?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* AtpAgent login() opts
|
||||
*/
|
||||
export interface AtpAgentLoginOpts {
|
||||
identifier: string
|
||||
password: string
|
||||
}
|
||||
|
||||
/**
|
||||
* AtpAgent global fetch handler
|
||||
*/
|
||||
type AtpAgentFetchHeaders = Record<string, string>
|
||||
export interface AtpAgentFetchHandlerResponse {
|
||||
status: number
|
||||
headers: Record<string, string>
|
||||
body: any
|
||||
}
|
||||
export type AptAgentFetchHandler = (
|
||||
httpUri: string,
|
||||
httpMethod: string,
|
||||
httpHeaders: AtpAgentFetchHeaders,
|
||||
httpReqBody: any,
|
||||
) => Promise<AtpAgentFetchHandlerResponse>
|
||||
|
||||
/**
|
||||
* AtpAgent global config opts
|
||||
*/
|
||||
export interface AtpAgentGlobalOpts {
|
||||
fetch: AptAgentFetchHandler
|
||||
}
|
26
packages/api/tests/_util.ts
Normal file
26
packages/api/tests/_util.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { AtpAgentFetchHandlerResponse } from '..'
|
||||
|
||||
export async function fetchHandler(
|
||||
httpUri: string,
|
||||
httpMethod: string,
|
||||
httpHeaders: Record<string, string>,
|
||||
httpReqBody: unknown,
|
||||
): Promise<AtpAgentFetchHandlerResponse> {
|
||||
// The duplex field is now required for streaming bodies, but not yet reflected
|
||||
// anywhere in docs or types. See whatwg/fetch#1438, nodejs/node#46221.
|
||||
const reqInit: RequestInit & { duplex: string } = {
|
||||
method: httpMethod,
|
||||
headers: httpHeaders,
|
||||
body: httpReqBody
|
||||
? new TextEncoder().encode(JSON.stringify(httpReqBody))
|
||||
: undefined,
|
||||
duplex: 'half',
|
||||
}
|
||||
const res = await fetch(httpUri, reqInit)
|
||||
const resBody = await res.arrayBuffer()
|
||||
return {
|
||||
status: res.status,
|
||||
headers: Object.fromEntries(res.headers.entries()),
|
||||
body: resBody ? JSON.parse(new TextDecoder().decode(resBody)) : undefined,
|
||||
}
|
||||
}
|
391
packages/api/tests/agent.test.ts
Normal file
391
packages/api/tests/agent.test.ts
Normal file
@ -0,0 +1,391 @@
|
||||
import { defaultFetchHandler } from '@atproto/xrpc'
|
||||
import {
|
||||
CloseFn,
|
||||
runTestServer,
|
||||
TestServerInfo,
|
||||
} from '@atproto/pds/tests/_util'
|
||||
import {
|
||||
AtpAgent,
|
||||
AtpAgentFetchHandlerResponse,
|
||||
AtpSessionEvent,
|
||||
AtpSessionData,
|
||||
} from '..'
|
||||
|
||||
describe('agent', () => {
|
||||
let server: TestServerInfo
|
||||
let close: CloseFn
|
||||
|
||||
beforeAll(async () => {
|
||||
server = await runTestServer({
|
||||
dbPostgresSchema: 'session',
|
||||
})
|
||||
close = server.close
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await close()
|
||||
})
|
||||
|
||||
it('creates a new session on account creation.', async () => {
|
||||
const events: string[] = []
|
||||
const sessions: (AtpSessionData | undefined)[] = []
|
||||
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
||||
events.push(evt)
|
||||
sessions.push(sess)
|
||||
}
|
||||
|
||||
const agent = new AtpAgent({ service: server.url, persistSession })
|
||||
|
||||
const res = await agent.createAccount({
|
||||
handle: 'user1.test',
|
||||
email: 'user1@test.com',
|
||||
password: 'password',
|
||||
})
|
||||
|
||||
expect(agent.hasSession).toEqual(true)
|
||||
expect(agent.session?.accessJwt).toEqual(res.data.accessJwt)
|
||||
expect(agent.session?.refreshJwt).toEqual(res.data.refreshJwt)
|
||||
expect(agent.session?.handle).toEqual(res.data.handle)
|
||||
expect(agent.session?.did).toEqual(res.data.did)
|
||||
|
||||
const { data: sessionInfo } = await agent.api.com.atproto.session.get({})
|
||||
expect(sessionInfo).toEqual({
|
||||
did: res.data.did,
|
||||
handle: res.data.handle,
|
||||
})
|
||||
|
||||
expect(events.length).toEqual(1)
|
||||
expect(events[0]).toEqual('create')
|
||||
expect(sessions.length).toEqual(1)
|
||||
expect(sessions[0]?.accessJwt).toEqual(agent.session?.accessJwt)
|
||||
})
|
||||
|
||||
it('creates a new session on login.', async () => {
|
||||
const events: string[] = []
|
||||
const sessions: (AtpSessionData | undefined)[] = []
|
||||
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
||||
events.push(evt)
|
||||
sessions.push(sess)
|
||||
}
|
||||
|
||||
const agent1 = new AtpAgent({ service: server.url, persistSession })
|
||||
|
||||
await agent1.createAccount({
|
||||
handle: 'user2.test',
|
||||
email: 'user2@test.com',
|
||||
password: 'password',
|
||||
})
|
||||
|
||||
const agent2 = new AtpAgent({ service: server.url, persistSession })
|
||||
const res1 = await agent2.login({
|
||||
identifier: 'user2.test',
|
||||
password: 'password',
|
||||
})
|
||||
|
||||
expect(agent2.hasSession).toEqual(true)
|
||||
expect(agent2.session?.accessJwt).toEqual(res1.data.accessJwt)
|
||||
expect(agent2.session?.refreshJwt).toEqual(res1.data.refreshJwt)
|
||||
expect(agent2.session?.handle).toEqual(res1.data.handle)
|
||||
expect(agent2.session?.did).toEqual(res1.data.did)
|
||||
|
||||
const { data: sessionInfo } = await agent2.api.com.atproto.session.get({})
|
||||
expect(sessionInfo).toEqual({
|
||||
did: res1.data.did,
|
||||
handle: res1.data.handle,
|
||||
})
|
||||
|
||||
expect(events.length).toEqual(2)
|
||||
expect(events[0]).toEqual('create')
|
||||
expect(events[1]).toEqual('create')
|
||||
expect(sessions.length).toEqual(2)
|
||||
expect(sessions[0]?.accessJwt).toEqual(agent1.session?.accessJwt)
|
||||
expect(sessions[1]?.accessJwt).toEqual(agent2.session?.accessJwt)
|
||||
})
|
||||
|
||||
it('resumes an existing session.', async () => {
|
||||
const events: string[] = []
|
||||
const sessions: (AtpSessionData | undefined)[] = []
|
||||
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
||||
events.push(evt)
|
||||
sessions.push(sess)
|
||||
}
|
||||
|
||||
const agent1 = new AtpAgent({ service: server.url, persistSession })
|
||||
|
||||
await agent1.createAccount({
|
||||
handle: 'user3.test',
|
||||
email: 'user3@test.com',
|
||||
password: 'password',
|
||||
})
|
||||
if (!agent1.session) {
|
||||
throw new Error('No session created')
|
||||
}
|
||||
|
||||
const agent2 = new AtpAgent({ service: server.url, persistSession })
|
||||
const res1 = await agent2.resumeSession(agent1.session)
|
||||
|
||||
expect(agent2.hasSession).toEqual(true)
|
||||
expect(agent2.session?.handle).toEqual(res1.data.handle)
|
||||
expect(agent2.session?.did).toEqual(res1.data.did)
|
||||
|
||||
const { data: sessionInfo } = await agent2.api.com.atproto.session.get({})
|
||||
expect(sessionInfo).toEqual({
|
||||
did: res1.data.did,
|
||||
handle: res1.data.handle,
|
||||
})
|
||||
|
||||
expect(events.length).toEqual(2)
|
||||
expect(events[0]).toEqual('create')
|
||||
expect(events[1]).toEqual('create')
|
||||
expect(sessions.length).toEqual(2)
|
||||
expect(sessions[0]?.accessJwt).toEqual(agent1.session?.accessJwt)
|
||||
expect(sessions[1]?.accessJwt).toEqual(agent2.session?.accessJwt)
|
||||
})
|
||||
|
||||
it('refreshes existing session.', async () => {
|
||||
const events: string[] = []
|
||||
const sessions: (AtpSessionData | undefined)[] = []
|
||||
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
||||
events.push(evt)
|
||||
sessions.push(sess)
|
||||
}
|
||||
|
||||
const agent = new AtpAgent({ service: server.url, persistSession })
|
||||
|
||||
// create an account and a session with it
|
||||
await agent.createAccount({
|
||||
handle: 'user4.test',
|
||||
email: 'user4@test.com',
|
||||
password: 'password',
|
||||
})
|
||||
if (!agent.session) {
|
||||
throw new Error('No session created')
|
||||
}
|
||||
const session1 = agent.session
|
||||
const origAccessJwt = session1.accessJwt
|
||||
|
||||
// wait 1 second so that a token refresh will issue a new access token
|
||||
// (if the timestamp, which has 1 second resolution, is the same -- then the access token won't change)
|
||||
await new Promise((r) => setTimeout(r, 1000))
|
||||
|
||||
// patch the fetch handler to fake an expired token error on the next request
|
||||
const tokenExpiredFetchHandler = async function (
|
||||
httpUri: string,
|
||||
httpMethod: string,
|
||||
httpHeaders: Record<string, string>,
|
||||
httpReqBody: unknown,
|
||||
): Promise<AtpAgentFetchHandlerResponse> {
|
||||
if (httpHeaders.authorization === `Bearer ${origAccessJwt}`) {
|
||||
return {
|
||||
status: 400,
|
||||
headers: {},
|
||||
body: { error: 'ExpiredToken' },
|
||||
}
|
||||
}
|
||||
return defaultFetchHandler(httpUri, httpMethod, httpHeaders, httpReqBody)
|
||||
}
|
||||
|
||||
// put the agent through the auth flow
|
||||
AtpAgent.configure({ fetch: tokenExpiredFetchHandler })
|
||||
const res1 = await agent.api.app.bsky.feed.getTimeline()
|
||||
AtpAgent.configure({ fetch: defaultFetchHandler })
|
||||
|
||||
expect(res1.success).toEqual(true)
|
||||
expect(agent.hasSession).toEqual(true)
|
||||
expect(agent.session?.accessJwt).not.toEqual(session1.accessJwt)
|
||||
expect(agent.session?.refreshJwt).not.toEqual(session1.refreshJwt)
|
||||
expect(agent.session?.handle).toEqual(session1.handle)
|
||||
expect(agent.session?.did).toEqual(session1.did)
|
||||
|
||||
expect(events.length).toEqual(2)
|
||||
expect(events[0]).toEqual('create')
|
||||
expect(events[1]).toEqual('update')
|
||||
expect(sessions.length).toEqual(2)
|
||||
expect(sessions[0]?.accessJwt).toEqual(origAccessJwt)
|
||||
expect(sessions[1]?.accessJwt).toEqual(agent.session?.accessJwt)
|
||||
})
|
||||
|
||||
it('dedupes session refreshes.', async () => {
|
||||
const events: string[] = []
|
||||
const sessions: (AtpSessionData | undefined)[] = []
|
||||
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
||||
events.push(evt)
|
||||
sessions.push(sess)
|
||||
}
|
||||
|
||||
const agent = new AtpAgent({ service: server.url, persistSession })
|
||||
|
||||
// create an account and a session with it
|
||||
await agent.createAccount({
|
||||
handle: 'user5.test',
|
||||
email: 'user5@test.com',
|
||||
password: 'password',
|
||||
})
|
||||
if (!agent.session) {
|
||||
throw new Error('No session created')
|
||||
}
|
||||
const session1 = agent.session
|
||||
const origAccessJwt = session1.accessJwt
|
||||
|
||||
// wait 1 second so that a token refresh will issue a new access token
|
||||
// (if the timestamp, which has 1 second resolution, is the same -- then the access token won't change)
|
||||
await new Promise((r) => setTimeout(r, 1000))
|
||||
|
||||
// patch the fetch handler to fake an expired token error on the next request
|
||||
let expiredCalls = 0
|
||||
let refreshCalls = 0
|
||||
const tokenExpiredFetchHandler = async function (
|
||||
httpUri: string,
|
||||
httpMethod: string,
|
||||
httpHeaders: Record<string, string>,
|
||||
httpReqBody: unknown,
|
||||
): Promise<AtpAgentFetchHandlerResponse> {
|
||||
if (httpHeaders.authorization === `Bearer ${origAccessJwt}`) {
|
||||
expiredCalls++
|
||||
return {
|
||||
status: 400,
|
||||
headers: {},
|
||||
body: { error: 'ExpiredToken' },
|
||||
}
|
||||
}
|
||||
if (httpUri.includes('com.atproto.session.refresh')) {
|
||||
refreshCalls++
|
||||
}
|
||||
return defaultFetchHandler(httpUri, httpMethod, httpHeaders, httpReqBody)
|
||||
}
|
||||
|
||||
// put the agent through the auth flow
|
||||
AtpAgent.configure({ fetch: tokenExpiredFetchHandler })
|
||||
const [res1, res2, res3] = await Promise.all([
|
||||
agent.api.app.bsky.feed.getTimeline(),
|
||||
agent.api.app.bsky.feed.getTimeline(),
|
||||
agent.api.app.bsky.feed.getTimeline(),
|
||||
])
|
||||
AtpAgent.configure({ fetch: defaultFetchHandler })
|
||||
|
||||
expect(expiredCalls).toEqual(3)
|
||||
expect(refreshCalls).toEqual(1)
|
||||
expect(res1.success).toEqual(true)
|
||||
expect(res2.success).toEqual(true)
|
||||
expect(res3.success).toEqual(true)
|
||||
expect(agent.hasSession).toEqual(true)
|
||||
expect(agent.session?.accessJwt).not.toEqual(session1.accessJwt)
|
||||
expect(agent.session?.refreshJwt).not.toEqual(session1.refreshJwt)
|
||||
expect(agent.session?.handle).toEqual(session1.handle)
|
||||
expect(agent.session?.did).toEqual(session1.did)
|
||||
|
||||
expect(events.length).toEqual(2)
|
||||
expect(events[0]).toEqual('create')
|
||||
expect(events[1]).toEqual('update')
|
||||
expect(sessions.length).toEqual(2)
|
||||
expect(sessions[0]?.accessJwt).toEqual(origAccessJwt)
|
||||
expect(sessions[1]?.accessJwt).toEqual(agent.session?.accessJwt)
|
||||
})
|
||||
|
||||
it('persists an empty session on login and resumeSession failures', async () => {
|
||||
const events: string[] = []
|
||||
const sessions: (AtpSessionData | undefined)[] = []
|
||||
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
||||
events.push(evt)
|
||||
sessions.push(sess)
|
||||
}
|
||||
|
||||
const agent = new AtpAgent({ service: server.url, persistSession })
|
||||
|
||||
try {
|
||||
await agent.login({
|
||||
identifier: 'baduser.test',
|
||||
password: 'password',
|
||||
})
|
||||
} catch (_e: any) {
|
||||
// ignore
|
||||
}
|
||||
expect(agent.hasSession).toEqual(false)
|
||||
|
||||
try {
|
||||
await agent.resumeSession({
|
||||
accessJwt: 'bad',
|
||||
refreshJwt: 'bad',
|
||||
did: 'bad',
|
||||
handle: 'bad',
|
||||
})
|
||||
} catch (_e: any) {
|
||||
// ignore
|
||||
}
|
||||
expect(agent.hasSession).toEqual(false)
|
||||
|
||||
expect(events.length).toEqual(2)
|
||||
expect(events[0]).toEqual('create-failed')
|
||||
expect(events[1]).toEqual('create-failed')
|
||||
expect(sessions.length).toEqual(2)
|
||||
expect(typeof sessions[0]).toEqual('undefined')
|
||||
expect(typeof sessions[1]).toEqual('undefined')
|
||||
})
|
||||
|
||||
it('does not modify or persist the session on a failed refresh', async () => {
|
||||
const events: string[] = []
|
||||
const sessions: (AtpSessionData | undefined)[] = []
|
||||
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
||||
events.push(evt)
|
||||
sessions.push(sess)
|
||||
}
|
||||
|
||||
const agent = new AtpAgent({ service: server.url, persistSession })
|
||||
|
||||
// create an account and a session with it
|
||||
await agent.createAccount({
|
||||
handle: 'user6.test',
|
||||
email: 'user6@test.com',
|
||||
password: 'password',
|
||||
})
|
||||
if (!agent.session) {
|
||||
throw new Error('No session created')
|
||||
}
|
||||
const session1 = agent.session
|
||||
const origAccessJwt = session1.accessJwt
|
||||
|
||||
// patch the fetch handler to fake an expired token error on the next request
|
||||
const tokenExpiredFetchHandler = async function (
|
||||
httpUri: string,
|
||||
httpMethod: string,
|
||||
httpHeaders: Record<string, string>,
|
||||
httpReqBody: unknown,
|
||||
): Promise<AtpAgentFetchHandlerResponse> {
|
||||
if (httpHeaders.authorization === `Bearer ${origAccessJwt}`) {
|
||||
return {
|
||||
status: 400,
|
||||
headers: {},
|
||||
body: { error: 'ExpiredToken' },
|
||||
}
|
||||
}
|
||||
if (httpUri.includes('com.atproto.session.refresh')) {
|
||||
return {
|
||||
status: 500,
|
||||
headers: {},
|
||||
body: undefined,
|
||||
}
|
||||
}
|
||||
return defaultFetchHandler(httpUri, httpMethod, httpHeaders, httpReqBody)
|
||||
}
|
||||
|
||||
// put the agent through the auth flow
|
||||
AtpAgent.configure({ fetch: tokenExpiredFetchHandler })
|
||||
try {
|
||||
await agent.api.app.bsky.feed.getTimeline()
|
||||
throw new Error('Should have failed')
|
||||
} catch (e: any) {
|
||||
// the original error passes through
|
||||
expect(e.status).toEqual(400)
|
||||
expect(e.error).toEqual('ExpiredToken')
|
||||
}
|
||||
AtpAgent.configure({ fetch: defaultFetchHandler })
|
||||
|
||||
// still has session because it wasn't invalidated
|
||||
expect(agent.hasSession).toEqual(true)
|
||||
|
||||
expect(events.length).toEqual(1)
|
||||
expect(events[0]).toEqual('create')
|
||||
expect(sessions.length).toEqual(1)
|
||||
expect(sessions[0]?.accessJwt).toEqual(origAccessJwt)
|
||||
})
|
||||
})
|
@ -3,22 +3,18 @@ import {
|
||||
runTestServer,
|
||||
TestServerInfo,
|
||||
} from '@atproto/pds/tests/_util'
|
||||
import {
|
||||
sessionClient,
|
||||
SessionServiceClient,
|
||||
ComAtprotoAccountCreate,
|
||||
} from '..'
|
||||
import { AtpAgent, ComAtprotoAccountCreate } from '..'
|
||||
|
||||
describe('errors', () => {
|
||||
let server: TestServerInfo
|
||||
let client: SessionServiceClient
|
||||
let client: AtpAgent
|
||||
let close: CloseFn
|
||||
|
||||
beforeAll(async () => {
|
||||
server = await runTestServer({
|
||||
dbPostgresSchema: 'known_errors',
|
||||
})
|
||||
client = sessionClient.service(server.url)
|
||||
client = new AtpAgent({ service: server.url })
|
||||
close = server.close
|
||||
})
|
||||
|
||||
@ -27,7 +23,7 @@ describe('errors', () => {
|
||||
})
|
||||
|
||||
it('constructs the correct error instance', async () => {
|
||||
const res = client.com.atproto.account.create({
|
||||
const res = client.api.com.atproto.account.create({
|
||||
handle: 'admin',
|
||||
email: 'admin@test.com',
|
||||
password: 'password',
|
||||
|
@ -1,239 +0,0 @@
|
||||
import {
|
||||
CloseFn,
|
||||
runTestServer,
|
||||
TestServerInfo,
|
||||
} from '@atproto/pds/tests/_util'
|
||||
import { sessionClient, Session, SessionServiceClient } from '..'
|
||||
|
||||
describe('session', () => {
|
||||
let server: TestServerInfo
|
||||
let client: SessionServiceClient
|
||||
let close: CloseFn
|
||||
|
||||
beforeAll(async () => {
|
||||
server = await runTestServer({
|
||||
dbPostgresSchema: 'session',
|
||||
})
|
||||
client = sessionClient.service(server.url)
|
||||
close = server.close
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await close()
|
||||
})
|
||||
|
||||
it('manages a new session on account creation.', async () => {
|
||||
const sessions: (Session | undefined)[] = []
|
||||
client.sessionManager.on('session', (session) => sessions.push(session))
|
||||
|
||||
const { data: account } = await client.com.atproto.account.create({
|
||||
handle: 'alice.test',
|
||||
email: 'alice@test.com',
|
||||
password: 'password',
|
||||
})
|
||||
|
||||
expect(client.sessionManager.active()).toEqual(true)
|
||||
expect(sessions).toEqual([
|
||||
{ accessJwt: account.accessJwt, refreshJwt: account.refreshJwt },
|
||||
])
|
||||
|
||||
const { data: sessionInfo } = await client.com.atproto.session.get({})
|
||||
expect(sessionInfo).toEqual({
|
||||
did: account.did,
|
||||
handle: account.handle,
|
||||
})
|
||||
})
|
||||
|
||||
it('ends a new session on session deletion.', async () => {
|
||||
const sessions: (Session | undefined)[] = []
|
||||
client.sessionManager.on('session', (session) => sessions.push(session))
|
||||
|
||||
await client.com.atproto.session.delete()
|
||||
|
||||
expect(sessions).toEqual([undefined])
|
||||
expect(client.sessionManager.active()).toEqual(false)
|
||||
|
||||
const getSessionAfterDeletion = client.com.atproto.session.get({})
|
||||
await expect(getSessionAfterDeletion).rejects.toThrow(
|
||||
'Authentication Required',
|
||||
)
|
||||
})
|
||||
|
||||
it('manages a new session on session creation.', async () => {
|
||||
const sessions: (Session | undefined)[] = []
|
||||
client.sessionManager.on('session', (session) => sessions.push(session))
|
||||
|
||||
const { data: session } = await client.com.atproto.session.create({
|
||||
handle: 'alice.test',
|
||||
password: 'password',
|
||||
})
|
||||
|
||||
expect(sessions).toEqual([
|
||||
{ accessJwt: session.accessJwt, refreshJwt: session.refreshJwt },
|
||||
])
|
||||
expect(client.sessionManager.active()).toEqual(true)
|
||||
|
||||
const { data: sessionInfo } = await client.com.atproto.session.get({})
|
||||
expect(sessionInfo).toEqual({
|
||||
did: session.did,
|
||||
handle: session.handle,
|
||||
})
|
||||
})
|
||||
|
||||
it('refreshes existing session.', async () => {
|
||||
const sessions: (Session | undefined)[] = []
|
||||
client.sessionManager.on('session', (session) => sessions.push(session))
|
||||
|
||||
const { data: session } = await client.com.atproto.session.create({
|
||||
handle: 'alice.test',
|
||||
password: 'password',
|
||||
})
|
||||
|
||||
const { data: sessionRefresh } = await client.com.atproto.session.refresh()
|
||||
|
||||
expect(sessions).toEqual([
|
||||
{ accessJwt: session.accessJwt, refreshJwt: session.refreshJwt },
|
||||
{
|
||||
accessJwt: sessionRefresh.accessJwt,
|
||||
refreshJwt: sessionRefresh.refreshJwt,
|
||||
},
|
||||
])
|
||||
expect(client.sessionManager.active()).toEqual(true)
|
||||
|
||||
const { data: sessionInfo } = await client.com.atproto.session.get({})
|
||||
expect(sessionInfo).toEqual({
|
||||
did: sessionRefresh.did,
|
||||
handle: sessionRefresh.handle,
|
||||
})
|
||||
|
||||
// Uses escape hatch: authorization set, so sessions are not managed by this call
|
||||
const refreshStaleSession = client.com.atproto.session.refresh(undefined, {
|
||||
headers: { authorization: `Bearer ${session.refreshJwt}` },
|
||||
})
|
||||
await expect(refreshStaleSession).rejects.toThrow('Token has been revoked')
|
||||
|
||||
expect(sessions.length).toEqual(2)
|
||||
expect(client.sessionManager.active()).toEqual(true)
|
||||
})
|
||||
|
||||
it('dedupes concurrent refreshes.', async () => {
|
||||
const sessions: (Session | undefined)[] = []
|
||||
client.sessionManager.on('session', (session) => sessions.push(session))
|
||||
|
||||
const { data: session } = await client.com.atproto.session.create({
|
||||
handle: 'alice.test',
|
||||
password: 'password',
|
||||
})
|
||||
|
||||
const [{ data: sessionRefresh }] = await Promise.all(
|
||||
[...Array(10)].map(() => client.com.atproto.session.refresh()),
|
||||
)
|
||||
|
||||
expect(sessions).toEqual([
|
||||
{ accessJwt: session.accessJwt, refreshJwt: session.refreshJwt },
|
||||
{
|
||||
accessJwt: sessionRefresh.accessJwt,
|
||||
refreshJwt: sessionRefresh.refreshJwt,
|
||||
},
|
||||
])
|
||||
expect(client.sessionManager.active()).toEqual(true)
|
||||
|
||||
const { data: sessionInfo } = await client.com.atproto.session.get({})
|
||||
expect(sessionInfo).toEqual({
|
||||
did: sessionRefresh.did,
|
||||
handle: sessionRefresh.handle,
|
||||
})
|
||||
})
|
||||
|
||||
it('manually sets and unsets existing session.', async () => {
|
||||
const sessions: (Session | undefined)[] = []
|
||||
client.sessionManager.on('session', (session) => sessions.push(session))
|
||||
|
||||
const { data: session } = await client.com.atproto.session.create({
|
||||
handle: 'alice.test',
|
||||
password: 'password',
|
||||
})
|
||||
const sessionCreds = {
|
||||
accessJwt: session.accessJwt,
|
||||
refreshJwt: session.refreshJwt,
|
||||
}
|
||||
expect(client.sessionManager.active()).toEqual(true)
|
||||
|
||||
client.sessionManager.unset()
|
||||
expect(client.sessionManager.active()).toEqual(false)
|
||||
|
||||
const getSessionAfterUnset = client.com.atproto.session.get({})
|
||||
await expect(getSessionAfterUnset).rejects.toThrow(
|
||||
'Authentication Required',
|
||||
)
|
||||
|
||||
client.sessionManager.set(sessionCreds)
|
||||
expect(client.sessionManager.active()).toEqual(true)
|
||||
|
||||
const { data: sessionInfo } = await client.com.atproto.session.get({})
|
||||
expect(sessionInfo).toEqual({
|
||||
did: session.did,
|
||||
handle: session.handle,
|
||||
})
|
||||
|
||||
expect(sessions).toEqual([sessionCreds, undefined, sessionCreds])
|
||||
expect(client.sessionManager.active()).toEqual(true)
|
||||
})
|
||||
|
||||
it('refreshes and retries request when access token is expired.', async () => {
|
||||
const sessions: (Session | undefined)[] = []
|
||||
client.sessionManager.on('session', (session) => sessions.push(session))
|
||||
const auth = server.ctx.auth
|
||||
|
||||
const { data: sessionInfo } = await client.com.atproto.session.get({})
|
||||
const accessExpired = await auth.createAccessToken(sessionInfo.did, -1)
|
||||
|
||||
expect(sessions.length).toEqual(0)
|
||||
expect(client.sessionManager.active()).toEqual(true)
|
||||
|
||||
client.sessionManager.set({
|
||||
refreshJwt: 'not-used-since-session-is-active',
|
||||
...client.sessionManager.get(),
|
||||
accessJwt: accessExpired.jwt,
|
||||
})
|
||||
|
||||
expect(sessions.length).toEqual(1)
|
||||
expect(client.sessionManager.active()).toEqual(true)
|
||||
|
||||
const { data: updatedSessionInfo } = await client.com.atproto.session.get(
|
||||
{},
|
||||
)
|
||||
expect(updatedSessionInfo).toEqual(sessionInfo)
|
||||
|
||||
expect(sessions.length).toEqual(2) // New session was created during session.get()
|
||||
expect(client.sessionManager.active()).toEqual(true)
|
||||
})
|
||||
|
||||
it('unsets session when refresh token becomes expired.', async () => {
|
||||
const sessions: (Session | undefined)[] = []
|
||||
client.sessionManager.on('session', (session) => sessions.push(session))
|
||||
const auth = server.ctx.auth
|
||||
|
||||
const { data: sessionInfo } = await client.com.atproto.session.get({})
|
||||
const accessExpired = await auth.createAccessToken(sessionInfo.did, -1)
|
||||
const refreshExpired = await auth.createRefreshToken(sessionInfo.did, -1)
|
||||
|
||||
expect(sessions.length).toEqual(0)
|
||||
expect(client.sessionManager.active()).toEqual(true)
|
||||
|
||||
client.sessionManager.set({
|
||||
accessJwt: accessExpired.jwt,
|
||||
refreshJwt: refreshExpired.jwt,
|
||||
})
|
||||
|
||||
expect(sessions.length).toEqual(1)
|
||||
expect(client.sessionManager.active()).toEqual(true)
|
||||
|
||||
const getSessionAfterExpired = client.com.atproto.session.get({})
|
||||
await expect(getSessionAfterExpired).rejects.toThrow('Token has expired')
|
||||
|
||||
expect(sessions.length).toEqual(2)
|
||||
expect(sessions[1]).toEqual(undefined)
|
||||
expect(client.sessionManager.active()).toEqual(false)
|
||||
})
|
||||
})
|
@ -88,7 +88,7 @@ const envApi = {
|
||||
|
||||
// create the PDS account
|
||||
const client = pds.getClient()
|
||||
const pdsRes = await client.com.atproto.account.create({
|
||||
const pdsRes = await client.api.com.atproto.account.create({
|
||||
email: handleNoTld + '@test.com',
|
||||
handle,
|
||||
password: handleNoTld + '-pass',
|
||||
|
@ -8,7 +8,7 @@ import PDSServer, {
|
||||
} from '@atproto/pds'
|
||||
import * as plc from '@atproto/plc'
|
||||
import * as crypto from '@atproto/crypto'
|
||||
import AtpApi, { ServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { ServerType, ServerConfig, StartParams } from './types.js'
|
||||
|
||||
interface Startable {
|
||||
@ -128,8 +128,8 @@ export class DevEnvServer {
|
||||
}
|
||||
}
|
||||
|
||||
getClient(): ServiceClient {
|
||||
return AtpApi.service(`http://localhost:${this.port}`)
|
||||
getClient(): AtpAgent {
|
||||
return new AtpAgent({ service: `http://localhost:${this.port}` })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { AtUri } from '@atproto/uri'
|
||||
import { ServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import {
|
||||
SPAM,
|
||||
OTHER,
|
||||
@ -54,7 +54,7 @@ export async function generateMockSetup(env: DevEnv) {
|
||||
declarationCid: string
|
||||
handle: string
|
||||
password: string
|
||||
api: ServiceClient
|
||||
agent: AtpAgent
|
||||
}
|
||||
const users: User[] = [
|
||||
{
|
||||
@ -63,7 +63,7 @@ export async function generateMockSetup(env: DevEnv) {
|
||||
declarationCid: '',
|
||||
handle: `alice.test`,
|
||||
password: 'hunter2',
|
||||
api: clients.alice,
|
||||
agent: clients.alice,
|
||||
},
|
||||
{
|
||||
email: 'bob@test.com',
|
||||
@ -71,7 +71,7 @@ export async function generateMockSetup(env: DevEnv) {
|
||||
declarationCid: '',
|
||||
handle: `bob.test`,
|
||||
password: 'hunter2',
|
||||
api: clients.bob,
|
||||
agent: clients.bob,
|
||||
},
|
||||
{
|
||||
email: 'carla@test.com',
|
||||
@ -79,7 +79,7 @@ export async function generateMockSetup(env: DevEnv) {
|
||||
declarationCid: '',
|
||||
handle: `carla.test`,
|
||||
password: 'hunter2',
|
||||
api: clients.carla,
|
||||
agent: clients.carla,
|
||||
},
|
||||
]
|
||||
const alice = users[0]
|
||||
@ -88,18 +88,18 @@ export async function generateMockSetup(env: DevEnv) {
|
||||
|
||||
let _i = 1
|
||||
for (const user of users) {
|
||||
const res = await clients.loggedout.com.atproto.account.create({
|
||||
const res = await clients.loggedout.api.com.atproto.account.create({
|
||||
email: user.email,
|
||||
handle: user.handle,
|
||||
password: user.password,
|
||||
})
|
||||
user.api.setHeader('Authorization', `Bearer ${res.data.accessJwt}`)
|
||||
const { data: profile } = await user.api.app.bsky.actor.getProfile({
|
||||
user.agent.api.setHeader('Authorization', `Bearer ${res.data.accessJwt}`)
|
||||
const { data: profile } = await user.agent.api.app.bsky.actor.getProfile({
|
||||
actor: user.handle,
|
||||
})
|
||||
user.did = res.data.did
|
||||
user.declarationCid = profile.declaration.cid
|
||||
await user.api.app.bsky.actor.profile.create(
|
||||
await user.agent.api.app.bsky.actor.profile.create(
|
||||
{ did: user.did },
|
||||
{
|
||||
displayName: ucfirst(user.handle).slice(0, -5),
|
||||
@ -110,7 +110,7 @@ export async function generateMockSetup(env: DevEnv) {
|
||||
|
||||
// Report one user
|
||||
const reporter = picka(users)
|
||||
await reporter.api.com.atproto.report.create({
|
||||
await reporter.agent.api.com.atproto.report.create({
|
||||
reasonType: picka([SPAM, OTHER]),
|
||||
reason: picka(["Didn't look right to me", undefined, undefined]),
|
||||
subject: {
|
||||
@ -121,7 +121,7 @@ export async function generateMockSetup(env: DevEnv) {
|
||||
|
||||
// everybody follows everybody
|
||||
const follow = async (author: User, subject: User) => {
|
||||
await author.api.app.bsky.graph.follow.create(
|
||||
await author.agent.api.app.bsky.graph.follow.create(
|
||||
{ did: author.did },
|
||||
{
|
||||
subject: {
|
||||
@ -143,7 +143,7 @@ export async function generateMockSetup(env: DevEnv) {
|
||||
const posts: { uri: string; cid: string }[] = []
|
||||
for (let i = 0; i < postTexts.length; i++) {
|
||||
const author = picka(users)
|
||||
const post = await author.api.app.bsky.feed.post.create(
|
||||
const post = await author.agent.api.app.bsky.feed.post.create(
|
||||
{ did: author.did },
|
||||
{
|
||||
text: postTexts[i],
|
||||
@ -153,7 +153,7 @@ export async function generateMockSetup(env: DevEnv) {
|
||||
posts.push(post)
|
||||
if (rand(10) === 0) {
|
||||
const reposter = picka(users)
|
||||
await reposter.api.app.bsky.feed.repost.create(
|
||||
await reposter.agent.api.app.bsky.feed.repost.create(
|
||||
{ did: reposter.did },
|
||||
{
|
||||
subject: picka(posts),
|
||||
@ -163,7 +163,7 @@ export async function generateMockSetup(env: DevEnv) {
|
||||
}
|
||||
if (rand(6) === 0) {
|
||||
const reporter = picka(users)
|
||||
await reporter.api.com.atproto.report.create({
|
||||
await reporter.agent.api.com.atproto.report.create({
|
||||
reasonType: picka([SPAM, OTHER]),
|
||||
reason: picka(["Didn't look right to me", undefined, undefined]),
|
||||
subject: {
|
||||
@ -178,13 +178,13 @@ export async function generateMockSetup(env: DevEnv) {
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const targetUri = picka(posts).uri
|
||||
const urip = new AtUri(targetUri)
|
||||
const target = await alice.api.app.bsky.feed.post.get({
|
||||
const target = await alice.agent.api.app.bsky.feed.post.get({
|
||||
user: urip.host,
|
||||
rkey: urip.rkey,
|
||||
})
|
||||
const author = picka(users)
|
||||
posts.push(
|
||||
await author.api.app.bsky.feed.post.create(
|
||||
await author.agent.api.app.bsky.feed.post.create(
|
||||
{ did: author.did },
|
||||
{
|
||||
text: picka(replyTexts),
|
||||
@ -202,7 +202,7 @@ export async function generateMockSetup(env: DevEnv) {
|
||||
for (const post of posts) {
|
||||
for (const user of users) {
|
||||
if (rand(3) === 0) {
|
||||
await user.api.app.bsky.feed.vote.create(
|
||||
await user.agent.api.app.bsky.feed.vote.create(
|
||||
{ did: user.did },
|
||||
{
|
||||
direction: rand(3) !== 0 ? 'up' : 'down',
|
||||
|
@ -68,7 +68,7 @@ const indexTs = (
|
||||
nsidTokens: Record<string, string[]>,
|
||||
) =>
|
||||
gen(project, '/index.ts', async (file) => {
|
||||
//= import {Client as XrpcClient, ServiceClient as XrpcServiceClient} from '@atproto/xrpc'
|
||||
//= import {Client as XrpcClient, AtpServiceClient as XrpcServiceClient} from '@atproto/xrpc'
|
||||
const xrpcImport = file.addImportDeclaration({
|
||||
moduleSpecifier: '@atproto/xrpc',
|
||||
})
|
||||
@ -116,9 +116,9 @@ const indexTs = (
|
||||
})
|
||||
}
|
||||
|
||||
//= export class Client {...}
|
||||
//= export class AtpBaseClient {...}
|
||||
const clientCls = file.addClass({
|
||||
name: 'Client',
|
||||
name: 'AtpBaseClient',
|
||||
isExported: true,
|
||||
})
|
||||
//= xrpc: XrpcClient = new XrpcClient()
|
||||
@ -131,39 +131,26 @@ const indexTs = (
|
||||
//= this.xrpc.addLexicons(schemas)
|
||||
//= }
|
||||
clientCls.addConstructor().setBodyText(`this.xrpc.addLexicons(schemas)`)
|
||||
//= service(serviceUri: string | URL): ServiceClient {
|
||||
//= return new ServiceClient(this, this.xrpc.service(serviceUri))
|
||||
//= service(serviceUri: string | URL): AtpServiceClient {
|
||||
//= return new AtpServiceClient(this, this.xrpc.service(serviceUri))
|
||||
//= }
|
||||
clientCls
|
||||
.addMethod({
|
||||
name: 'service',
|
||||
parameters: [{ name: 'serviceUri', type: 'string | URL' }],
|
||||
returnType: 'ServiceClient',
|
||||
returnType: 'AtpServiceClient',
|
||||
})
|
||||
.setBodyText(
|
||||
`return new ServiceClient(this, this.xrpc.service(serviceUri))`,
|
||||
`return new AtpServiceClient(this, this.xrpc.service(serviceUri))`,
|
||||
)
|
||||
|
||||
//= const defaultInst = new Client()
|
||||
//= export default defaultInst
|
||||
file.addVariableStatement({
|
||||
declarationKind: VariableDeclarationKind.Const,
|
||||
declarations: [
|
||||
{
|
||||
name: 'defaultInst',
|
||||
initializer: 'new Client()',
|
||||
},
|
||||
],
|
||||
})
|
||||
file.insertText(file.getFullText().length, `export default defaultInst`)
|
||||
|
||||
//= export class ServiceClient {...}
|
||||
//= export class AtpServiceClient {...}
|
||||
const serviceClientCls = file.addClass({
|
||||
name: 'ServiceClient',
|
||||
name: 'AtpServiceClient',
|
||||
isExported: true,
|
||||
})
|
||||
//= _baseClient: Client
|
||||
serviceClientCls.addProperty({ name: '_baseClient', type: 'Client' })
|
||||
//= _baseClient: AtpBaseClient
|
||||
serviceClientCls.addProperty({ name: '_baseClient', type: 'AtpBaseClient' })
|
||||
//= xrpc: XrpcServiceClient
|
||||
serviceClientCls.addProperty({
|
||||
name: 'xrpc',
|
||||
@ -176,7 +163,7 @@ const indexTs = (
|
||||
type: ns.className,
|
||||
})
|
||||
}
|
||||
//= constructor (baseClient: Client, xrpcService: XrpcServiceClient) {
|
||||
//= constructor (baseClient: AtpBaseClient, xrpcService: XrpcServiceClient) {
|
||||
//= this.baseClient = baseClient
|
||||
//= this.xrpcService = xrpcService
|
||||
//= {namespace declarations}
|
||||
@ -184,7 +171,7 @@ const indexTs = (
|
||||
serviceClientCls
|
||||
.addConstructor({
|
||||
parameters: [
|
||||
{ name: 'baseClient', type: 'Client' },
|
||||
{ name: 'baseClient', type: 'AtpBaseClient' },
|
||||
{ name: 'xrpcService', type: 'XrpcServiceClient' },
|
||||
],
|
||||
})
|
||||
@ -227,10 +214,10 @@ function genNamespaceCls(file: SourceFile, ns: DefTreeNode) {
|
||||
name: ns.className,
|
||||
isExported: true,
|
||||
})
|
||||
//= _service: ServiceClient
|
||||
//= _service: AtpServiceClient
|
||||
cls.addProperty({
|
||||
name: '_service',
|
||||
type: 'ServiceClient',
|
||||
type: 'AtpServiceClient',
|
||||
})
|
||||
|
||||
for (const userType of ns.userTypes) {
|
||||
@ -256,7 +243,7 @@ function genNamespaceCls(file: SourceFile, ns: DefTreeNode) {
|
||||
genNamespaceCls(file, child)
|
||||
}
|
||||
|
||||
//= constructor(service: ServiceClient) {
|
||||
//= constructor(service: AtpServiceClient) {
|
||||
//= this._service = service
|
||||
//= {child namespace prop declarations}
|
||||
//= {record prop declarations}
|
||||
@ -264,7 +251,7 @@ function genNamespaceCls(file: SourceFile, ns: DefTreeNode) {
|
||||
const cons = cls.addConstructor()
|
||||
cons.addParameter({
|
||||
name: 'service',
|
||||
type: 'ServiceClient',
|
||||
type: 'AtpServiceClient',
|
||||
})
|
||||
cons.setBodyText(
|
||||
[
|
||||
@ -338,19 +325,19 @@ function genRecordCls(file: SourceFile, userType: DefTreeNodeUserType) {
|
||||
name: `${toTitleCase(name)}Record`,
|
||||
isExported: true,
|
||||
})
|
||||
//= _service: ServiceClient
|
||||
//= _service: AtpServiceClient
|
||||
cls.addProperty({
|
||||
name: '_service',
|
||||
type: 'ServiceClient',
|
||||
type: 'AtpServiceClient',
|
||||
})
|
||||
|
||||
//= constructor(service: ServiceClient) {
|
||||
//= constructor(service: AtpServiceClient) {
|
||||
//= this._service = service
|
||||
//= }
|
||||
const cons = cls.addConstructor()
|
||||
cons.addParameter({
|
||||
name: 'service',
|
||||
type: 'ServiceClient',
|
||||
type: 'AtpServiceClient',
|
||||
})
|
||||
cons.setBodyText(`this._service = service`)
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { once, EventEmitter } from 'events'
|
||||
import Mail from 'nodemailer/lib/mailer'
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { SeedClient } from './seeds/client'
|
||||
import basicSeed from './seeds/basic'
|
||||
import { Database } from '../src'
|
||||
@ -25,7 +25,7 @@ import { RepoCommitBlock } from '../src/db/tables/repo-commit-block'
|
||||
import { Record } from '../src/db/tables/record'
|
||||
|
||||
describe('account deletion', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: util.CloseFn
|
||||
let sc: SeedClient
|
||||
|
||||
@ -48,8 +48,8 @@ describe('account deletion', () => {
|
||||
mailer = server.ctx.mailer
|
||||
db = server.ctx.db
|
||||
blobstore = server.ctx.blobstore
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await basicSeed(sc, server.ctx.messageQueue)
|
||||
carol = sc.accounts[sc.dids.carol]
|
||||
|
||||
@ -83,7 +83,7 @@ describe('account deletion', () => {
|
||||
|
||||
it('requests account deletion', async () => {
|
||||
const mail = await getMailFrom(
|
||||
client.com.atproto.account.requestDelete(undefined, {
|
||||
agent.api.com.atproto.account.requestDelete(undefined, {
|
||||
headers: sc.getHeaders(carol.did),
|
||||
}),
|
||||
)
|
||||
@ -98,7 +98,7 @@ describe('account deletion', () => {
|
||||
})
|
||||
|
||||
it('fails account deletion with a bad token', async () => {
|
||||
const attempt = client.com.atproto.account.delete({
|
||||
const attempt = agent.api.com.atproto.account.delete({
|
||||
token: '123456',
|
||||
did: carol.did,
|
||||
password: carol.password,
|
||||
@ -107,7 +107,7 @@ describe('account deletion', () => {
|
||||
})
|
||||
|
||||
it('fails account deletion with a bad password', async () => {
|
||||
const attempt = client.com.atproto.account.delete({
|
||||
const attempt = agent.api.com.atproto.account.delete({
|
||||
token,
|
||||
did: carol.did,
|
||||
password: 'bad-pass',
|
||||
@ -116,7 +116,7 @@ describe('account deletion', () => {
|
||||
})
|
||||
|
||||
it('deletes account with a valid token & password', async () => {
|
||||
await client.com.atproto.account.delete({
|
||||
await agent.api.com.atproto.account.delete({
|
||||
token,
|
||||
did: carol.did,
|
||||
password: carol.password,
|
||||
@ -124,7 +124,7 @@ describe('account deletion', () => {
|
||||
})
|
||||
|
||||
it('no longer lets the user log in', async () => {
|
||||
const attempt = client.com.atproto.session.create({
|
||||
const attempt = agent.api.com.atproto.session.create({
|
||||
handle: carol.handle,
|
||||
password: carol.password,
|
||||
})
|
||||
@ -205,7 +205,7 @@ describe('account deletion', () => {
|
||||
})
|
||||
|
||||
it('no longer displays the users posts in feeds', async () => {
|
||||
const feed = await client.app.bsky.feed.getTimeline(undefined, {
|
||||
const feed = await agent.api.app.bsky.feed.getTimeline(undefined, {
|
||||
headers: sc.getHeaders(sc.dids.alice),
|
||||
})
|
||||
const found = feed.data.feed.filter(
|
||||
@ -215,7 +215,7 @@ describe('account deletion', () => {
|
||||
})
|
||||
|
||||
it('removes notifications from the user', async () => {
|
||||
const notifs = await client.app.bsky.notification.list(undefined, {
|
||||
const notifs = await agent.api.app.bsky.notification.list(undefined, {
|
||||
headers: sc.getHeaders(sc.dids.alice),
|
||||
})
|
||||
const found = notifs.data.notifications.filter(
|
||||
@ -232,7 +232,7 @@ describe('account deletion', () => {
|
||||
})
|
||||
|
||||
const mail = await getMailFrom(
|
||||
client.com.atproto.account.requestDelete(undefined, {
|
||||
agent.api.com.atproto.account.requestDelete(undefined, {
|
||||
headers: sc.getHeaders(eve.did),
|
||||
}),
|
||||
)
|
||||
@ -241,7 +241,7 @@ describe('account deletion', () => {
|
||||
if (!token) {
|
||||
return expect(token).toBeDefined()
|
||||
}
|
||||
await client.com.atproto.account.delete({
|
||||
await agent.api.com.atproto.account.delete({
|
||||
token,
|
||||
did: eve.did,
|
||||
password: eve.password,
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { once, EventEmitter } from 'events'
|
||||
import AtpApi, {
|
||||
ServiceClient as AtpServiceClient,
|
||||
import AtpAgent, {
|
||||
ComAtprotoAccountCreate,
|
||||
ComAtprotoAccountResetPassword as ResetAccountPassword,
|
||||
} from '@atproto/api'
|
||||
@ -18,10 +17,10 @@ const passwordAlt = 'test456'
|
||||
const minsToMs = 60 * 1000
|
||||
|
||||
const createInviteCode = async (
|
||||
client: AtpServiceClient,
|
||||
agent: AtpAgent,
|
||||
uses: number,
|
||||
): Promise<string> => {
|
||||
const res = await client.com.atproto.account.createInviteCode(
|
||||
const res = await agent.api.com.atproto.account.createInviteCode(
|
||||
{ useCount: uses },
|
||||
{
|
||||
headers: { authorization: util.adminAuth() },
|
||||
@ -35,7 +34,7 @@ describe('account', () => {
|
||||
let serverUrl: string
|
||||
let cfg: ServerConfig
|
||||
let serverKey: string
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: util.CloseFn
|
||||
let mailer: ServerMailer
|
||||
let db: Database
|
||||
@ -55,7 +54,7 @@ describe('account', () => {
|
||||
cfg = server.ctx.cfg
|
||||
serverUrl = server.url
|
||||
serverKey = server.ctx.keypair.did()
|
||||
client = AtpApi.service(serverUrl)
|
||||
agent = new AtpAgent({ service: serverUrl })
|
||||
|
||||
// Catch emails for use in tests
|
||||
_origSendMail = mailer.transporter.sendMail
|
||||
@ -76,14 +75,14 @@ describe('account', () => {
|
||||
let inviteCode: string
|
||||
|
||||
it('creates an invite code', async () => {
|
||||
inviteCode = await createInviteCode(client, 1)
|
||||
inviteCode = await createInviteCode(agent, 1)
|
||||
const [host, code] = inviteCode.split('-')
|
||||
expect(host).toBe('pds.public.url') // Hostname of public url
|
||||
expect(code.length).toBe(5)
|
||||
})
|
||||
|
||||
it('serves the accounts system config', async () => {
|
||||
const res = await client.com.atproto.server.getAccountsConfig({})
|
||||
const res = await agent.api.com.atproto.server.getAccountsConfig({})
|
||||
expect(res.data.inviteCodeRequired).toBe(true)
|
||||
expect(res.data.availableUserDomains[0]).toBe('.test')
|
||||
expect(typeof res.data.inviteCodeRequired).toBe('boolean')
|
||||
@ -94,7 +93,7 @@ describe('account', () => {
|
||||
})
|
||||
|
||||
it('fails on invalid handles', async () => {
|
||||
const promise = client.com.atproto.account.create({
|
||||
const promise = agent.api.com.atproto.account.create({
|
||||
email: 'bad-handle@test.com',
|
||||
handle: 'did:bad-handle.test',
|
||||
password: 'asdf',
|
||||
@ -106,7 +105,7 @@ describe('account', () => {
|
||||
})
|
||||
|
||||
it('fails on bad invite code', async () => {
|
||||
const promise = client.com.atproto.account.create({
|
||||
const promise = agent.api.com.atproto.account.create({
|
||||
email,
|
||||
handle,
|
||||
password,
|
||||
@ -121,7 +120,7 @@ describe('account', () => {
|
||||
let jwt: string
|
||||
|
||||
it('creates an account', async () => {
|
||||
const res = await client.com.atproto.account.create({
|
||||
const res = await agent.api.com.atproto.account.create({
|
||||
email,
|
||||
handle,
|
||||
password,
|
||||
@ -146,9 +145,9 @@ describe('account', () => {
|
||||
})
|
||||
|
||||
it('allows a custom set recovery key', async () => {
|
||||
const inviteCode = await createInviteCode(client, 1)
|
||||
const inviteCode = await createInviteCode(agent, 1)
|
||||
const recoveryKey = (await crypto.EcdsaKeypair.create()).did()
|
||||
const res = await client.com.atproto.account.create({
|
||||
const res = await agent.api.com.atproto.account.create({
|
||||
email: 'custom-recovery@test.com',
|
||||
handle: 'custom-recovery.test',
|
||||
password: 'custom-recovery',
|
||||
@ -163,11 +162,11 @@ describe('account', () => {
|
||||
})
|
||||
|
||||
it('disallows duplicate email addresses and handles', async () => {
|
||||
const inviteCode = await createInviteCode(client, 2)
|
||||
const inviteCode = await createInviteCode(agent, 2)
|
||||
const email = 'bob@test.com'
|
||||
const handle = 'bob.test'
|
||||
const password = 'test123'
|
||||
await client.com.atproto.account.create({
|
||||
await agent.api.com.atproto.account.create({
|
||||
email,
|
||||
handle,
|
||||
password,
|
||||
@ -175,7 +174,7 @@ describe('account', () => {
|
||||
})
|
||||
|
||||
await expect(
|
||||
client.com.atproto.account.create({
|
||||
agent.api.com.atproto.account.create({
|
||||
email: email.toUpperCase(),
|
||||
handle: 'carol.test',
|
||||
password,
|
||||
@ -184,7 +183,7 @@ describe('account', () => {
|
||||
).rejects.toThrow('Email already taken: BOB@TEST.COM')
|
||||
|
||||
await expect(
|
||||
client.com.atproto.account.create({
|
||||
agent.api.com.atproto.account.create({
|
||||
email: 'carol@test.com',
|
||||
handle: handle.toUpperCase(),
|
||||
password,
|
||||
@ -194,9 +193,9 @@ describe('account', () => {
|
||||
})
|
||||
|
||||
it('disallows improperly formatted handles', async () => {
|
||||
const inviteCode = await createInviteCode(client, 1)
|
||||
const inviteCode = await createInviteCode(agent, 1)
|
||||
const tryHandle = async (handle: string) => {
|
||||
await client.com.atproto.account.create({
|
||||
await agent.api.com.atproto.account.create({
|
||||
email: 'john@test.com',
|
||||
handle,
|
||||
password: 'test123',
|
||||
@ -242,7 +241,7 @@ describe('account', () => {
|
||||
})
|
||||
|
||||
it('fails on used up invite code', async () => {
|
||||
const promise = client.com.atproto.account.create({
|
||||
const promise = agent.api.com.atproto.account.create({
|
||||
email: 'bob@test.com',
|
||||
handle: 'bob.test',
|
||||
password: 'asdf',
|
||||
@ -254,7 +253,7 @@ describe('account', () => {
|
||||
})
|
||||
|
||||
it('handles racing invite code uses', async () => {
|
||||
const inviteCode = await createInviteCode(client, 1)
|
||||
const inviteCode = await createInviteCode(agent, 1)
|
||||
const COUNT = 10
|
||||
|
||||
let successes = 0
|
||||
@ -263,7 +262,7 @@ describe('account', () => {
|
||||
for (let i = 0; i < COUNT; i++) {
|
||||
const attempt = async () => {
|
||||
try {
|
||||
await client.com.atproto.account.create({
|
||||
await agent.api.com.atproto.account.create({
|
||||
email: `user${i}@test.com`,
|
||||
handle: `user${i}.test`,
|
||||
password: `password`,
|
||||
@ -284,8 +283,8 @@ describe('account', () => {
|
||||
it('handles racing signups for same handle', async () => {
|
||||
const COUNT = 10
|
||||
|
||||
const invite1 = await createInviteCode(client, COUNT)
|
||||
const invite2 = await createInviteCode(client, COUNT)
|
||||
const invite1 = await createInviteCode(agent, COUNT)
|
||||
const invite2 = await createInviteCode(agent, COUNT)
|
||||
|
||||
let successes = 0
|
||||
let failures = 0
|
||||
@ -296,7 +295,7 @@ describe('account', () => {
|
||||
// Use two invites to ensure per-invite locking doesn't
|
||||
// give the appearance of fixing a race for handle.
|
||||
const invite = i % 2 ? invite1 : invite2
|
||||
await client.com.atproto.account.create({
|
||||
await agent.api.com.atproto.account.create({
|
||||
email: `matching@test.com`,
|
||||
handle: `matching.test`,
|
||||
password: `password`,
|
||||
@ -315,11 +314,11 @@ describe('account', () => {
|
||||
})
|
||||
|
||||
it('fails on unauthenticated requests', async () => {
|
||||
await expect(client.com.atproto.session.get({})).rejects.toThrow()
|
||||
await expect(agent.api.com.atproto.session.get({})).rejects.toThrow()
|
||||
})
|
||||
|
||||
it('logs in', async () => {
|
||||
const res = await client.com.atproto.session.create({
|
||||
const res = await agent.api.com.atproto.session.create({
|
||||
identifier: handle,
|
||||
password,
|
||||
})
|
||||
@ -330,8 +329,8 @@ describe('account', () => {
|
||||
})
|
||||
|
||||
it('can perform authenticated requests', async () => {
|
||||
client.setHeader('authorization', `Bearer ${jwt}`)
|
||||
const res = await client.com.atproto.session.get({})
|
||||
agent.api.setHeader('authorization', `Bearer ${jwt}`)
|
||||
const res = await agent.api.com.atproto.session.get({})
|
||||
expect(res.data.did).toBe(did)
|
||||
expect(res.data.handle).toBe(handle)
|
||||
})
|
||||
@ -346,7 +345,7 @@ describe('account', () => {
|
||||
|
||||
it('can reset account password', async () => {
|
||||
const mail = await getMailFrom(
|
||||
client.com.atproto.account.requestPasswordReset({ email }),
|
||||
agent.api.com.atproto.account.requestPasswordReset({ email }),
|
||||
)
|
||||
|
||||
expect(mail.to).toEqual(email)
|
||||
@ -359,18 +358,18 @@ describe('account', () => {
|
||||
return expect(token).toBeDefined()
|
||||
}
|
||||
|
||||
await client.com.atproto.account.resetPassword({
|
||||
await agent.api.com.atproto.account.resetPassword({
|
||||
token,
|
||||
password: passwordAlt,
|
||||
})
|
||||
|
||||
// Logs in with new password and not previous password
|
||||
await expect(
|
||||
client.com.atproto.session.create({ identifier: handle, password }),
|
||||
agent.api.com.atproto.session.create({ identifier: handle, password }),
|
||||
).rejects.toThrow('Invalid identifier or password')
|
||||
|
||||
await expect(
|
||||
client.com.atproto.session.create({
|
||||
agent.api.com.atproto.session.create({
|
||||
identifier: handle,
|
||||
password: passwordAlt,
|
||||
}),
|
||||
@ -379,7 +378,7 @@ describe('account', () => {
|
||||
|
||||
it('allows only single-use of password reset token', async () => {
|
||||
const mail = await getMailFrom(
|
||||
client.com.atproto.account.requestPasswordReset({ email }),
|
||||
agent.api.com.atproto.account.requestPasswordReset({ email }),
|
||||
)
|
||||
|
||||
const token = getTokenFromMail(mail)
|
||||
@ -389,28 +388,28 @@ describe('account', () => {
|
||||
}
|
||||
|
||||
// Reset back from passwordAlt to password
|
||||
await client.com.atproto.account.resetPassword({ token, password })
|
||||
await agent.api.com.atproto.account.resetPassword({ token, password })
|
||||
|
||||
// Reuse of token fails
|
||||
await expect(
|
||||
client.com.atproto.account.resetPassword({ token, password }),
|
||||
agent.api.com.atproto.account.resetPassword({ token, password }),
|
||||
).rejects.toThrow(ResetAccountPassword.InvalidTokenError)
|
||||
|
||||
// Logs in with new password and not previous password
|
||||
await expect(
|
||||
client.com.atproto.session.create({
|
||||
agent.api.com.atproto.session.create({
|
||||
identifier: handle,
|
||||
password: passwordAlt,
|
||||
}),
|
||||
).rejects.toThrow('Invalid identifier or password')
|
||||
|
||||
await expect(
|
||||
client.com.atproto.session.create({ identifier: handle, password }),
|
||||
agent.api.com.atproto.session.create({ identifier: handle, password }),
|
||||
).resolves.toBeDefined()
|
||||
})
|
||||
|
||||
it('allows only unexpired password reset tokens', async () => {
|
||||
await client.com.atproto.account.requestPasswordReset({ email })
|
||||
await agent.api.com.atproto.account.requestPasswordReset({ email })
|
||||
|
||||
const user = await db.db
|
||||
.updateTable('user')
|
||||
@ -428,7 +427,7 @@ describe('account', () => {
|
||||
|
||||
// Use of expired token fails
|
||||
await expect(
|
||||
client.com.atproto.account.resetPassword({
|
||||
agent.api.com.atproto.account.resetPassword({
|
||||
token: user.passwordResetToken,
|
||||
password: passwordAlt,
|
||||
}),
|
||||
@ -436,25 +435,25 @@ describe('account', () => {
|
||||
|
||||
// Still logs in with previous password
|
||||
await expect(
|
||||
client.com.atproto.session.create({
|
||||
agent.api.com.atproto.session.create({
|
||||
identifier: handle,
|
||||
password: passwordAlt,
|
||||
}),
|
||||
).rejects.toThrow('Invalid identifier or password')
|
||||
|
||||
await expect(
|
||||
client.com.atproto.session.create({ identifier: handle, password }),
|
||||
agent.api.com.atproto.session.create({ identifier: handle, password }),
|
||||
).resolves.toBeDefined()
|
||||
})
|
||||
|
||||
it('resolves did for account', async () => {
|
||||
const resolved = await client.com.atproto.handle.resolve({ handle })
|
||||
const resolved = await agent.api.com.atproto.handle.resolve({ handle })
|
||||
expect(resolved.data).toEqual({ did })
|
||||
})
|
||||
|
||||
it('resolves did for server', async () => {
|
||||
const resolvedImplicit = await client.com.atproto.handle.resolve({})
|
||||
const resolvedExplicit = await client.com.atproto.handle.resolve({
|
||||
const resolvedImplicit = await agent.api.com.atproto.handle.resolve({})
|
||||
const resolvedExplicit = await agent.api.com.atproto.handle.resolve({
|
||||
handle: 'pds.public.url',
|
||||
})
|
||||
expect(resolvedImplicit.data).toEqual({ did: cfg.serverDid })
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/moderationAction'
|
||||
import * as CreateSession from '@atproto/api/src/client/types/com/atproto/session/create'
|
||||
import * as RefreshSession from '@atproto/api/src/client/types/com/atproto/session/refresh'
|
||||
@ -7,14 +7,14 @@ import { adminAuth, CloseFn, runTestServer, TestServerInfo } from './_util'
|
||||
|
||||
describe('auth', () => {
|
||||
let server: TestServerInfo
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
|
||||
beforeAll(async () => {
|
||||
server = await runTestServer({
|
||||
dbPostgresSchema: 'auth',
|
||||
})
|
||||
client = AtpApi.service(server.url)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
close = server.close
|
||||
})
|
||||
|
||||
@ -23,11 +23,11 @@ describe('auth', () => {
|
||||
})
|
||||
|
||||
const createAccount = async (info) => {
|
||||
const { data } = await client.com.atproto.account.create(info)
|
||||
const { data } = await agent.api.com.atproto.account.create(info)
|
||||
return data
|
||||
}
|
||||
const getSession = async (jwt) => {
|
||||
const { data } = await client.com.atproto.session.get(
|
||||
const { data } = await agent.api.com.atproto.session.get(
|
||||
{},
|
||||
{
|
||||
headers: SeedClient.getHeaders(jwt),
|
||||
@ -36,16 +36,16 @@ describe('auth', () => {
|
||||
return data
|
||||
}
|
||||
const createSession = async (info) => {
|
||||
const { data } = await client.com.atproto.session.create(info)
|
||||
const { data } = await agent.api.com.atproto.session.create(info)
|
||||
return data
|
||||
}
|
||||
const deleteSession = async (jwt) => {
|
||||
await client.com.atproto.session.delete(undefined, {
|
||||
await agent.api.com.atproto.session.delete(undefined, {
|
||||
headers: SeedClient.getHeaders(jwt),
|
||||
})
|
||||
}
|
||||
const refreshSession = async (jwt) => {
|
||||
const { data } = await client.com.atproto.session.refresh(undefined, {
|
||||
const { data } = await agent.api.com.atproto.session.refresh(undefined, {
|
||||
headers: SeedClient.getHeaders(jwt),
|
||||
})
|
||||
return data
|
||||
@ -198,7 +198,7 @@ describe('auth', () => {
|
||||
email: 'iris@test.com',
|
||||
password: 'password',
|
||||
})
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -224,7 +224,7 @@ describe('auth', () => {
|
||||
email: 'jared@test.com',
|
||||
password: 'password',
|
||||
})
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import fs from 'fs/promises'
|
||||
import { CID } from 'multiformats/cid'
|
||||
import { AtUri } from '@atproto/uri'
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import * as Post from '../src/lexicon/types/app/bsky/feed/post'
|
||||
import { adminAuth, CloseFn, paginateAll, runTestServer } from './_util'
|
||||
import { BlobNotFoundError } from '@atproto/repo'
|
||||
@ -23,8 +23,8 @@ const bob = {
|
||||
|
||||
describe('crud operations', () => {
|
||||
let ctx: AppContext
|
||||
let client: AtpServiceClient
|
||||
let aliceClient: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let aliceAgent: AtpAgent
|
||||
let close: CloseFn
|
||||
|
||||
beforeAll(async () => {
|
||||
@ -33,8 +33,8 @@ describe('crud operations', () => {
|
||||
})
|
||||
ctx = server.ctx
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
aliceClient = AtpApi.service(server.url)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
aliceAgent = new AtpAgent({ service: server.url })
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
@ -42,14 +42,14 @@ describe('crud operations', () => {
|
||||
})
|
||||
|
||||
it('registers users', async () => {
|
||||
const res = await client.com.atproto.account.create({
|
||||
const res = await agent.api.com.atproto.account.create({
|
||||
email: alice.email,
|
||||
handle: alice.handle,
|
||||
password: alice.password,
|
||||
})
|
||||
aliceClient.setHeader('authorization', `Bearer ${res.data.accessJwt}`)
|
||||
aliceAgent.api.setHeader('authorization', `Bearer ${res.data.accessJwt}`)
|
||||
alice.did = res.data.did
|
||||
const res2 = await client.com.atproto.account.create({
|
||||
const res2 = await agent.api.com.atproto.account.create({
|
||||
email: bob.email,
|
||||
handle: bob.handle,
|
||||
password: bob.password,
|
||||
@ -58,12 +58,12 @@ describe('crud operations', () => {
|
||||
})
|
||||
|
||||
it('describes repo', async () => {
|
||||
const description = await client.com.atproto.repo.describe({
|
||||
const description = await agent.api.com.atproto.repo.describe({
|
||||
user: alice.did,
|
||||
})
|
||||
expect(description.data.handle).toBe(alice.handle)
|
||||
expect(description.data.did).toBe(alice.did)
|
||||
const description2 = await client.com.atproto.repo.describe({
|
||||
const description2 = await agent.api.com.atproto.repo.describe({
|
||||
user: bob.did,
|
||||
})
|
||||
expect(description2.data.handle).toBe(bob.handle)
|
||||
@ -72,7 +72,7 @@ describe('crud operations', () => {
|
||||
|
||||
let uri: AtUri
|
||||
it('creates records', async () => {
|
||||
const res = await aliceClient.com.atproto.repo.createRecord({
|
||||
const res = await aliceAgent.api.com.atproto.repo.createRecord({
|
||||
did: alice.did,
|
||||
collection: 'app.bsky.feed.post',
|
||||
record: {
|
||||
@ -88,7 +88,7 @@ describe('crud operations', () => {
|
||||
})
|
||||
|
||||
it('lists records', async () => {
|
||||
const res1 = await client.com.atproto.repo.listRecords({
|
||||
const res1 = await agent.api.com.atproto.repo.listRecords({
|
||||
user: alice.did,
|
||||
collection: 'app.bsky.feed.post',
|
||||
})
|
||||
@ -98,7 +98,7 @@ describe('crud operations', () => {
|
||||
'Hello, world!',
|
||||
)
|
||||
|
||||
const res2 = await client.app.bsky.feed.post.list({
|
||||
const res2 = await agent.api.app.bsky.feed.post.list({
|
||||
user: alice.did,
|
||||
})
|
||||
expect(res2.records.length).toBe(1)
|
||||
@ -107,7 +107,7 @@ describe('crud operations', () => {
|
||||
})
|
||||
|
||||
it('gets records', async () => {
|
||||
const res1 = await client.com.atproto.repo.getRecord({
|
||||
const res1 = await agent.api.com.atproto.repo.getRecord({
|
||||
user: alice.did,
|
||||
collection: 'app.bsky.feed.post',
|
||||
rkey: uri.rkey,
|
||||
@ -115,7 +115,7 @@ describe('crud operations', () => {
|
||||
expect(res1.data.uri).toBe(uri.toString())
|
||||
expect((res1.data.value as Post.Record).text).toBe('Hello, world!')
|
||||
|
||||
const res2 = await client.app.bsky.feed.post.get({
|
||||
const res2 = await agent.api.app.bsky.feed.post.get({
|
||||
user: alice.did,
|
||||
rkey: uri.rkey,
|
||||
})
|
||||
@ -124,12 +124,12 @@ describe('crud operations', () => {
|
||||
})
|
||||
|
||||
it('deletes records', async () => {
|
||||
await aliceClient.com.atproto.repo.deleteRecord({
|
||||
await aliceAgent.api.com.atproto.repo.deleteRecord({
|
||||
did: alice.did,
|
||||
collection: 'app.bsky.feed.post',
|
||||
rkey: uri.rkey,
|
||||
})
|
||||
const res1 = await client.com.atproto.repo.listRecords({
|
||||
const res1 = await agent.api.com.atproto.repo.listRecords({
|
||||
user: alice.did,
|
||||
collection: 'app.bsky.feed.post',
|
||||
})
|
||||
@ -137,7 +137,7 @@ describe('crud operations', () => {
|
||||
})
|
||||
|
||||
it('CRUDs records with the semantic sugars', async () => {
|
||||
const res1 = await aliceClient.app.bsky.feed.post.create(
|
||||
const res1 = await aliceAgent.api.app.bsky.feed.post.create(
|
||||
{ did: alice.did },
|
||||
{
|
||||
$type: 'app.bsky.feed.post',
|
||||
@ -147,17 +147,17 @@ describe('crud operations', () => {
|
||||
)
|
||||
const uri = new AtUri(res1.uri)
|
||||
|
||||
const res2 = await client.app.bsky.feed.post.list({
|
||||
const res2 = await agent.api.app.bsky.feed.post.list({
|
||||
user: alice.did,
|
||||
})
|
||||
expect(res2.records.length).toBe(1)
|
||||
|
||||
await aliceClient.app.bsky.feed.post.delete({
|
||||
await aliceAgent.api.app.bsky.feed.post.delete({
|
||||
did: alice.did,
|
||||
rkey: uri.rkey,
|
||||
})
|
||||
|
||||
const res3 = await client.app.bsky.feed.post.list({
|
||||
const res3 = await agent.api.app.bsky.feed.post.list({
|
||||
user: alice.did,
|
||||
})
|
||||
expect(res3.records.length).toBe(0)
|
||||
@ -167,7 +167,7 @@ describe('crud operations', () => {
|
||||
const file = await fs.readFile(
|
||||
'tests/image/fixtures/key-landscape-small.jpg',
|
||||
)
|
||||
const { data: image } = await aliceClient.com.atproto.blob.upload(file, {
|
||||
const { data: image } = await aliceAgent.api.com.atproto.blob.upload(file, {
|
||||
encoding: 'image/jpeg',
|
||||
})
|
||||
const imageCid = CID.parse(image.cid)
|
||||
@ -176,7 +176,7 @@ describe('crud operations', () => {
|
||||
BlobNotFoundError,
|
||||
)
|
||||
// Associate image with post, image should be placed in blobstore
|
||||
const res = await aliceClient.app.bsky.feed.post.create(
|
||||
const res = await aliceAgent.api.app.bsky.feed.post.create(
|
||||
{ did: alice.did },
|
||||
{
|
||||
$type: 'app.bsky.feed.post',
|
||||
@ -192,7 +192,7 @@ describe('crud operations', () => {
|
||||
)
|
||||
// Ensure image is on post record
|
||||
const postUri = new AtUri(res.uri)
|
||||
const post = await aliceClient.app.bsky.feed.post.get({
|
||||
const post = await aliceAgent.api.app.bsky.feed.post.get({
|
||||
rkey: postUri.rkey,
|
||||
user: alice.did,
|
||||
})
|
||||
@ -202,14 +202,14 @@ describe('crud operations', () => {
|
||||
// Ensure that the uploaded image is now in the blobstore, i.e. doesn't throw BlobNotFoundError
|
||||
await ctx.blobstore.getBytes(imageCid)
|
||||
// Cleanup
|
||||
await aliceClient.app.bsky.feed.post.delete({
|
||||
await aliceAgent.api.app.bsky.feed.post.delete({
|
||||
rkey: postUri.rkey,
|
||||
did: alice.did,
|
||||
})
|
||||
})
|
||||
|
||||
it('creates records with the correct key described by the schema', async () => {
|
||||
const res1 = await aliceClient.app.bsky.actor.profile.create(
|
||||
const res1 = await aliceAgent.api.app.bsky.actor.profile.create(
|
||||
{ did: alice.did },
|
||||
{
|
||||
displayName: 'alice',
|
||||
@ -230,7 +230,7 @@ describe('crud operations', () => {
|
||||
}
|
||||
const responses = await Promise.all(
|
||||
postTexts.map((text) =>
|
||||
aliceClient.app.bsky.feed.post.create(
|
||||
aliceAgent.api.app.bsky.feed.post.create(
|
||||
{ did: alice.did },
|
||||
{
|
||||
text,
|
||||
@ -244,7 +244,7 @@ describe('crud operations', () => {
|
||||
|
||||
for (let i = 0; i < uris.length; i++) {
|
||||
const uri = uris[i]
|
||||
const got = await aliceClient.com.atproto.repo.getRecord({
|
||||
const got = await aliceAgent.api.com.atproto.repo.getRecord({
|
||||
user: alice.did,
|
||||
collection: uri.collection,
|
||||
rkey: uri.rkey,
|
||||
@ -257,7 +257,7 @@ describe('crud operations', () => {
|
||||
it('handles races on del', async () => {
|
||||
await Promise.all(
|
||||
uris.map((uri) =>
|
||||
aliceClient.app.bsky.feed.post.delete({
|
||||
aliceAgent.api.app.bsky.feed.post.delete({
|
||||
did: alice.did,
|
||||
rkey: uri.rkey,
|
||||
}),
|
||||
@ -265,7 +265,7 @@ describe('crud operations', () => {
|
||||
)
|
||||
for (const uri of uris) {
|
||||
await expect(
|
||||
aliceClient.com.atproto.repo.getRecord({
|
||||
aliceAgent.api.com.atproto.repo.getRecord({
|
||||
user: alice.did,
|
||||
collection: uri.collection,
|
||||
rkey: uri.rkey,
|
||||
@ -284,7 +284,7 @@ describe('crud operations', () => {
|
||||
|
||||
beforeAll(async () => {
|
||||
const createPost = async (text: string) => {
|
||||
const res = await aliceClient.app.bsky.feed.post.create(
|
||||
const res = await aliceAgent.api.app.bsky.feed.post.create(
|
||||
{ did: alice.did },
|
||||
{
|
||||
$type: 'app.bsky.feed.post',
|
||||
@ -304,7 +304,7 @@ describe('crud operations', () => {
|
||||
afterAll(async () => {
|
||||
await Promise.all(
|
||||
[uri1, uri2, uri3, uri4, uri5].map((uri) =>
|
||||
aliceClient.app.bsky.feed.post.delete({
|
||||
aliceAgent.api.app.bsky.feed.post.delete({
|
||||
did: alice.did,
|
||||
rkey: uri.rkey,
|
||||
}),
|
||||
@ -315,7 +315,7 @@ describe('crud operations', () => {
|
||||
it('in forwards order', async () => {
|
||||
const results = (results) => results.flatMap((res) => res.records)
|
||||
const paginator = async (cursor?: string) => {
|
||||
const res = await client.app.bsky.feed.post.list({
|
||||
const res = await agent.api.app.bsky.feed.post.list({
|
||||
user: alice.did,
|
||||
before: cursor,
|
||||
limit: 2,
|
||||
@ -328,7 +328,7 @@ describe('crud operations', () => {
|
||||
expect(res.records.length).toBeLessThanOrEqual(2),
|
||||
)
|
||||
|
||||
const full = await client.app.bsky.feed.post.list({
|
||||
const full = await agent.api.app.bsky.feed.post.list({
|
||||
user: alice.did,
|
||||
})
|
||||
|
||||
@ -339,7 +339,7 @@ describe('crud operations', () => {
|
||||
it('in reverse order', async () => {
|
||||
const results = (results) => results.flatMap((res) => res.records)
|
||||
const paginator = async (cursor?: string) => {
|
||||
const res = await client.app.bsky.feed.post.list({
|
||||
const res = await agent.api.app.bsky.feed.post.list({
|
||||
user: alice.did,
|
||||
reverse: true,
|
||||
after: cursor,
|
||||
@ -353,7 +353,7 @@ describe('crud operations', () => {
|
||||
expect(res.records.length).toBeLessThanOrEqual(2),
|
||||
)
|
||||
|
||||
const full = await client.app.bsky.feed.post.list({
|
||||
const full = await agent.api.app.bsky.feed.post.list({
|
||||
user: alice.did,
|
||||
reverse: true,
|
||||
})
|
||||
@ -363,7 +363,7 @@ describe('crud operations', () => {
|
||||
})
|
||||
|
||||
it('between two records', async () => {
|
||||
const list = await client.app.bsky.feed.post.list({
|
||||
const list = await agent.api.app.bsky.feed.post.list({
|
||||
user: alice.did,
|
||||
after: uri1.rkey,
|
||||
before: uri5.rkey,
|
||||
@ -375,10 +375,10 @@ describe('crud operations', () => {
|
||||
})
|
||||
|
||||
it('reverses', async () => {
|
||||
const forwards = await client.app.bsky.feed.post.list({
|
||||
const forwards = await agent.api.app.bsky.feed.post.list({
|
||||
user: alice.did,
|
||||
})
|
||||
const reverse = await client.app.bsky.feed.post.list({
|
||||
const reverse = await agent.api.app.bsky.feed.post.list({
|
||||
user: alice.did,
|
||||
reverse: true,
|
||||
})
|
||||
@ -394,7 +394,7 @@ describe('crud operations', () => {
|
||||
// --------------
|
||||
|
||||
it('defaults an undefined $type on records', async () => {
|
||||
const res = await aliceClient.com.atproto.repo.createRecord({
|
||||
const res = await aliceAgent.api.com.atproto.repo.createRecord({
|
||||
did: alice.did,
|
||||
collection: 'app.bsky.feed.post',
|
||||
record: {
|
||||
@ -403,7 +403,7 @@ describe('crud operations', () => {
|
||||
},
|
||||
})
|
||||
const uri = new AtUri(res.data.uri)
|
||||
const got = await client.com.atproto.repo.getRecord({
|
||||
const got = await agent.api.com.atproto.repo.getRecord({
|
||||
user: alice.did,
|
||||
collection: uri.collection,
|
||||
rkey: uri.rkey,
|
||||
@ -412,7 +412,7 @@ describe('crud operations', () => {
|
||||
})
|
||||
|
||||
it('requires the schema to be known if validating', async () => {
|
||||
const prom = aliceClient.com.atproto.repo.createRecord({
|
||||
const prom = aliceAgent.api.com.atproto.repo.createRecord({
|
||||
did: alice.did,
|
||||
collection: 'com.example.foobar',
|
||||
record: { $type: 'com.example.foobar' },
|
||||
@ -424,7 +424,7 @@ describe('crud operations', () => {
|
||||
|
||||
it('requires the $type to match the schema', async () => {
|
||||
await expect(
|
||||
aliceClient.com.atproto.repo.createRecord({
|
||||
aliceAgent.api.com.atproto.repo.createRecord({
|
||||
did: alice.did,
|
||||
collection: 'app.bsky.feed.post',
|
||||
record: { $type: 'app.bsky.feed.vote' },
|
||||
@ -436,7 +436,7 @@ describe('crud operations', () => {
|
||||
|
||||
it('validates the record on write', async () => {
|
||||
await expect(
|
||||
aliceClient.com.atproto.repo.createRecord({
|
||||
aliceAgent.api.com.atproto.repo.createRecord({
|
||||
did: alice.did,
|
||||
collection: 'app.bsky.feed.post',
|
||||
record: { $type: 'app.bsky.feed.post' },
|
||||
@ -450,7 +450,7 @@ describe('crud operations', () => {
|
||||
// --------------
|
||||
|
||||
it("doesn't serve taken-down record", async () => {
|
||||
const created = await aliceClient.app.bsky.feed.post.create(
|
||||
const created = await aliceAgent.api.app.bsky.feed.post.create(
|
||||
{ did: alice.did },
|
||||
{
|
||||
$type: 'app.bsky.feed.post',
|
||||
@ -459,15 +459,15 @@ describe('crud operations', () => {
|
||||
},
|
||||
)
|
||||
const postUri = new AtUri(created.uri)
|
||||
const post = await client.app.bsky.feed.post.get({
|
||||
const post = await agent.api.app.bsky.feed.post.get({
|
||||
user: alice.did,
|
||||
rkey: postUri.rkey,
|
||||
})
|
||||
const posts = await client.app.bsky.feed.post.list({ user: alice.did })
|
||||
const posts = await agent.api.app.bsky.feed.post.list({ user: alice.did })
|
||||
expect(posts.records.map((r) => r.uri)).toContain(post.uri)
|
||||
|
||||
const { data: action } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -483,18 +483,18 @@ describe('crud operations', () => {
|
||||
},
|
||||
)
|
||||
|
||||
const postTakedownPromise = client.app.bsky.feed.post.get({
|
||||
const postTakedownPromise = agent.api.app.bsky.feed.post.get({
|
||||
user: alice.did,
|
||||
rkey: postUri.rkey,
|
||||
})
|
||||
await expect(postTakedownPromise).rejects.toThrow('Could not locate record')
|
||||
const postsTakedown = await client.app.bsky.feed.post.list({
|
||||
const postsTakedown = await agent.api.app.bsky.feed.post.list({
|
||||
user: alice.did,
|
||||
})
|
||||
expect(postsTakedown.records.map((r) => r.uri)).not.toContain(post.uri)
|
||||
|
||||
// Cleanup
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: action.id,
|
||||
createdBy: 'X',
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { sql } from 'kysely'
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { getWriteLog, RecordWriteOp, WriteOpAction } from '@atproto/repo'
|
||||
import SqlRepoStorage from '../../src/sql-repo-storage'
|
||||
import basicSeed from '../seeds/basic'
|
||||
@ -16,7 +16,7 @@ import { AppContext } from '../../src'
|
||||
describe('sync', () => {
|
||||
let server: TestServerInfo
|
||||
let ctx: AppContext
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let sc: SeedClient
|
||||
|
||||
beforeAll(async () => {
|
||||
@ -24,8 +24,8 @@ describe('sync', () => {
|
||||
dbPostgresSchema: 'event_stream_sync',
|
||||
})
|
||||
ctx = server.ctx
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await basicSeed(sc, ctx.messageQueue)
|
||||
})
|
||||
|
||||
@ -41,7 +41,7 @@ describe('sync', () => {
|
||||
indexedTables.map((t) => sql`delete from ${ref(t)}`.execute(db.db)),
|
||||
)
|
||||
// Confirm timeline empty
|
||||
const emptiedTL = await client.app.bsky.feed.getTimeline(
|
||||
const emptiedTL = await agent.api.app.bsky.feed.getTimeline(
|
||||
{},
|
||||
{ headers: sc.getHeaders(sc.dids.alice) },
|
||||
)
|
||||
@ -70,7 +70,7 @@ describe('sync', () => {
|
||||
}
|
||||
await messageQueue.processAll()
|
||||
// Check indexed timeline
|
||||
const aliceTL = await client.app.bsky.feed.getTimeline(
|
||||
const aliceTL = await agent.api.app.bsky.feed.getTimeline(
|
||||
{},
|
||||
{ headers: sc.getHeaders(sc.dids.alice) },
|
||||
)
|
||||
|
@ -1,6 +1,6 @@
|
||||
import fs from 'fs/promises'
|
||||
import { gzipSync } from 'zlib'
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { CloseFn, runTestServer, TestServerInfo } from './_util'
|
||||
import { CID } from 'multiformats/cid'
|
||||
import { Database, ServerConfig } from '../src'
|
||||
@ -25,9 +25,9 @@ const bob = {
|
||||
|
||||
describe('file uploads', () => {
|
||||
let server: TestServerInfo
|
||||
let client: AtpServiceClient
|
||||
let aliceClient: AtpServiceClient
|
||||
let bobClient: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let aliceAgent: AtpAgent
|
||||
let bobAgent: AtpAgent
|
||||
let blobstore: DiskBlobStore
|
||||
let db: Database
|
||||
let cfg: ServerConfig
|
||||
@ -41,9 +41,9 @@ describe('file uploads', () => {
|
||||
blobstore = server.ctx.blobstore as DiskBlobStore
|
||||
db = server.ctx.db
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
aliceClient = AtpApi.service(server.url)
|
||||
bobClient = AtpApi.service(server.url)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
aliceAgent = new AtpAgent({ service: server.url })
|
||||
bobAgent = new AtpAgent({ service: server.url })
|
||||
cfg = server.ctx.cfg
|
||||
serverUrl = server.url
|
||||
})
|
||||
@ -53,19 +53,19 @@ describe('file uploads', () => {
|
||||
})
|
||||
|
||||
it('registers users', async () => {
|
||||
const res = await client.com.atproto.account.create({
|
||||
const res = await agent.api.com.atproto.account.create({
|
||||
email: alice.email,
|
||||
handle: alice.handle,
|
||||
password: alice.password,
|
||||
})
|
||||
aliceClient.setHeader('authorization', `Bearer ${res.data.accessJwt}`)
|
||||
aliceAgent.api.setHeader('authorization', `Bearer ${res.data.accessJwt}`)
|
||||
alice.did = res.data.did
|
||||
const res2 = await client.com.atproto.account.create({
|
||||
const res2 = await agent.api.com.atproto.account.create({
|
||||
email: bob.email,
|
||||
handle: bob.handle,
|
||||
password: bob.password,
|
||||
})
|
||||
bobClient.setHeader('authorization', `Bearer ${res2.data.accessJwt}`)
|
||||
bobAgent.api.setHeader('authorization', `Bearer ${res2.data.accessJwt}`)
|
||||
bob.did = res2.data.did
|
||||
})
|
||||
|
||||
@ -86,7 +86,7 @@ describe('file uploads', () => {
|
||||
signal: abortController.signal,
|
||||
headers: {
|
||||
'content-type': 'image/jpeg',
|
||||
authorization: aliceClient.xrpc.headers.authorization,
|
||||
authorization: aliceAgent.api.xrpc.headers.authorization,
|
||||
},
|
||||
})
|
||||
await expect(response).rejects.toThrow('operation was aborted')
|
||||
@ -98,7 +98,7 @@ describe('file uploads', () => {
|
||||
|
||||
it('uploads files', async () => {
|
||||
smallFile = await fs.readFile('tests/image/fixtures/key-portrait-small.jpg')
|
||||
const res = await aliceClient.com.atproto.blob.upload(smallFile, {
|
||||
const res = await aliceAgent.api.com.atproto.blob.upload(smallFile, {
|
||||
encoding: 'image/jpeg',
|
||||
} as any)
|
||||
smallCid = CID.parse(res.data.cid)
|
||||
@ -118,7 +118,7 @@ describe('file uploads', () => {
|
||||
})
|
||||
|
||||
it('can reference the file', async () => {
|
||||
await aliceClient.app.bsky.actor.updateProfile({
|
||||
await aliceAgent.api.app.bsky.actor.updateProfile({
|
||||
displayName: 'Alice',
|
||||
avatar: { cid: smallCid.toString(), mimeType: 'image/jpeg' },
|
||||
})
|
||||
@ -138,7 +138,7 @@ describe('file uploads', () => {
|
||||
})
|
||||
|
||||
it('serves the referenced blob', async () => {
|
||||
const profile = await aliceClient.app.bsky.actor.getProfile({
|
||||
const profile = await aliceAgent.api.app.bsky.actor.getProfile({
|
||||
actor: 'alice.test',
|
||||
})
|
||||
const avatar = profile.data.avatar as string
|
||||
@ -160,12 +160,12 @@ describe('file uploads', () => {
|
||||
|
||||
it('does not allow referencing a file that is outside blob constraints', async () => {
|
||||
largeFile = await fs.readFile('tests/image/fixtures/hd-key.jpg')
|
||||
const res = await aliceClient.com.atproto.blob.upload(largeFile, {
|
||||
const res = await aliceAgent.api.com.atproto.blob.upload(largeFile, {
|
||||
encoding: 'image/jpeg',
|
||||
} as any)
|
||||
largeCid = CID.parse(res.data.cid)
|
||||
|
||||
const profilePromise = aliceClient.app.bsky.actor.updateProfile({
|
||||
const profilePromise = aliceAgent.api.app.bsky.actor.updateProfile({
|
||||
avatar: { cid: largeCid.toString(), mimeType: 'image/jpeg' },
|
||||
})
|
||||
|
||||
@ -188,25 +188,29 @@ describe('file uploads', () => {
|
||||
const file = await fs.readFile(
|
||||
'tests/image/fixtures/key-landscape-small.jpg',
|
||||
)
|
||||
const { data: uploadA } = await aliceClient.com.atproto.blob.upload(file, {
|
||||
encoding: 'image/jpeg',
|
||||
} as any)
|
||||
const { data: uploadB } = await bobClient.com.atproto.blob.upload(file, {
|
||||
const { data: uploadA } = await aliceAgent.api.com.atproto.blob.upload(
|
||||
file,
|
||||
{
|
||||
encoding: 'image/jpeg',
|
||||
} as any,
|
||||
)
|
||||
const { data: uploadB } = await bobAgent.api.com.atproto.blob.upload(file, {
|
||||
encoding: 'image/jpeg',
|
||||
} as any)
|
||||
expect(uploadA).toEqual(uploadB)
|
||||
const { data: profileA } = await aliceClient.app.bsky.actor.updateProfile({
|
||||
displayName: 'Alice',
|
||||
avatar: { cid: uploadA.cid, mimeType: 'image/jpeg' },
|
||||
})
|
||||
const { data: profileA } =
|
||||
await aliceAgent.api.app.bsky.actor.updateProfile({
|
||||
displayName: 'Alice',
|
||||
avatar: { cid: uploadA.cid, mimeType: 'image/jpeg' },
|
||||
})
|
||||
expect((profileA.record as any).avatar.cid).toEqual(uploadA.cid)
|
||||
const { data: profileB } = await bobClient.app.bsky.actor.updateProfile({
|
||||
const { data: profileB } = await bobAgent.api.app.bsky.actor.updateProfile({
|
||||
displayName: 'Bob',
|
||||
avatar: { cid: uploadB.cid, mimeType: 'image/jpeg' },
|
||||
})
|
||||
expect((profileB.record as any).avatar.cid).toEqual(uploadA.cid)
|
||||
const { data: uploadAfterPermanent } =
|
||||
await aliceClient.com.atproto.blob.upload(file, {
|
||||
await aliceAgent.api.com.atproto.blob.upload(file, {
|
||||
encoding: 'image/jpeg',
|
||||
} as any)
|
||||
expect(uploadAfterPermanent).toEqual(uploadA)
|
||||
@ -219,7 +223,7 @@ describe('file uploads', () => {
|
||||
})
|
||||
|
||||
it('supports compression during upload', async () => {
|
||||
const { data: uploaded } = await aliceClient.com.atproto.blob.upload(
|
||||
const { data: uploaded } = await aliceAgent.api.com.atproto.blob.upload(
|
||||
gzipSync(smallFile),
|
||||
{
|
||||
encoding: 'image/jpeg',
|
||||
@ -235,7 +239,7 @@ describe('file uploads', () => {
|
||||
const file = await fs.readFile(
|
||||
'tests/image/fixtures/key-landscape-large.jpg',
|
||||
)
|
||||
const res = await aliceClient.com.atproto.blob.upload(file, {
|
||||
const res = await aliceAgent.api.com.atproto.blob.upload(file, {
|
||||
encoding: 'video/mp4',
|
||||
} as any)
|
||||
|
||||
@ -252,7 +256,7 @@ describe('file uploads', () => {
|
||||
|
||||
it('handles unknown mimetypes', async () => {
|
||||
const file = await randomBytes(20000)
|
||||
const res = await aliceClient.com.atproto.blob.upload(file, {
|
||||
const res = await aliceAgent.api.com.atproto.blob.upload(file, {
|
||||
encoding: 'test/fake',
|
||||
} as any)
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpApi from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { Database } from '../../src'
|
||||
import { Kysely } from 'kysely'
|
||||
import { CloseFn, runTestServer } from '../_util'
|
||||
@ -17,8 +17,8 @@ describe('repo sync data migration', () => {
|
||||
db = server.ctx.db
|
||||
rawDb = db.db
|
||||
close = server.close
|
||||
const client = AtpApi.service(server.url)
|
||||
const sc = new SeedClient(client)
|
||||
const agent = new AtpAgent({ service: server.url })
|
||||
const sc = new SeedClient(agent)
|
||||
await basicSeed(sc)
|
||||
})
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { AtUri } from '@atproto/uri'
|
||||
import {
|
||||
adminAuth,
|
||||
@ -21,7 +21,7 @@ import { BlobNotFoundError } from '@atproto/repo'
|
||||
describe('moderation', () => {
|
||||
let server: TestServerInfo
|
||||
let close: CloseFn
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let sc: SeedClient
|
||||
|
||||
beforeAll(async () => {
|
||||
@ -29,8 +29,8 @@ describe('moderation', () => {
|
||||
dbPostgresSchema: 'moderation',
|
||||
})
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await basicSeed(sc)
|
||||
})
|
||||
|
||||
@ -40,7 +40,7 @@ describe('moderation', () => {
|
||||
|
||||
describe('reporting', () => {
|
||||
it('creates reports of a repo.', async () => {
|
||||
const { data: reportA } = await client.com.atproto.report.create(
|
||||
const { data: reportA } = await agent.api.com.atproto.report.create(
|
||||
{
|
||||
reasonType: SPAM,
|
||||
subject: {
|
||||
@ -50,7 +50,7 @@ describe('moderation', () => {
|
||||
},
|
||||
{ headers: sc.getHeaders(sc.dids.alice), encoding: 'application/json' },
|
||||
)
|
||||
const { data: reportB } = await client.com.atproto.report.create(
|
||||
const { data: reportB } = await agent.api.com.atproto.report.create(
|
||||
{
|
||||
reasonType: OTHER,
|
||||
reason: 'impersonation',
|
||||
@ -65,7 +65,7 @@ describe('moderation', () => {
|
||||
})
|
||||
|
||||
it("fails reporting a repo that doesn't exist.", async () => {
|
||||
const promise = client.com.atproto.report.create(
|
||||
const promise = agent.api.com.atproto.report.create(
|
||||
{
|
||||
reasonType: SPAM,
|
||||
subject: {
|
||||
@ -81,7 +81,7 @@ describe('moderation', () => {
|
||||
it('creates reports of a record.', async () => {
|
||||
const postA = sc.posts[sc.dids.bob][0].ref
|
||||
const postB = sc.posts[sc.dids.bob][1].ref
|
||||
const { data: reportA } = await client.com.atproto.report.create(
|
||||
const { data: reportA } = await agent.api.com.atproto.report.create(
|
||||
{
|
||||
reasonType: SPAM,
|
||||
subject: {
|
||||
@ -91,7 +91,7 @@ describe('moderation', () => {
|
||||
},
|
||||
{ headers: sc.getHeaders(sc.dids.alice), encoding: 'application/json' },
|
||||
)
|
||||
const { data: reportB } = await client.com.atproto.report.create(
|
||||
const { data: reportB } = await agent.api.com.atproto.report.create(
|
||||
{
|
||||
reasonType: OTHER,
|
||||
reason: 'defamation',
|
||||
@ -112,7 +112,7 @@ describe('moderation', () => {
|
||||
const postUriBad = new AtUri(postA.uriStr)
|
||||
postUriBad.rkey = 'badrkey'
|
||||
|
||||
const promiseA = client.com.atproto.report.create(
|
||||
const promiseA = agent.api.com.atproto.report.create(
|
||||
{
|
||||
reasonType: SPAM,
|
||||
subject: {
|
||||
@ -124,7 +124,7 @@ describe('moderation', () => {
|
||||
)
|
||||
await expect(promiseA).rejects.toThrow('Record not found')
|
||||
|
||||
const promiseB = client.com.atproto.report.create(
|
||||
const promiseB = agent.api.com.atproto.report.create(
|
||||
{
|
||||
reasonType: OTHER,
|
||||
reason: 'defamation',
|
||||
@ -142,7 +142,7 @@ describe('moderation', () => {
|
||||
|
||||
describe('actioning', () => {
|
||||
it('resolves reports on repos and records.', async () => {
|
||||
const { data: reportA } = await client.com.atproto.report.create(
|
||||
const { data: reportA } = await agent.api.com.atproto.report.create(
|
||||
{
|
||||
reasonType: SPAM,
|
||||
subject: {
|
||||
@ -153,7 +153,7 @@ describe('moderation', () => {
|
||||
{ headers: sc.getHeaders(sc.dids.alice), encoding: 'application/json' },
|
||||
)
|
||||
const post = sc.posts[sc.dids.bob][1].ref
|
||||
const { data: reportB } = await client.com.atproto.report.create(
|
||||
const { data: reportB } = await agent.api.com.atproto.report.create(
|
||||
{
|
||||
reasonType: OTHER,
|
||||
reason: 'defamation',
|
||||
@ -165,7 +165,7 @@ describe('moderation', () => {
|
||||
{ headers: sc.getHeaders(sc.dids.carol), encoding: 'application/json' },
|
||||
)
|
||||
const { data: action } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -181,7 +181,7 @@ describe('moderation', () => {
|
||||
},
|
||||
)
|
||||
const { data: actionResolvedReports } =
|
||||
await client.com.atproto.admin.resolveModerationReports(
|
||||
await agent.api.com.atproto.admin.resolveModerationReports(
|
||||
{
|
||||
actionId: action.id,
|
||||
reportIds: [reportB.id, reportA.id],
|
||||
@ -196,7 +196,7 @@ describe('moderation', () => {
|
||||
expect(forSnapshot(actionResolvedReports)).toMatchSnapshot()
|
||||
|
||||
// Cleanup
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: action.id,
|
||||
createdBy: 'X',
|
||||
@ -210,7 +210,7 @@ describe('moderation', () => {
|
||||
})
|
||||
|
||||
it('does not resolve report for mismatching repo.', async () => {
|
||||
const { data: report } = await client.com.atproto.report.create(
|
||||
const { data: report } = await agent.api.com.atproto.report.create(
|
||||
{
|
||||
reasonType: SPAM,
|
||||
subject: {
|
||||
@ -221,7 +221,7 @@ describe('moderation', () => {
|
||||
{ headers: sc.getHeaders(sc.dids.alice), encoding: 'application/json' },
|
||||
)
|
||||
const { data: action } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -237,7 +237,7 @@ describe('moderation', () => {
|
||||
},
|
||||
)
|
||||
|
||||
const promise = client.com.atproto.admin.resolveModerationReports(
|
||||
const promise = agent.api.com.atproto.admin.resolveModerationReports(
|
||||
{
|
||||
actionId: action.id,
|
||||
reportIds: [report.id],
|
||||
@ -254,7 +254,7 @@ describe('moderation', () => {
|
||||
)
|
||||
|
||||
// Cleanup
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: action.id,
|
||||
createdBy: 'X',
|
||||
@ -270,7 +270,7 @@ describe('moderation', () => {
|
||||
it('does not resolve report for mismatching record.', async () => {
|
||||
const postUri1 = sc.posts[sc.dids.alice][0].ref.uri
|
||||
const postUri2 = sc.posts[sc.dids.bob][0].ref.uri
|
||||
const { data: report } = await client.com.atproto.report.create(
|
||||
const { data: report } = await agent.api.com.atproto.report.create(
|
||||
{
|
||||
reasonType: SPAM,
|
||||
subject: {
|
||||
@ -281,7 +281,7 @@ describe('moderation', () => {
|
||||
{ headers: sc.getHeaders(sc.dids.alice), encoding: 'application/json' },
|
||||
)
|
||||
const { data: action } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -297,7 +297,7 @@ describe('moderation', () => {
|
||||
},
|
||||
)
|
||||
|
||||
const promise = client.com.atproto.admin.resolveModerationReports(
|
||||
const promise = agent.api.com.atproto.admin.resolveModerationReports(
|
||||
{
|
||||
actionId: action.id,
|
||||
reportIds: [report.id],
|
||||
@ -314,7 +314,7 @@ describe('moderation', () => {
|
||||
)
|
||||
|
||||
// Cleanup
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: action.id,
|
||||
createdBy: 'X',
|
||||
@ -331,7 +331,7 @@ describe('moderation', () => {
|
||||
const postRef1 = sc.posts[sc.dids.alice][0].ref
|
||||
const postRef2 = sc.posts[sc.dids.bob][0].ref
|
||||
const { data: action1 } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: FLAG,
|
||||
subject: {
|
||||
@ -357,7 +357,7 @@ describe('moderation', () => {
|
||||
}),
|
||||
)
|
||||
const { data: action2 } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: ACKNOWLEDGE,
|
||||
subject: {
|
||||
@ -383,7 +383,7 @@ describe('moderation', () => {
|
||||
}),
|
||||
)
|
||||
// Cleanup
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: action1.id,
|
||||
createdBy: 'X',
|
||||
@ -394,7 +394,7 @@ describe('moderation', () => {
|
||||
headers: { authorization: adminAuth() },
|
||||
},
|
||||
)
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: action2.id,
|
||||
createdBy: 'X',
|
||||
@ -410,7 +410,7 @@ describe('moderation', () => {
|
||||
it('only allows record to have one current action.', async () => {
|
||||
const postUri = sc.posts[sc.dids.alice][0].ref.uri
|
||||
const { data: acknowledge } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: ACKNOWLEDGE,
|
||||
subject: {
|
||||
@ -425,7 +425,7 @@ describe('moderation', () => {
|
||||
headers: { authorization: adminAuth() },
|
||||
},
|
||||
)
|
||||
const flagPromise = client.com.atproto.admin.takeModerationAction(
|
||||
const flagPromise = agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: FLAG,
|
||||
subject: {
|
||||
@ -445,7 +445,7 @@ describe('moderation', () => {
|
||||
)
|
||||
|
||||
// Reverse current then retry
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: acknowledge.id,
|
||||
createdBy: 'X',
|
||||
@ -457,7 +457,7 @@ describe('moderation', () => {
|
||||
},
|
||||
)
|
||||
const { data: flag } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: FLAG,
|
||||
subject: {
|
||||
@ -474,7 +474,7 @@ describe('moderation', () => {
|
||||
)
|
||||
|
||||
// Cleanup
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: flag.id,
|
||||
createdBy: 'X',
|
||||
@ -489,7 +489,7 @@ describe('moderation', () => {
|
||||
|
||||
it('only allows repo to have one current action.', async () => {
|
||||
const { data: acknowledge } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: ACKNOWLEDGE,
|
||||
subject: {
|
||||
@ -504,7 +504,7 @@ describe('moderation', () => {
|
||||
headers: { authorization: adminAuth() },
|
||||
},
|
||||
)
|
||||
const flagPromise = client.com.atproto.admin.takeModerationAction(
|
||||
const flagPromise = agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: FLAG,
|
||||
subject: {
|
||||
@ -524,7 +524,7 @@ describe('moderation', () => {
|
||||
)
|
||||
|
||||
// Reverse current then retry
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: acknowledge.id,
|
||||
createdBy: 'X',
|
||||
@ -536,7 +536,7 @@ describe('moderation', () => {
|
||||
},
|
||||
)
|
||||
const { data: flag } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: FLAG,
|
||||
subject: {
|
||||
@ -553,7 +553,7 @@ describe('moderation', () => {
|
||||
)
|
||||
|
||||
// Cleanup
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: flag.id,
|
||||
createdBy: 'X',
|
||||
@ -571,7 +571,7 @@ describe('moderation', () => {
|
||||
const postA = await sc.post(sc.dids.alice, 'image A', undefined, [img])
|
||||
const postB = await sc.post(sc.dids.alice, 'image B', undefined, [img])
|
||||
const { data: acknowledge } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: ACKNOWLEDGE,
|
||||
subject: {
|
||||
@ -587,7 +587,7 @@ describe('moderation', () => {
|
||||
headers: { authorization: adminAuth() },
|
||||
},
|
||||
)
|
||||
const flagPromise = client.com.atproto.admin.takeModerationAction(
|
||||
const flagPromise = agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: FLAG,
|
||||
subject: {
|
||||
@ -607,7 +607,7 @@ describe('moderation', () => {
|
||||
'Blob already has an active action:',
|
||||
)
|
||||
// Reverse current then retry
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: acknowledge.id,
|
||||
createdBy: 'X',
|
||||
@ -619,7 +619,7 @@ describe('moderation', () => {
|
||||
},
|
||||
)
|
||||
const { data: flag } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: FLAG,
|
||||
subject: {
|
||||
@ -637,7 +637,7 @@ describe('moderation', () => {
|
||||
)
|
||||
|
||||
// Cleanup
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: flag.id,
|
||||
createdBy: 'X',
|
||||
@ -666,7 +666,7 @@ describe('moderation', () => {
|
||||
await fetch(imageUri)
|
||||
const cached = await fetch(imageUri)
|
||||
expect(cached.headers.get('x-cache')).toEqual('hit')
|
||||
const takeAction = await client.com.atproto.admin.takeModerationAction(
|
||||
const takeAction = await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -710,7 +710,7 @@ describe('moderation', () => {
|
||||
})
|
||||
|
||||
it('restores blob when action is reversed.', async () => {
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: actionId,
|
||||
createdBy: 'X',
|
||||
|
@ -1,5 +1,5 @@
|
||||
import fs from 'fs/promises'
|
||||
import { ServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { InputSchema as TakeActionInput } from '@atproto/api/src/client/types/com/atproto/admin/takeModerationAction'
|
||||
import { InputSchema as CreateReportInput } from '@atproto/api/src/client/types/com/atproto/report/create'
|
||||
import { AtUri } from '@atproto/uri'
|
||||
@ -94,7 +94,7 @@ export class SeedClient {
|
||||
reposts: Record<string, RecordRef[]>
|
||||
dids: Record<string, string>
|
||||
|
||||
constructor(public client: ServiceClient) {
|
||||
constructor(public agent: AtpAgent) {
|
||||
this.accounts = {}
|
||||
this.profiles = {}
|
||||
this.follows = {}
|
||||
@ -113,10 +113,10 @@ export class SeedClient {
|
||||
password: string
|
||||
},
|
||||
) {
|
||||
const { data: account } = await this.client.com.atproto.account.create(
|
||||
const { data: account } = await this.agent.api.com.atproto.account.create(
|
||||
params,
|
||||
)
|
||||
const { data: profile } = await this.client.app.bsky.actor.getProfile(
|
||||
const { data: profile } = await this.agent.api.app.bsky.actor.getProfile(
|
||||
{
|
||||
actor: params.handle,
|
||||
},
|
||||
@ -144,7 +144,7 @@ export class SeedClient {
|
||||
|
||||
let avatarCid
|
||||
{
|
||||
const res = await this.client.com.atproto.blob.upload(AVATAR_IMG, {
|
||||
const res = await this.agent.api.com.atproto.blob.upload(AVATAR_IMG, {
|
||||
encoding: 'image/jpeg',
|
||||
headers: this.getHeaders(fromUser || by),
|
||||
} as any)
|
||||
@ -152,7 +152,7 @@ export class SeedClient {
|
||||
}
|
||||
|
||||
{
|
||||
const res = await this.client.app.bsky.actor.profile.create(
|
||||
const res = await this.agent.api.app.bsky.actor.profile.create(
|
||||
{ did: by },
|
||||
{
|
||||
displayName,
|
||||
@ -172,7 +172,7 @@ export class SeedClient {
|
||||
}
|
||||
|
||||
async follow(from: string, to: ActorRef) {
|
||||
const res = await this.client.app.bsky.graph.follow.create(
|
||||
const res = await this.agent.api.app.bsky.graph.follow.create(
|
||||
{ did: from },
|
||||
{
|
||||
subject: to.raw,
|
||||
@ -192,7 +192,7 @@ export class SeedClient {
|
||||
images,
|
||||
}
|
||||
: undefined
|
||||
const res = await this.client.app.bsky.feed.post.create(
|
||||
const res = await this.agent.api.app.bsky.feed.post.create(
|
||||
{ did: by },
|
||||
{
|
||||
text: text,
|
||||
@ -213,7 +213,7 @@ export class SeedClient {
|
||||
}
|
||||
|
||||
async deletePost(by: string, uri: AtUri) {
|
||||
await this.client.app.bsky.feed.post.delete(
|
||||
await this.agent.api.app.bsky.feed.post.delete(
|
||||
{
|
||||
did: by,
|
||||
rkey: uri.rkey,
|
||||
@ -228,7 +228,7 @@ export class SeedClient {
|
||||
encoding: string,
|
||||
): Promise<ImageRef> {
|
||||
const file = await fs.readFile(filePath)
|
||||
const res = await this.client.com.atproto.blob.upload(file, {
|
||||
const res = await this.agent.api.com.atproto.blob.upload(file, {
|
||||
headers: this.getHeaders(by),
|
||||
encoding,
|
||||
} as any)
|
||||
@ -236,7 +236,7 @@ export class SeedClient {
|
||||
}
|
||||
|
||||
async vote(direction: 'up' | 'down', by: string, subject: RecordRef) {
|
||||
const res = await this.client.app.bsky.feed.vote.create(
|
||||
const res = await this.agent.api.app.bsky.feed.vote.create(
|
||||
{ did: by },
|
||||
{ direction, subject: subject.raw, createdAt: new Date().toISOString() },
|
||||
this.getHeaders(by),
|
||||
@ -260,7 +260,7 @@ export class SeedClient {
|
||||
images,
|
||||
}
|
||||
: undefined
|
||||
const res = await this.client.app.bsky.feed.post.create(
|
||||
const res = await this.agent.api.app.bsky.feed.post.create(
|
||||
{ did: by },
|
||||
{
|
||||
text: text,
|
||||
@ -284,7 +284,7 @@ export class SeedClient {
|
||||
}
|
||||
|
||||
async repost(by: string, subject: RecordRef) {
|
||||
const res = await this.client.app.bsky.feed.repost.create(
|
||||
const res = await this.agent.api.app.bsky.feed.repost.create(
|
||||
{ did: by },
|
||||
{ subject: subject.raw, createdAt: new Date().toISOString() },
|
||||
this.getHeaders(by),
|
||||
@ -306,7 +306,7 @@ export class SeedClient {
|
||||
createdBy?: string
|
||||
}) {
|
||||
const { action, subject, reason = 'X', createdBy = 'Y' } = opts
|
||||
const result = await this.client.com.atproto.admin.takeModerationAction(
|
||||
const result = await this.agent.api.com.atproto.admin.takeModerationAction(
|
||||
{ action, subject, createdBy, reason },
|
||||
{
|
||||
encoding: 'application/json',
|
||||
@ -322,13 +322,14 @@ export class SeedClient {
|
||||
createdBy?: string
|
||||
}) {
|
||||
const { id, reason = 'X', createdBy = 'Y' } = opts
|
||||
const result = await this.client.com.atproto.admin.reverseModerationAction(
|
||||
{ id, reason, createdBy },
|
||||
{
|
||||
encoding: 'application/json',
|
||||
headers: { authorization: adminAuth() },
|
||||
},
|
||||
)
|
||||
const result =
|
||||
await this.agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{ id, reason, createdBy },
|
||||
{
|
||||
encoding: 'application/json',
|
||||
headers: { authorization: adminAuth() },
|
||||
},
|
||||
)
|
||||
return result.data
|
||||
}
|
||||
|
||||
@ -338,13 +339,14 @@ export class SeedClient {
|
||||
createdBy?: string
|
||||
}) {
|
||||
const { actionId, reportIds, createdBy = 'Y' } = opts
|
||||
const result = await this.client.com.atproto.admin.resolveModerationReports(
|
||||
{ actionId, createdBy, reportIds },
|
||||
{
|
||||
encoding: 'application/json',
|
||||
headers: { authorization: adminAuth() },
|
||||
},
|
||||
)
|
||||
const result =
|
||||
await this.agent.api.com.atproto.admin.resolveModerationReports(
|
||||
{ actionId, createdBy, reportIds },
|
||||
{
|
||||
encoding: 'application/json',
|
||||
headers: { authorization: adminAuth() },
|
||||
},
|
||||
)
|
||||
return result.data
|
||||
}
|
||||
|
||||
@ -355,7 +357,7 @@ export class SeedClient {
|
||||
reportedByDid: string
|
||||
}) {
|
||||
const { reasonType, subject, reason, reportedByDid } = opts
|
||||
const result = await this.client.com.atproto.report.create(
|
||||
const result = await this.agent.api.com.atproto.report.create(
|
||||
{ reasonType, subject, reason },
|
||||
{
|
||||
encoding: 'application/json',
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { AddressInfo } from 'net'
|
||||
import express from 'express'
|
||||
import axios, { AxiosError } from 'axios'
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { CloseFn, runTestServer, TestServerInfo } from './_util'
|
||||
import { handler as errorHandler } from '../src/error'
|
||||
import { SeedClient } from './seeds/client'
|
||||
@ -12,7 +12,7 @@ describe('server', () => {
|
||||
let server: TestServerInfo
|
||||
let close: CloseFn
|
||||
let db: Database
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let sc: SeedClient
|
||||
let alice: string
|
||||
|
||||
@ -22,8 +22,8 @@ describe('server', () => {
|
||||
})
|
||||
close = server.close
|
||||
db = server.ctx.db
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await usersSeed(sc)
|
||||
alice = sc.dids.alice
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { TID } from '@atproto/common'
|
||||
import { randomStr } from '@atproto/crypto'
|
||||
import { DidResolver } from '@atproto/did-resolver'
|
||||
@ -9,7 +9,7 @@ import { CID } from 'multiformats/cid'
|
||||
import { CloseFn, runTestServer } from './_util'
|
||||
|
||||
describe('repo sync', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let did: string
|
||||
|
||||
const repoData: repo.RepoContents = {}
|
||||
@ -25,13 +25,13 @@ describe('repo sync', () => {
|
||||
dbPostgresSchema: 'repo_sync',
|
||||
})
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
const res = await client.com.atproto.account.create({
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
const res = await agent.api.com.atproto.account.create({
|
||||
email: 'alice@test.com',
|
||||
handle: 'alice.test',
|
||||
password: 'alice-pass',
|
||||
})
|
||||
client.setHeader('authorization', `Bearer ${res.data.accessJwt}`)
|
||||
agent.api.setHeader('authorization', `Bearer ${res.data.accessJwt}`)
|
||||
did = res.data.did
|
||||
didResolver = new DidResolver({ plcUrl: server.ctx.cfg.didPlcUrl })
|
||||
repoData['app.bsky.system.declaration'] = {
|
||||
@ -49,7 +49,7 @@ describe('repo sync', () => {
|
||||
it('creates and syncs some records', async () => {
|
||||
const ADD_COUNT = 10
|
||||
for (let i = 0; i < ADD_COUNT; i++) {
|
||||
const { obj, uri } = await makePost(client, did)
|
||||
const { obj, uri } = await makePost(agent, did)
|
||||
if (!repoData[uri.collection]) {
|
||||
repoData[uri.collection] = {}
|
||||
}
|
||||
@ -57,7 +57,7 @@ describe('repo sync', () => {
|
||||
uris.push(uri)
|
||||
}
|
||||
|
||||
const car = await client.com.atproto.sync.getRepo({ did })
|
||||
const car = await agent.api.com.atproto.sync.getRepo({ did })
|
||||
const synced = await repo.loadFullRepo(
|
||||
storage,
|
||||
new Uint8Array(car.data),
|
||||
@ -77,7 +77,7 @@ describe('repo sync', () => {
|
||||
const ADD_COUNT = 10
|
||||
const DEL_COUNT = 4
|
||||
for (let i = 0; i < ADD_COUNT; i++) {
|
||||
const { obj, uri } = await makePost(client, did)
|
||||
const { obj, uri } = await makePost(agent, did)
|
||||
if (!repoData[uri.collection]) {
|
||||
repoData[uri.collection] = {}
|
||||
}
|
||||
@ -87,7 +87,7 @@ describe('repo sync', () => {
|
||||
// delete two that are already sync & two that have not been
|
||||
for (let i = 0; i < DEL_COUNT; i++) {
|
||||
const uri = uris[i * 5]
|
||||
await client.app.bsky.feed.post.delete({
|
||||
await agent.api.app.bsky.feed.post.delete({
|
||||
did,
|
||||
colleciton: uri.collection,
|
||||
rkey: uri.rkey,
|
||||
@ -95,7 +95,7 @@ describe('repo sync', () => {
|
||||
delete repoData[uri.collection][uri.rkey]
|
||||
}
|
||||
|
||||
const car = await client.com.atproto.sync.getRepo({
|
||||
const car = await agent.api.com.atproto.sync.getRepo({
|
||||
did,
|
||||
from: currRoot?.toString(),
|
||||
})
|
||||
@ -116,7 +116,7 @@ describe('repo sync', () => {
|
||||
})
|
||||
|
||||
it('syncs current root', async () => {
|
||||
const root = await client.com.atproto.sync.getHead({ did })
|
||||
const root = await agent.api.com.atproto.sync.getHead({ did })
|
||||
expect(root.data.root).toEqual(currRoot?.toString())
|
||||
})
|
||||
|
||||
@ -126,10 +126,10 @@ describe('repo sync', () => {
|
||||
throw new Error('Could not get local commit path')
|
||||
}
|
||||
const localStr = local.map((c) => c.toString())
|
||||
const commitPath = await client.com.atproto.sync.getCommitPath({ did })
|
||||
const commitPath = await agent.api.com.atproto.sync.getCommitPath({ did })
|
||||
expect(commitPath.data.commits).toEqual(localStr)
|
||||
|
||||
const partialCommitPath = await client.com.atproto.sync.getCommitPath({
|
||||
const partialCommitPath = await agent.api.com.atproto.sync.getCommitPath({
|
||||
did,
|
||||
earliest: localStr[2],
|
||||
latest: localStr[15],
|
||||
@ -138,7 +138,7 @@ describe('repo sync', () => {
|
||||
})
|
||||
|
||||
it('sync a repo checkout', async () => {
|
||||
const car = await client.com.atproto.sync.getCheckout({ did })
|
||||
const car = await agent.api.com.atproto.sync.getCheckout({ did })
|
||||
const checkoutStorage = new MemoryBlockstore()
|
||||
const loaded = await repo.loadCheckout(
|
||||
checkoutStorage,
|
||||
@ -153,7 +153,7 @@ describe('repo sync', () => {
|
||||
it('sync a record proof', async () => {
|
||||
const collection = Object.keys(repoData)[0]
|
||||
const rkey = Object.keys(repoData[collection])[0]
|
||||
const car = await client.com.atproto.sync.getRecord({
|
||||
const car = await agent.api.com.atproto.sync.getRecord({
|
||||
did,
|
||||
collection,
|
||||
rkey,
|
||||
@ -183,7 +183,7 @@ describe('repo sync', () => {
|
||||
it('sync a proof of non-existence', async () => {
|
||||
const collection = Object.keys(repoData)[0]
|
||||
const rkey = TID.nextStr() // rkey that doesn't exist
|
||||
const car = await client.com.atproto.sync.getRecord({
|
||||
const car = await agent.api.com.atproto.sync.getRecord({
|
||||
did,
|
||||
collection,
|
||||
rkey,
|
||||
@ -204,13 +204,13 @@ describe('repo sync', () => {
|
||||
})
|
||||
})
|
||||
|
||||
const makePost = async (client: AtpServiceClient, did: string) => {
|
||||
const makePost = async (agent: AtpAgent, did: string) => {
|
||||
const obj = {
|
||||
$type: 'app.bsky.feed.post',
|
||||
text: randomStr(32, 'base32'),
|
||||
createdAt: new Date().toISOString(),
|
||||
}
|
||||
const res = await client.app.bsky.feed.post.create({ did }, obj)
|
||||
const res = await agent.api.app.bsky.feed.post.create({ did }, obj)
|
||||
const uri = new AtUri(res.uri)
|
||||
return { obj, uri }
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import {
|
||||
FLAG,
|
||||
TAKEDOWN,
|
||||
@ -12,7 +12,7 @@ import { SeedClient } from '../../seeds/client'
|
||||
import basicSeed from '../../seeds/basic'
|
||||
|
||||
describe('pds admin get moderation action view', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
|
||||
@ -21,8 +21,8 @@ describe('pds admin get moderation action view', () => {
|
||||
dbPostgresSchema: 'views_admin_get_moderation_action',
|
||||
})
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await basicSeed(sc)
|
||||
})
|
||||
|
||||
@ -74,7 +74,7 @@ describe('pds admin get moderation action view', () => {
|
||||
})
|
||||
|
||||
it('gets moderation action for a repo.', async () => {
|
||||
const result = await client.com.atproto.admin.getModerationAction(
|
||||
const result = await agent.api.com.atproto.admin.getModerationAction(
|
||||
{ id: 1 },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
@ -82,7 +82,7 @@ describe('pds admin get moderation action view', () => {
|
||||
})
|
||||
|
||||
it('gets moderation action for a record.', async () => {
|
||||
const result = await client.com.atproto.admin.getModerationAction(
|
||||
const result = await agent.api.com.atproto.admin.getModerationAction(
|
||||
{ id: 2 },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
@ -90,7 +90,7 @@ describe('pds admin get moderation action view', () => {
|
||||
})
|
||||
|
||||
it('fails when moderation action does not exist.', async () => {
|
||||
const promise = client.com.atproto.admin.getModerationAction(
|
||||
const promise = agent.api.com.atproto.admin.getModerationAction(
|
||||
{ id: 100 },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import {
|
||||
ACKNOWLEDGE,
|
||||
FLAG,
|
||||
@ -19,7 +19,7 @@ import { SeedClient } from '../../seeds/client'
|
||||
import basicSeed from '../../seeds/basic'
|
||||
|
||||
describe('pds admin get moderation actions view', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
|
||||
@ -28,8 +28,8 @@ describe('pds admin get moderation actions view', () => {
|
||||
dbPostgresSchema: 'views_admin_get_moderation_actions',
|
||||
})
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await basicSeed(sc)
|
||||
})
|
||||
|
||||
@ -120,7 +120,7 @@ describe('pds admin get moderation actions view', () => {
|
||||
})
|
||||
|
||||
it('gets all moderation actions.', async () => {
|
||||
const result = await client.com.atproto.admin.getModerationActions(
|
||||
const result = await agent.api.com.atproto.admin.getModerationActions(
|
||||
{},
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
@ -128,7 +128,7 @@ describe('pds admin get moderation actions view', () => {
|
||||
})
|
||||
|
||||
it('gets all moderation actions for a repo.', async () => {
|
||||
const result = await client.com.atproto.admin.getModerationActions(
|
||||
const result = await agent.api.com.atproto.admin.getModerationActions(
|
||||
{ subject: Object.values(sc.dids)[0] },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
@ -136,7 +136,7 @@ describe('pds admin get moderation actions view', () => {
|
||||
})
|
||||
|
||||
it('gets all moderation actions for a record.', async () => {
|
||||
const result = await client.com.atproto.admin.getModerationActions(
|
||||
const result = await agent.api.com.atproto.admin.getModerationActions(
|
||||
{ subject: Object.values(sc.posts)[0][0].ref.uriStr },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
@ -146,7 +146,7 @@ describe('pds admin get moderation actions view', () => {
|
||||
it('paginates.', async () => {
|
||||
const results = (results) => results.flatMap((res) => res.actions)
|
||||
const paginator = async (cursor?: string) => {
|
||||
const res = await client.com.atproto.admin.getModerationActions(
|
||||
const res = await agent.api.com.atproto.admin.getModerationActions(
|
||||
{ before: cursor, limit: 3 },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
@ -158,7 +158,7 @@ describe('pds admin get moderation actions view', () => {
|
||||
expect(res.actions.length).toBeLessThanOrEqual(3),
|
||||
)
|
||||
|
||||
const full = await client.com.atproto.admin.getModerationActions(
|
||||
const full = await agent.api.com.atproto.admin.getModerationActions(
|
||||
{},
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import {
|
||||
FLAG,
|
||||
TAKEDOWN,
|
||||
@ -12,7 +12,7 @@ import { SeedClient } from '../../seeds/client'
|
||||
import basicSeed from '../../seeds/basic'
|
||||
|
||||
describe('pds admin get moderation action view', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
|
||||
@ -21,8 +21,8 @@ describe('pds admin get moderation action view', () => {
|
||||
dbPostgresSchema: 'views_admin_get_moderation_report',
|
||||
})
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await basicSeed(sc)
|
||||
})
|
||||
|
||||
@ -74,7 +74,7 @@ describe('pds admin get moderation action view', () => {
|
||||
})
|
||||
|
||||
it('gets moderation report for a repo.', async () => {
|
||||
const result = await client.com.atproto.admin.getModerationReport(
|
||||
const result = await agent.api.com.atproto.admin.getModerationReport(
|
||||
{ id: 1 },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
@ -82,7 +82,7 @@ describe('pds admin get moderation action view', () => {
|
||||
})
|
||||
|
||||
it('gets moderation report for a record.', async () => {
|
||||
const result = await client.com.atproto.admin.getModerationReport(
|
||||
const result = await agent.api.com.atproto.admin.getModerationReport(
|
||||
{ id: 2 },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
@ -90,7 +90,7 @@ describe('pds admin get moderation action view', () => {
|
||||
})
|
||||
|
||||
it('fails when moderation report does not exist.', async () => {
|
||||
const promise = client.com.atproto.admin.getModerationReport(
|
||||
const promise = agent.api.com.atproto.admin.getModerationReport(
|
||||
{ id: 100 },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import {
|
||||
ACKNOWLEDGE,
|
||||
FLAG,
|
||||
@ -19,7 +19,7 @@ import { SeedClient } from '../../seeds/client'
|
||||
import basicSeed from '../../seeds/basic'
|
||||
|
||||
describe('pds admin get moderation reports view', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
|
||||
@ -28,8 +28,8 @@ describe('pds admin get moderation reports view', () => {
|
||||
dbPostgresSchema: 'views_admin_get_moderation_reports',
|
||||
})
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await basicSeed(sc)
|
||||
})
|
||||
|
||||
@ -119,7 +119,7 @@ describe('pds admin get moderation reports view', () => {
|
||||
})
|
||||
|
||||
it('gets all moderation reports.', async () => {
|
||||
const result = await client.com.atproto.admin.getModerationReports(
|
||||
const result = await agent.api.com.atproto.admin.getModerationReports(
|
||||
{},
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
@ -127,7 +127,7 @@ describe('pds admin get moderation reports view', () => {
|
||||
})
|
||||
|
||||
it('gets all moderation reports for a repo.', async () => {
|
||||
const result = await client.com.atproto.admin.getModerationReports(
|
||||
const result = await agent.api.com.atproto.admin.getModerationReports(
|
||||
{ subject: Object.values(sc.dids)[0] },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
@ -135,7 +135,7 @@ describe('pds admin get moderation reports view', () => {
|
||||
})
|
||||
|
||||
it('gets all moderation reports for a record.', async () => {
|
||||
const result = await client.com.atproto.admin.getModerationReports(
|
||||
const result = await agent.api.com.atproto.admin.getModerationReports(
|
||||
{ subject: Object.values(sc.posts)[0][0].ref.uriStr },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
@ -143,12 +143,12 @@ describe('pds admin get moderation reports view', () => {
|
||||
})
|
||||
|
||||
it('gets all resolved/unresolved moderation reports.', async () => {
|
||||
const resolved = await client.com.atproto.admin.getModerationReports(
|
||||
const resolved = await agent.api.com.atproto.admin.getModerationReports(
|
||||
{ resolved: true },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
expect(forSnapshot(resolved.data.reports)).toMatchSnapshot()
|
||||
const unresolved = await client.com.atproto.admin.getModerationReports(
|
||||
const unresolved = await agent.api.com.atproto.admin.getModerationReports(
|
||||
{ resolved: false },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
@ -158,7 +158,7 @@ describe('pds admin get moderation reports view', () => {
|
||||
it('paginates.', async () => {
|
||||
const results = (results) => results.flatMap((res) => res.reports)
|
||||
const paginator = async (cursor?: string) => {
|
||||
const res = await client.com.atproto.admin.getModerationReports(
|
||||
const res = await agent.api.com.atproto.admin.getModerationReports(
|
||||
{ before: cursor, limit: 3 },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
@ -170,7 +170,7 @@ describe('pds admin get moderation reports view', () => {
|
||||
expect(res.reports.length).toBeLessThanOrEqual(3),
|
||||
)
|
||||
|
||||
const full = await client.com.atproto.admin.getModerationReports(
|
||||
const full = await agent.api.com.atproto.admin.getModerationReports(
|
||||
{},
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { AtUri } from '@atproto/uri'
|
||||
import {
|
||||
ACKNOWLEDGE,
|
||||
@ -13,7 +13,7 @@ import { SeedClient } from '../../seeds/client'
|
||||
import basicSeed from '../../seeds/basic'
|
||||
|
||||
describe('pds admin get record view', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
|
||||
@ -22,8 +22,8 @@ describe('pds admin get record view', () => {
|
||||
dbPostgresSchema: 'views_admin_get_record',
|
||||
})
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await basicSeed(sc)
|
||||
})
|
||||
|
||||
@ -67,7 +67,7 @@ describe('pds admin get record view', () => {
|
||||
})
|
||||
|
||||
it('gets a record by uri, even when taken down.', async () => {
|
||||
const result = await client.com.atproto.admin.getRecord(
|
||||
const result = await agent.api.com.atproto.admin.getRecord(
|
||||
{ uri: sc.posts[sc.dids.alice][0].ref.uriStr },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
@ -75,7 +75,7 @@ describe('pds admin get record view', () => {
|
||||
})
|
||||
|
||||
it('gets a record by uri and cid.', async () => {
|
||||
const result = await client.com.atproto.admin.getRecord(
|
||||
const result = await agent.api.com.atproto.admin.getRecord(
|
||||
{
|
||||
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
||||
cid: sc.posts[sc.dids.alice][0].ref.cidStr,
|
||||
@ -86,7 +86,7 @@ describe('pds admin get record view', () => {
|
||||
})
|
||||
|
||||
it('fails when record does not exist.', async () => {
|
||||
const promise = client.com.atproto.admin.getRecord(
|
||||
const promise = agent.api.com.atproto.admin.getRecord(
|
||||
{
|
||||
uri: AtUri.make(
|
||||
sc.dids.alice,
|
||||
@ -100,7 +100,7 @@ describe('pds admin get record view', () => {
|
||||
})
|
||||
|
||||
it('fails when record cid does not exist.', async () => {
|
||||
const promise = client.com.atproto.admin.getRecord(
|
||||
const promise = agent.api.com.atproto.admin.getRecord(
|
||||
{
|
||||
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
||||
cid: sc.posts[sc.dids.alice][1].ref.cidStr, // Mismatching cid
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import {
|
||||
ACKNOWLEDGE,
|
||||
TAKEDOWN,
|
||||
@ -12,7 +12,7 @@ import { SeedClient } from '../../seeds/client'
|
||||
import basicSeed from '../../seeds/basic'
|
||||
|
||||
describe('pds admin get repo view', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
|
||||
@ -21,8 +21,8 @@ describe('pds admin get repo view', () => {
|
||||
dbPostgresSchema: 'views_admin_get_repo',
|
||||
})
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await basicSeed(sc)
|
||||
})
|
||||
|
||||
@ -66,7 +66,7 @@ describe('pds admin get repo view', () => {
|
||||
})
|
||||
|
||||
it('gets a repo by did, even when taken down.', async () => {
|
||||
const result = await client.com.atproto.admin.getRepo(
|
||||
const result = await agent.api.com.atproto.admin.getRepo(
|
||||
{ did: sc.dids.alice },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
@ -74,7 +74,7 @@ describe('pds admin get repo view', () => {
|
||||
})
|
||||
|
||||
it('fails when repo does not exist.', async () => {
|
||||
const promise = client.com.atproto.admin.getRepo(
|
||||
const promise = agent.api.com.atproto.admin.getRepo(
|
||||
{ did: 'did:plc:doesnotexist' },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/moderationAction'
|
||||
import {
|
||||
runTestServer,
|
||||
@ -12,7 +12,7 @@ import usersBulkSeed from '../../seeds/users-bulk'
|
||||
import { Database } from '../../../src'
|
||||
|
||||
describe('pds admin repo search view', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let db: Database
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
@ -24,8 +24,8 @@ describe('pds admin repo search view', () => {
|
||||
})
|
||||
close = server.close
|
||||
db = server.ctx.db
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await usersBulkSeed(sc)
|
||||
headers = { authorization: adminAuth() }
|
||||
})
|
||||
@ -45,7 +45,7 @@ describe('pds admin repo search view', () => {
|
||||
})
|
||||
|
||||
it('gives relevant results', async () => {
|
||||
const result = await client.com.atproto.admin.searchRepos(
|
||||
const result = await agent.api.com.atproto.admin.searchRepos(
|
||||
{ term: 'car' },
|
||||
{ headers },
|
||||
)
|
||||
@ -91,7 +91,7 @@ describe('pds admin repo search view', () => {
|
||||
it('paginates with term', async () => {
|
||||
const results = (results) => results.flatMap((res) => res.users)
|
||||
const paginator = async (cursor?: string) => {
|
||||
const res = await client.com.atproto.admin.searchRepos(
|
||||
const res = await agent.api.com.atproto.admin.searchRepos(
|
||||
{ term: 'p', before: cursor, limit: 3 },
|
||||
{ headers },
|
||||
)
|
||||
@ -103,7 +103,7 @@ describe('pds admin repo search view', () => {
|
||||
expect(res.repos.length).toBeLessThanOrEqual(3),
|
||||
)
|
||||
|
||||
const full = await client.com.atproto.admin.searchRepos(
|
||||
const full = await agent.api.com.atproto.admin.searchRepos(
|
||||
{ term: 'p' },
|
||||
{ headers },
|
||||
)
|
||||
@ -115,7 +115,7 @@ describe('pds admin repo search view', () => {
|
||||
it('paginates without term', async () => {
|
||||
const results = (results) => results.flatMap((res) => res.repos)
|
||||
const paginator = async (cursor?: string) => {
|
||||
const res = await client.com.atproto.admin.searchRepos(
|
||||
const res = await agent.api.com.atproto.admin.searchRepos(
|
||||
{ before: cursor, limit: 3 },
|
||||
{ headers },
|
||||
)
|
||||
@ -127,7 +127,7 @@ describe('pds admin repo search view', () => {
|
||||
expect(res.repos.length).toBeLessThanOrEqual(3),
|
||||
)
|
||||
|
||||
const full = await client.com.atproto.admin.searchRepos(
|
||||
const full = await agent.api.com.atproto.admin.searchRepos(
|
||||
{ limit: 15 },
|
||||
{ headers },
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { AtUri } from '@atproto/uri'
|
||||
import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/moderationAction'
|
||||
import {
|
||||
@ -12,7 +12,7 @@ import { SeedClient } from '../seeds/client'
|
||||
import basicSeed from '../seeds/basic'
|
||||
|
||||
describe('pds author feed views', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
|
||||
@ -27,8 +27,8 @@ describe('pds author feed views', () => {
|
||||
dbPostgresSchema: 'views_author_feed',
|
||||
})
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await basicSeed(sc, server.ctx.messageQueue)
|
||||
alice = sc.dids.alice
|
||||
bob = sc.dids.bob
|
||||
@ -41,7 +41,7 @@ describe('pds author feed views', () => {
|
||||
})
|
||||
|
||||
it('fetches full author feeds for self (sorted, minimal myState).', async () => {
|
||||
const aliceForAlice = await client.app.bsky.feed.getAuthorFeed(
|
||||
const aliceForAlice = await agent.api.app.bsky.feed.getAuthorFeed(
|
||||
{ author: sc.accounts[alice].handle },
|
||||
{
|
||||
headers: sc.getHeaders(alice),
|
||||
@ -50,7 +50,7 @@ describe('pds author feed views', () => {
|
||||
|
||||
expect(forSnapshot(aliceForAlice.data.feed)).toMatchSnapshot()
|
||||
|
||||
const bobForBob = await client.app.bsky.feed.getAuthorFeed(
|
||||
const bobForBob = await agent.api.app.bsky.feed.getAuthorFeed(
|
||||
{ author: sc.accounts[bob].handle },
|
||||
{
|
||||
headers: sc.getHeaders(bob),
|
||||
@ -59,7 +59,7 @@ describe('pds author feed views', () => {
|
||||
|
||||
expect(forSnapshot(bobForBob.data.feed)).toMatchSnapshot()
|
||||
|
||||
const carolForCarol = await client.app.bsky.feed.getAuthorFeed(
|
||||
const carolForCarol = await agent.api.app.bsky.feed.getAuthorFeed(
|
||||
{ author: sc.accounts[carol].handle },
|
||||
{
|
||||
headers: sc.getHeaders(carol),
|
||||
@ -68,7 +68,7 @@ describe('pds author feed views', () => {
|
||||
|
||||
expect(forSnapshot(carolForCarol.data.feed)).toMatchSnapshot()
|
||||
|
||||
const danForDan = await client.app.bsky.feed.getAuthorFeed(
|
||||
const danForDan = await agent.api.app.bsky.feed.getAuthorFeed(
|
||||
{ author: sc.accounts[dan].handle },
|
||||
{
|
||||
headers: sc.getHeaders(dan),
|
||||
@ -79,7 +79,7 @@ describe('pds author feed views', () => {
|
||||
})
|
||||
|
||||
it("reflects fetching user's state in the feed.", async () => {
|
||||
const aliceForCarol = await client.app.bsky.feed.getAuthorFeed(
|
||||
const aliceForCarol = await agent.api.app.bsky.feed.getAuthorFeed(
|
||||
{ author: sc.accounts[alice].handle },
|
||||
{
|
||||
headers: sc.getHeaders(carol),
|
||||
@ -97,26 +97,26 @@ describe('pds author feed views', () => {
|
||||
})
|
||||
|
||||
it('omits reposts from muted users.', async () => {
|
||||
await client.app.bsky.graph.mute(
|
||||
await agent.api.app.bsky.graph.mute(
|
||||
{ user: alice }, // Has a repost by dan: will be omitted from dan's feed
|
||||
{ headers: sc.getHeaders(bob), encoding: 'application/json' },
|
||||
)
|
||||
await client.app.bsky.graph.mute(
|
||||
await agent.api.app.bsky.graph.mute(
|
||||
{ user: dan }, // Feed author: their posts will still appear
|
||||
{ headers: sc.getHeaders(bob), encoding: 'application/json' },
|
||||
)
|
||||
const bobForDan = await client.app.bsky.feed.getAuthorFeed(
|
||||
const bobForDan = await agent.api.app.bsky.feed.getAuthorFeed(
|
||||
{ author: sc.accounts[dan].handle },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
|
||||
expect(forSnapshot(bobForDan.data.feed)).toMatchSnapshot()
|
||||
|
||||
await client.app.bsky.graph.unmute(
|
||||
await agent.api.app.bsky.graph.unmute(
|
||||
{ user: alice },
|
||||
{ headers: sc.getHeaders(bob), encoding: 'application/json' },
|
||||
)
|
||||
await client.app.bsky.graph.unmute(
|
||||
await agent.api.app.bsky.graph.unmute(
|
||||
{ user: dan },
|
||||
{ headers: sc.getHeaders(bob), encoding: 'application/json' },
|
||||
)
|
||||
@ -125,7 +125,7 @@ describe('pds author feed views', () => {
|
||||
it('paginates', async () => {
|
||||
const results = (results) => results.flatMap((res) => res.feed)
|
||||
const paginator = async (cursor?: string) => {
|
||||
const res = await client.app.bsky.feed.getAuthorFeed(
|
||||
const res = await agent.api.app.bsky.feed.getAuthorFeed(
|
||||
{
|
||||
author: sc.accounts[alice].handle,
|
||||
before: cursor,
|
||||
@ -141,7 +141,7 @@ describe('pds author feed views', () => {
|
||||
expect(res.feed.length).toBeLessThanOrEqual(2),
|
||||
)
|
||||
|
||||
const full = await client.app.bsky.feed.getAuthorFeed(
|
||||
const full = await agent.api.app.bsky.feed.getAuthorFeed(
|
||||
{
|
||||
author: sc.accounts[alice].handle,
|
||||
},
|
||||
@ -153,7 +153,7 @@ describe('pds author feed views', () => {
|
||||
})
|
||||
|
||||
it('blocked by actor takedown.', async () => {
|
||||
const { data: preBlock } = await client.app.bsky.feed.getAuthorFeed(
|
||||
const { data: preBlock } = await agent.api.app.bsky.feed.getAuthorFeed(
|
||||
{ author: alice },
|
||||
{ headers: sc.getHeaders(carol) },
|
||||
)
|
||||
@ -161,7 +161,7 @@ describe('pds author feed views', () => {
|
||||
expect(preBlock.feed.length).toBeGreaterThan(0)
|
||||
|
||||
const { data: action } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -177,7 +177,7 @@ describe('pds author feed views', () => {
|
||||
},
|
||||
)
|
||||
|
||||
const { data: postBlock } = await client.app.bsky.feed.getAuthorFeed(
|
||||
const { data: postBlock } = await agent.api.app.bsky.feed.getAuthorFeed(
|
||||
{ author: alice },
|
||||
{ headers: sc.getHeaders(carol) },
|
||||
)
|
||||
@ -185,7 +185,7 @@ describe('pds author feed views', () => {
|
||||
expect(postBlock.feed.length).toEqual(0)
|
||||
|
||||
// Cleanup
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: action.id,
|
||||
createdBy: 'X',
|
||||
@ -199,7 +199,7 @@ describe('pds author feed views', () => {
|
||||
})
|
||||
|
||||
it('blocked by record takedown.', async () => {
|
||||
const { data: preBlock } = await client.app.bsky.feed.getAuthorFeed(
|
||||
const { data: preBlock } = await agent.api.app.bsky.feed.getAuthorFeed(
|
||||
{ author: alice },
|
||||
{ headers: sc.getHeaders(carol) },
|
||||
)
|
||||
@ -209,7 +209,7 @@ describe('pds author feed views', () => {
|
||||
const postUri = new AtUri(preBlock.feed[0].post.uri)
|
||||
|
||||
const { data: action } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -225,7 +225,7 @@ describe('pds author feed views', () => {
|
||||
},
|
||||
)
|
||||
|
||||
const { data: postBlock } = await client.app.bsky.feed.getAuthorFeed(
|
||||
const { data: postBlock } = await agent.api.app.bsky.feed.getAuthorFeed(
|
||||
{ author: alice },
|
||||
{ headers: sc.getHeaders(carol) },
|
||||
)
|
||||
@ -236,7 +236,7 @@ describe('pds author feed views', () => {
|
||||
)
|
||||
|
||||
// Cleanup
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: action.id,
|
||||
createdBy: 'X',
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/moderationAction'
|
||||
import {
|
||||
runTestServer,
|
||||
@ -12,7 +12,7 @@ import { SeedClient } from '../seeds/client'
|
||||
import followsSeed from '../seeds/follows'
|
||||
|
||||
describe('pds follow views', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
|
||||
@ -24,8 +24,8 @@ describe('pds follow views', () => {
|
||||
dbPostgresSchema: 'views_follows',
|
||||
})
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await followsSeed(sc)
|
||||
alice = sc.dids.alice
|
||||
})
|
||||
@ -43,7 +43,7 @@ describe('pds follow views', () => {
|
||||
const tstamp = (x: string) => new Date(x).getTime()
|
||||
|
||||
it('fetches followers', async () => {
|
||||
const aliceFollowers = await client.app.bsky.graph.getFollowers(
|
||||
const aliceFollowers = await agent.api.app.bsky.graph.getFollowers(
|
||||
{ user: sc.dids.alice },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -53,7 +53,7 @@ describe('pds follow views', () => {
|
||||
getSortedCursors(aliceFollowers.data.followers),
|
||||
)
|
||||
|
||||
const bobFollowers = await client.app.bsky.graph.getFollowers(
|
||||
const bobFollowers = await agent.api.app.bsky.graph.getFollowers(
|
||||
{ user: sc.dids.bob },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -63,7 +63,7 @@ describe('pds follow views', () => {
|
||||
getSortedCursors(bobFollowers.data.followers),
|
||||
)
|
||||
|
||||
const carolFollowers = await client.app.bsky.graph.getFollowers(
|
||||
const carolFollowers = await agent.api.app.bsky.graph.getFollowers(
|
||||
{ user: sc.dids.carol },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -73,7 +73,7 @@ describe('pds follow views', () => {
|
||||
getSortedCursors(carolFollowers.data.followers),
|
||||
)
|
||||
|
||||
const danFollowers = await client.app.bsky.graph.getFollowers(
|
||||
const danFollowers = await agent.api.app.bsky.graph.getFollowers(
|
||||
{ user: sc.dids.dan },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -83,7 +83,7 @@ describe('pds follow views', () => {
|
||||
getSortedCursors(danFollowers.data.followers),
|
||||
)
|
||||
|
||||
const eveFollowers = await client.app.bsky.graph.getFollowers(
|
||||
const eveFollowers = await agent.api.app.bsky.graph.getFollowers(
|
||||
{ user: sc.dids.eve },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -95,11 +95,11 @@ describe('pds follow views', () => {
|
||||
})
|
||||
|
||||
it('fetches followers by handle', async () => {
|
||||
const byDid = await client.app.bsky.graph.getFollowers(
|
||||
const byDid = await agent.api.app.bsky.graph.getFollowers(
|
||||
{ user: sc.dids.alice },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
const byHandle = await client.app.bsky.graph.getFollowers(
|
||||
const byHandle = await agent.api.app.bsky.graph.getFollowers(
|
||||
{ user: sc.accounts[alice].handle },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -109,7 +109,7 @@ describe('pds follow views', () => {
|
||||
it('paginates followers', async () => {
|
||||
const results = (results) => results.flatMap((res) => res.followers)
|
||||
const paginator = async (cursor?: string) => {
|
||||
const res = await client.app.bsky.graph.getFollowers(
|
||||
const res = await agent.api.app.bsky.graph.getFollowers(
|
||||
{
|
||||
user: sc.dids.alice,
|
||||
before: cursor,
|
||||
@ -125,7 +125,7 @@ describe('pds follow views', () => {
|
||||
expect(res.followers.length).toBeLessThanOrEqual(2),
|
||||
)
|
||||
|
||||
const full = await client.app.bsky.graph.getFollowers(
|
||||
const full = await agent.api.app.bsky.graph.getFollowers(
|
||||
{ user: sc.dids.alice },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -136,7 +136,7 @@ describe('pds follow views', () => {
|
||||
|
||||
it('blocks followers by actor takedown', async () => {
|
||||
const { data: modAction } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -152,14 +152,14 @@ describe('pds follow views', () => {
|
||||
},
|
||||
)
|
||||
|
||||
const aliceFollowers = await client.app.bsky.graph.getFollowers(
|
||||
const aliceFollowers = await agent.api.app.bsky.graph.getFollowers(
|
||||
{ user: sc.dids.alice },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
|
||||
expect(forSnapshot(aliceFollowers.data)).toMatchSnapshot()
|
||||
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: modAction.id,
|
||||
createdBy: 'X',
|
||||
@ -173,7 +173,7 @@ describe('pds follow views', () => {
|
||||
})
|
||||
|
||||
it('fetches follows', async () => {
|
||||
const aliceFollowers = await client.app.bsky.graph.getFollows(
|
||||
const aliceFollowers = await agent.api.app.bsky.graph.getFollows(
|
||||
{ user: sc.dids.alice },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -183,7 +183,7 @@ describe('pds follow views', () => {
|
||||
getSortedCursors(aliceFollowers.data.follows),
|
||||
)
|
||||
|
||||
const bobFollowers = await client.app.bsky.graph.getFollows(
|
||||
const bobFollowers = await agent.api.app.bsky.graph.getFollows(
|
||||
{ user: sc.dids.bob },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -193,7 +193,7 @@ describe('pds follow views', () => {
|
||||
getSortedCursors(bobFollowers.data.follows),
|
||||
)
|
||||
|
||||
const carolFollowers = await client.app.bsky.graph.getFollows(
|
||||
const carolFollowers = await agent.api.app.bsky.graph.getFollows(
|
||||
{ user: sc.dids.carol },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -203,7 +203,7 @@ describe('pds follow views', () => {
|
||||
getSortedCursors(carolFollowers.data.follows),
|
||||
)
|
||||
|
||||
const danFollowers = await client.app.bsky.graph.getFollows(
|
||||
const danFollowers = await agent.api.app.bsky.graph.getFollows(
|
||||
{ user: sc.dids.dan },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -213,7 +213,7 @@ describe('pds follow views', () => {
|
||||
getSortedCursors(danFollowers.data.follows),
|
||||
)
|
||||
|
||||
const eveFollowers = await client.app.bsky.graph.getFollows(
|
||||
const eveFollowers = await agent.api.app.bsky.graph.getFollows(
|
||||
{ user: sc.dids.eve },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -225,11 +225,11 @@ describe('pds follow views', () => {
|
||||
})
|
||||
|
||||
it('fetches follows by handle', async () => {
|
||||
const byDid = await client.app.bsky.graph.getFollows(
|
||||
const byDid = await agent.api.app.bsky.graph.getFollows(
|
||||
{ user: sc.dids.alice },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
const byHandle = await client.app.bsky.graph.getFollows(
|
||||
const byHandle = await agent.api.app.bsky.graph.getFollows(
|
||||
{ user: sc.accounts[alice].handle },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -239,7 +239,7 @@ describe('pds follow views', () => {
|
||||
it('paginates follows', async () => {
|
||||
const results = (results) => results.flatMap((res) => res.follows)
|
||||
const paginator = async (cursor?: string) => {
|
||||
const res = await client.app.bsky.graph.getFollows(
|
||||
const res = await agent.api.app.bsky.graph.getFollows(
|
||||
{
|
||||
user: sc.dids.alice,
|
||||
before: cursor,
|
||||
@ -255,7 +255,7 @@ describe('pds follow views', () => {
|
||||
expect(res.follows.length).toBeLessThanOrEqual(2),
|
||||
)
|
||||
|
||||
const full = await client.app.bsky.graph.getFollows(
|
||||
const full = await agent.api.app.bsky.graph.getFollows(
|
||||
{ user: sc.dids.alice },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -266,7 +266,7 @@ describe('pds follow views', () => {
|
||||
|
||||
it('blocks follows by actor takedown', async () => {
|
||||
const { data: modAction } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -282,14 +282,14 @@ describe('pds follow views', () => {
|
||||
},
|
||||
)
|
||||
|
||||
const aliceFollows = await client.app.bsky.graph.getFollows(
|
||||
const aliceFollows = await agent.api.app.bsky.graph.getFollows(
|
||||
{ user: sc.dids.alice },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
|
||||
expect(forSnapshot(aliceFollows.data)).toMatchSnapshot()
|
||||
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: modAction.id,
|
||||
createdBy: 'X',
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import {
|
||||
runTestServer,
|
||||
forSnapshot,
|
||||
@ -10,7 +10,7 @@ import { SeedClient } from '../seeds/client'
|
||||
import usersBulkSeed from '../seeds/users-bulk'
|
||||
|
||||
describe('mute views', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
let silas: string
|
||||
@ -20,8 +20,8 @@ describe('mute views', () => {
|
||||
dbPostgresSchema: 'views_mutes',
|
||||
})
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await usersBulkSeed(sc, 10)
|
||||
silas = sc.dids['silas77.test']
|
||||
const mutes = [
|
||||
@ -33,7 +33,7 @@ describe('mute views', () => {
|
||||
'elta48.test',
|
||||
]
|
||||
for (const did of mutes) {
|
||||
await client.app.bsky.graph.mute(
|
||||
await agent.api.app.bsky.graph.mute(
|
||||
{ user: did },
|
||||
{ headers: sc.getHeaders(silas), encoding: 'application/json' },
|
||||
)
|
||||
@ -53,7 +53,7 @@ describe('mute views', () => {
|
||||
const tstamp = (x: string) => new Date(x).getTime()
|
||||
|
||||
it('fetches mutes for the logged-in user.', async () => {
|
||||
const { data: view } = await client.app.bsky.graph.getMutes(
|
||||
const { data: view } = await agent.api.app.bsky.graph.getMutes(
|
||||
{},
|
||||
{ headers: sc.getHeaders(silas) },
|
||||
)
|
||||
@ -64,7 +64,7 @@ describe('mute views', () => {
|
||||
it('paginates.', async () => {
|
||||
const results = (results) => results.flatMap((res) => res.mutes)
|
||||
const paginator = async (cursor?: string) => {
|
||||
const { data: view } = await client.app.bsky.graph.getMutes(
|
||||
const { data: view } = await agent.api.app.bsky.graph.getMutes(
|
||||
{ before: cursor, limit: 2 },
|
||||
{ headers: sc.getHeaders(silas) },
|
||||
)
|
||||
@ -76,7 +76,7 @@ describe('mute views', () => {
|
||||
expect(res.mutes.length).toBeLessThanOrEqual(2),
|
||||
)
|
||||
|
||||
const full = await client.app.bsky.graph.getMutes(
|
||||
const full = await agent.api.app.bsky.graph.getMutes(
|
||||
{},
|
||||
{ headers: sc.getHeaders(silas) },
|
||||
)
|
||||
@ -86,33 +86,33 @@ describe('mute views', () => {
|
||||
})
|
||||
|
||||
it('removes mute.', async () => {
|
||||
const { data: initial } = await client.app.bsky.graph.getMutes(
|
||||
const { data: initial } = await agent.api.app.bsky.graph.getMutes(
|
||||
{},
|
||||
{ headers: sc.getHeaders(silas) },
|
||||
)
|
||||
expect(initial.mutes.length).toEqual(6)
|
||||
expect(initial.mutes.map((m) => m.handle)).toContain('elta48.test')
|
||||
|
||||
await client.app.bsky.graph.unmute(
|
||||
await agent.api.app.bsky.graph.unmute(
|
||||
{ user: sc.dids['elta48.test'] },
|
||||
{ headers: sc.getHeaders(silas), encoding: 'application/json' },
|
||||
)
|
||||
|
||||
const { data: final } = await client.app.bsky.graph.getMutes(
|
||||
const { data: final } = await agent.api.app.bsky.graph.getMutes(
|
||||
{},
|
||||
{ headers: sc.getHeaders(silas) },
|
||||
)
|
||||
expect(final.mutes.length).toEqual(5)
|
||||
expect(final.mutes.map((m) => m.handle)).not.toContain('elta48.test')
|
||||
|
||||
await client.app.bsky.graph.mute(
|
||||
await agent.api.app.bsky.graph.mute(
|
||||
{ user: sc.dids['elta48.test'] },
|
||||
{ headers: sc.getHeaders(silas), encoding: 'application/json' },
|
||||
)
|
||||
})
|
||||
|
||||
it('does not allow muting self.', async () => {
|
||||
const promise = client.app.bsky.graph.mute(
|
||||
const promise = agent.api.app.bsky.graph.mute(
|
||||
{ user: silas },
|
||||
{ headers: sc.getHeaders(silas), encoding: 'application/json' },
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/moderationAction'
|
||||
import {
|
||||
runTestServer,
|
||||
@ -13,7 +13,7 @@ import { Database } from '../../src'
|
||||
import { Notification } from '../../src/lexicon/types/app/bsky/notification/list'
|
||||
|
||||
describe('pds notification views', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
let db: Database
|
||||
let sc: SeedClient
|
||||
@ -27,8 +27,8 @@ describe('pds notification views', () => {
|
||||
})
|
||||
close = server.close
|
||||
db = server.ctx.db
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await basicSeed(sc, server.ctx.messageQueue)
|
||||
alice = sc.dids.alice
|
||||
})
|
||||
@ -47,7 +47,7 @@ describe('pds notification views', () => {
|
||||
}
|
||||
|
||||
it('fetches notification count without a last-seen', async () => {
|
||||
const notifCount = await client.app.bsky.notification.getCount(
|
||||
const notifCount = await agent.api.app.bsky.notification.getCount(
|
||||
{},
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -56,7 +56,7 @@ describe('pds notification views', () => {
|
||||
})
|
||||
|
||||
it('fetches notifications without a last-seen', async () => {
|
||||
const notifRes = await client.app.bsky.notification.list(
|
||||
const notifRes = await agent.api.app.bsky.notification.list(
|
||||
{},
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -74,20 +74,20 @@ describe('pds notification views', () => {
|
||||
})
|
||||
|
||||
it('fetches notifications omitting mentions and replies by a muted user', async () => {
|
||||
await client.app.bsky.graph.mute(
|
||||
await agent.api.app.bsky.graph.mute(
|
||||
{ user: sc.dids.carol }, // Replier
|
||||
{ headers: sc.getHeaders(alice), encoding: 'application/json' },
|
||||
)
|
||||
await client.app.bsky.graph.mute(
|
||||
await agent.api.app.bsky.graph.mute(
|
||||
{ user: sc.dids.dan }, // Mentioner
|
||||
{ headers: sc.getHeaders(alice), encoding: 'application/json' },
|
||||
)
|
||||
|
||||
const notifRes = await client.app.bsky.notification.list(
|
||||
const notifRes = await agent.api.app.bsky.notification.list(
|
||||
{},
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
const notifCount = await client.app.bsky.notification.getCount(
|
||||
const notifCount = await agent.api.app.bsky.notification.getCount(
|
||||
{},
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -98,11 +98,11 @@ describe('pds notification views', () => {
|
||||
expect(notifCount.data.count).toBe(7)
|
||||
|
||||
// Cleanup
|
||||
await client.app.bsky.graph.unmute(
|
||||
await agent.api.app.bsky.graph.unmute(
|
||||
{ user: sc.dids.carol },
|
||||
{ headers: sc.getHeaders(alice), encoding: 'application/json' },
|
||||
)
|
||||
await client.app.bsky.graph.unmute(
|
||||
await agent.api.app.bsky.graph.unmute(
|
||||
{ user: sc.dids.dan },
|
||||
{ headers: sc.getHeaders(alice), encoding: 'application/json' },
|
||||
)
|
||||
@ -113,7 +113,7 @@ describe('pds notification views', () => {
|
||||
const postUri2 = sc.posts[sc.dids.dan][1].ref.uri // Mention
|
||||
const actionResults = await Promise.all(
|
||||
[postUri1, postUri2].map((postUri) =>
|
||||
client.com.atproto.admin.takeModerationAction(
|
||||
agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -131,11 +131,11 @@ describe('pds notification views', () => {
|
||||
),
|
||||
)
|
||||
|
||||
const notifRes = await client.app.bsky.notification.list(
|
||||
const notifRes = await agent.api.app.bsky.notification.list(
|
||||
{},
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
const notifCount = await client.app.bsky.notification.getCount(
|
||||
const notifCount = await agent.api.app.bsky.notification.getCount(
|
||||
{},
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -148,7 +148,7 @@ describe('pds notification views', () => {
|
||||
// Cleanup
|
||||
await Promise.all(
|
||||
actionResults.map((result) =>
|
||||
client.com.atproto.admin.reverseModerationAction(
|
||||
agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: result.data.id,
|
||||
createdBy: 'X',
|
||||
@ -167,7 +167,7 @@ describe('pds notification views', () => {
|
||||
const results = (results) =>
|
||||
sort(results.flatMap((res) => res.notifications))
|
||||
const paginator = async (cursor?: string) => {
|
||||
const res = await client.app.bsky.notification.list(
|
||||
const res = await agent.api.app.bsky.notification.list(
|
||||
{
|
||||
before: cursor,
|
||||
limit: 6,
|
||||
@ -182,7 +182,7 @@ describe('pds notification views', () => {
|
||||
expect(res.notifications.length).toBeLessThanOrEqual(6),
|
||||
)
|
||||
|
||||
const full = await client.app.bsky.notification.list(
|
||||
const full = await agent.api.app.bsky.notification.list(
|
||||
{},
|
||||
{
|
||||
headers: sc.getHeaders(alice),
|
||||
@ -194,7 +194,7 @@ describe('pds notification views', () => {
|
||||
})
|
||||
|
||||
it('updates notifications last seen', async () => {
|
||||
const full = await client.app.bsky.notification.list(
|
||||
const full = await agent.api.app.bsky.notification.list(
|
||||
{},
|
||||
{
|
||||
headers: sc.getHeaders(alice),
|
||||
@ -208,14 +208,14 @@ describe('pds notification views', () => {
|
||||
.where('recordUri', '=', full.data.notifications[3].uri)
|
||||
.executeTakeFirstOrThrow()
|
||||
|
||||
await client.app.bsky.notification.updateSeen(
|
||||
await agent.api.app.bsky.notification.updateSeen(
|
||||
{ seenAt: beforeNotif.indexedAt },
|
||||
{ encoding: 'application/json', headers: sc.getHeaders(alice) },
|
||||
)
|
||||
})
|
||||
|
||||
it('fetches notification count with a last-seen', async () => {
|
||||
const notifCount = await client.app.bsky.notification.getCount(
|
||||
const notifCount = await agent.api.app.bsky.notification.getCount(
|
||||
{},
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -224,7 +224,7 @@ describe('pds notification views', () => {
|
||||
})
|
||||
|
||||
it('fetches notifications with a last-seen', async () => {
|
||||
const notifRes = await client.app.bsky.notification.list(
|
||||
const notifRes = await agent.api.app.bsky.notification.list(
|
||||
{},
|
||||
{
|
||||
headers: sc.getHeaders(alice),
|
||||
|
@ -1,12 +1,12 @@
|
||||
import fs from 'fs/promises'
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/moderationAction'
|
||||
import { runTestServer, forSnapshot, CloseFn, adminAuth } from '../_util'
|
||||
import { SeedClient } from '../seeds/client'
|
||||
import basicSeed from '../seeds/basic'
|
||||
|
||||
describe('pds profile views', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
|
||||
@ -20,8 +20,8 @@ describe('pds profile views', () => {
|
||||
dbPostgresSchema: 'views_profile',
|
||||
})
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await basicSeed(sc)
|
||||
alice = sc.dids.alice
|
||||
bob = sc.dids.bob
|
||||
@ -33,7 +33,7 @@ describe('pds profile views', () => {
|
||||
})
|
||||
|
||||
it('fetches own profile', async () => {
|
||||
const aliceForAlice = await client.app.bsky.actor.getProfile(
|
||||
const aliceForAlice = await agent.api.app.bsky.actor.getProfile(
|
||||
{ actor: alice },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -42,7 +42,7 @@ describe('pds profile views', () => {
|
||||
})
|
||||
|
||||
it("fetches other's profile, with a follow", async () => {
|
||||
const aliceForBob = await client.app.bsky.actor.getProfile(
|
||||
const aliceForBob = await agent.api.app.bsky.actor.getProfile(
|
||||
{ actor: alice },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -51,7 +51,7 @@ describe('pds profile views', () => {
|
||||
})
|
||||
|
||||
it("fetches other's profile, without a follow", async () => {
|
||||
const danForBob = await client.app.bsky.actor.getProfile(
|
||||
const danForBob = await agent.api.app.bsky.actor.getProfile(
|
||||
{ actor: dan },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -60,12 +60,12 @@ describe('pds profile views', () => {
|
||||
})
|
||||
|
||||
it('updates profile', async () => {
|
||||
await client.app.bsky.actor.updateProfile(
|
||||
await agent.api.app.bsky.actor.updateProfile(
|
||||
{ displayName: 'ali', description: 'new descript' },
|
||||
{ headers: sc.getHeaders(alice), encoding: 'application/json' },
|
||||
)
|
||||
|
||||
const aliceForAlice = await client.app.bsky.actor.getProfile(
|
||||
const aliceForAlice = await agent.api.app.bsky.actor.getProfile(
|
||||
{ actor: alice },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -74,12 +74,12 @@ describe('pds profile views', () => {
|
||||
})
|
||||
|
||||
it('handles partial updates', async () => {
|
||||
await client.app.bsky.actor.updateProfile(
|
||||
await agent.api.app.bsky.actor.updateProfile(
|
||||
{ description: 'blah blah' },
|
||||
{ headers: sc.getHeaders(alice), encoding: 'application/json' },
|
||||
)
|
||||
|
||||
const aliceForAlice = await client.app.bsky.actor.getProfile(
|
||||
const aliceForAlice = await agent.api.app.bsky.actor.getProfile(
|
||||
{ actor: alice },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -94,16 +94,16 @@ describe('pds profile views', () => {
|
||||
const bannerImg = await fs.readFile(
|
||||
'tests/image/fixtures/key-landscape-small.jpg',
|
||||
)
|
||||
const avatarRes = await client.com.atproto.blob.upload(avatarImg, {
|
||||
const avatarRes = await agent.api.com.atproto.blob.upload(avatarImg, {
|
||||
headers: sc.getHeaders(alice),
|
||||
encoding: 'image/jpeg',
|
||||
})
|
||||
const bannerRes = await client.com.atproto.blob.upload(bannerImg, {
|
||||
const bannerRes = await agent.api.com.atproto.blob.upload(bannerImg, {
|
||||
headers: sc.getHeaders(alice),
|
||||
encoding: 'image/jpeg',
|
||||
})
|
||||
|
||||
await client.app.bsky.actor.updateProfile(
|
||||
await agent.api.app.bsky.actor.updateProfile(
|
||||
{
|
||||
avatar: { cid: avatarRes.data.cid, mimeType: 'image/jpeg' },
|
||||
banner: { cid: bannerRes.data.cid, mimeType: 'image/jpeg' },
|
||||
@ -111,7 +111,7 @@ describe('pds profile views', () => {
|
||||
{ headers: sc.getHeaders(alice), encoding: 'application/json' },
|
||||
)
|
||||
|
||||
const aliceForAlice = await client.app.bsky.actor.getProfile(
|
||||
const aliceForAlice = await agent.api.app.bsky.actor.getProfile(
|
||||
{ actor: alice },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -120,12 +120,12 @@ describe('pds profile views', () => {
|
||||
})
|
||||
|
||||
it('creates new profile', async () => {
|
||||
await client.app.bsky.actor.updateProfile(
|
||||
await agent.api.app.bsky.actor.updateProfile(
|
||||
{ displayName: 'danny boy' },
|
||||
{ headers: sc.getHeaders(dan), encoding: 'application/json' },
|
||||
)
|
||||
|
||||
const danForDan = await client.app.bsky.actor.getProfile(
|
||||
const danForDan = await agent.api.app.bsky.actor.getProfile(
|
||||
{ actor: dan },
|
||||
{ headers: sc.getHeaders(dan) },
|
||||
)
|
||||
@ -141,14 +141,14 @@ describe('pds profile views', () => {
|
||||
}
|
||||
await Promise.all(
|
||||
descriptions.map(async (description) => {
|
||||
await client.app.bsky.actor.updateProfile(
|
||||
await agent.api.app.bsky.actor.updateProfile(
|
||||
{ description },
|
||||
{ headers: sc.getHeaders(alice), encoding: 'application/json' },
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
const profile = await client.app.bsky.actor.getProfile(
|
||||
const profile = await agent.api.app.bsky.actor.getProfile(
|
||||
{ actor: alice },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -161,14 +161,14 @@ describe('pds profile views', () => {
|
||||
})
|
||||
|
||||
it('fetches profile by handle', async () => {
|
||||
const byDid = await client.app.bsky.actor.getProfile(
|
||||
const byDid = await agent.api.app.bsky.actor.getProfile(
|
||||
{ actor: alice },
|
||||
{
|
||||
headers: sc.getHeaders(bob),
|
||||
},
|
||||
)
|
||||
|
||||
const byHandle = await client.app.bsky.actor.getProfile(
|
||||
const byHandle = await agent.api.app.bsky.actor.getProfile(
|
||||
{ actor: sc.accounts[alice].handle },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -178,7 +178,7 @@ describe('pds profile views', () => {
|
||||
|
||||
it('blocked by actor takedown', async () => {
|
||||
const { data: action } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -193,7 +193,7 @@ describe('pds profile views', () => {
|
||||
headers: { authorization: adminAuth() },
|
||||
},
|
||||
)
|
||||
const promise = client.app.bsky.actor.getProfile(
|
||||
const promise = agent.api.app.bsky.actor.getProfile(
|
||||
{ actor: alice },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -201,7 +201,7 @@ describe('pds profile views', () => {
|
||||
await expect(promise).rejects.toThrow('Account has been taken down')
|
||||
|
||||
// Cleanup
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: action.id,
|
||||
createdBy: 'X',
|
||||
@ -215,37 +215,39 @@ describe('pds profile views', () => {
|
||||
})
|
||||
|
||||
it('includes muted status.', async () => {
|
||||
const { data: initial } = await client.app.bsky.actor.getProfile(
|
||||
const { data: initial } = await agent.api.app.bsky.actor.getProfile(
|
||||
{ actor: alice },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
|
||||
expect(initial.myState?.muted).toEqual(false)
|
||||
|
||||
await client.app.bsky.graph.mute(
|
||||
await agent.api.app.bsky.graph.mute(
|
||||
{ user: alice },
|
||||
{ headers: sc.getHeaders(bob), encoding: 'application/json' },
|
||||
)
|
||||
const { data: muted } = await client.app.bsky.actor.getProfile(
|
||||
const { data: muted } = await agent.api.app.bsky.actor.getProfile(
|
||||
{ actor: alice },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
|
||||
expect(muted.myState?.muted).toEqual(true)
|
||||
|
||||
const { data: fromBobUnrelated } = await client.app.bsky.actor.getProfile(
|
||||
{ actor: dan },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
const { data: toAliceUnrelated } = await client.app.bsky.actor.getProfile(
|
||||
{ actor: alice },
|
||||
{ headers: sc.getHeaders(dan) },
|
||||
)
|
||||
const { data: fromBobUnrelated } =
|
||||
await agent.api.app.bsky.actor.getProfile(
|
||||
{ actor: dan },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
const { data: toAliceUnrelated } =
|
||||
await agent.api.app.bsky.actor.getProfile(
|
||||
{ actor: alice },
|
||||
{ headers: sc.getHeaders(dan) },
|
||||
)
|
||||
|
||||
expect(fromBobUnrelated.myState?.muted).toEqual(false)
|
||||
expect(toAliceUnrelated.myState?.muted).toEqual(false)
|
||||
|
||||
await client.app.bsky.graph.unmute(
|
||||
await agent.api.app.bsky.graph.unmute(
|
||||
{ user: alice },
|
||||
{ headers: sc.getHeaders(bob), encoding: 'application/json' },
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import {
|
||||
runTestServer,
|
||||
forSnapshot,
|
||||
@ -10,7 +10,7 @@ import { SeedClient } from '../seeds/client'
|
||||
import repostsSeed from '../seeds/reposts'
|
||||
|
||||
describe('pds repost views', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
|
||||
@ -23,8 +23,8 @@ describe('pds repost views', () => {
|
||||
dbPostgresSchema: 'views_reposts',
|
||||
})
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await repostsSeed(sc)
|
||||
alice = sc.dids.alice
|
||||
bob = sc.dids.bob
|
||||
@ -43,7 +43,7 @@ describe('pds repost views', () => {
|
||||
const tstamp = (x: string) => new Date(x).getTime()
|
||||
|
||||
it('fetches reposted-by for a post', async () => {
|
||||
const view = await client.app.bsky.feed.getRepostedBy(
|
||||
const view = await agent.api.app.bsky.feed.getRepostedBy(
|
||||
{ uri: sc.posts[alice][2].ref.uriStr },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -55,7 +55,7 @@ describe('pds repost views', () => {
|
||||
})
|
||||
|
||||
it('fetches reposted-by for a reply', async () => {
|
||||
const view = await client.app.bsky.feed.getRepostedBy(
|
||||
const view = await agent.api.app.bsky.feed.getRepostedBy(
|
||||
{ uri: sc.replies[bob][0].ref.uriStr },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -69,7 +69,7 @@ describe('pds repost views', () => {
|
||||
it('paginates', async () => {
|
||||
const results = (results) => results.flatMap((res) => res.repostedBy)
|
||||
const paginator = async (cursor?: string) => {
|
||||
const res = await client.app.bsky.feed.getRepostedBy(
|
||||
const res = await agent.api.app.bsky.feed.getRepostedBy(
|
||||
{
|
||||
uri: sc.posts[alice][2].ref.uriStr,
|
||||
before: cursor,
|
||||
@ -85,7 +85,7 @@ describe('pds repost views', () => {
|
||||
expect(res.repostedBy.length).toBeLessThanOrEqual(2),
|
||||
)
|
||||
|
||||
const full = await client.app.bsky.feed.getRepostedBy(
|
||||
const full = await agent.api.app.bsky.feed.getRepostedBy(
|
||||
{ uri: sc.posts[alice][2].ref.uriStr },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
|
@ -1,10 +1,10 @@
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { runTestServer, CloseFn } from '../_util'
|
||||
import { SeedClient } from '../seeds/client'
|
||||
import basicSeed from '../seeds/basic'
|
||||
|
||||
describe('pds user search views', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
|
||||
@ -13,8 +13,8 @@ describe('pds user search views', () => {
|
||||
dbPostgresSchema: 'views_suggestions',
|
||||
})
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await basicSeed(sc)
|
||||
})
|
||||
|
||||
@ -23,7 +23,7 @@ describe('pds user search views', () => {
|
||||
})
|
||||
|
||||
it('actor suggestion gives users', async () => {
|
||||
const result = await client.app.bsky.actor.getSuggestions(
|
||||
const result = await agent.api.app.bsky.actor.getSuggestions(
|
||||
{ limit: 3 },
|
||||
{ headers: sc.getHeaders(sc.dids.carol) },
|
||||
)
|
||||
@ -45,7 +45,7 @@ describe('pds user search views', () => {
|
||||
})
|
||||
|
||||
it('does not suggest followed users', async () => {
|
||||
const result = await client.app.bsky.actor.getSuggestions(
|
||||
const result = await agent.api.app.bsky.actor.getSuggestions(
|
||||
{ limit: 3 },
|
||||
{ headers: sc.getHeaders(sc.dids.alice) },
|
||||
)
|
||||
@ -55,11 +55,11 @@ describe('pds user search views', () => {
|
||||
})
|
||||
|
||||
it('paginates', async () => {
|
||||
const result1 = await client.app.bsky.actor.getSuggestions(
|
||||
const result1 = await agent.api.app.bsky.actor.getSuggestions(
|
||||
{ limit: 1 },
|
||||
{ headers: sc.getHeaders(sc.dids.carol) },
|
||||
)
|
||||
const result2 = await client.app.bsky.actor.getSuggestions(
|
||||
const result2 = await agent.api.app.bsky.actor.getSuggestions(
|
||||
{ limit: 1, cursor: result1.data.cursor },
|
||||
{ headers: sc.getHeaders(sc.dids.carol) },
|
||||
)
|
||||
|
@ -1,7 +1,4 @@
|
||||
import AtpApi, {
|
||||
ServiceClient as AtpServiceClient,
|
||||
AppBskyFeedGetPostThread,
|
||||
} from '@atproto/api'
|
||||
import AtpAgent, { AppBskyFeedGetPostThread } from '@atproto/api'
|
||||
import { AtUri } from '@atproto/uri'
|
||||
import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/moderationAction'
|
||||
import { runTestServer, forSnapshot, CloseFn, adminAuth } from '../_util'
|
||||
@ -9,7 +6,7 @@ import { SeedClient } from '../seeds/client'
|
||||
import basicSeed from '../seeds/basic'
|
||||
|
||||
describe('pds thread views', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
|
||||
@ -23,8 +20,8 @@ describe('pds thread views', () => {
|
||||
dbPostgresSchema: 'views_thread',
|
||||
})
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await basicSeed(sc)
|
||||
alice = sc.dids.alice
|
||||
bob = sc.dids.bob
|
||||
@ -41,7 +38,7 @@ describe('pds thread views', () => {
|
||||
})
|
||||
|
||||
it('fetches deep post thread', async () => {
|
||||
const thread = await client.app.bsky.feed.getPostThread(
|
||||
const thread = await agent.api.app.bsky.feed.getPostThread(
|
||||
{ uri: sc.posts[alice][1].ref.uriStr },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -50,7 +47,7 @@ describe('pds thread views', () => {
|
||||
})
|
||||
|
||||
it('fetches shallow post thread', async () => {
|
||||
const thread = await client.app.bsky.feed.getPostThread(
|
||||
const thread = await agent.api.app.bsky.feed.getPostThread(
|
||||
{ depth: 1, uri: sc.posts[alice][1].ref.uriStr },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -59,7 +56,7 @@ describe('pds thread views', () => {
|
||||
})
|
||||
|
||||
it('fetches ancestors', async () => {
|
||||
const thread = await client.app.bsky.feed.getPostThread(
|
||||
const thread = await agent.api.app.bsky.feed.getPostThread(
|
||||
{ depth: 1, uri: sc.replies[alice][0].ref.uriStr },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -68,7 +65,7 @@ describe('pds thread views', () => {
|
||||
})
|
||||
|
||||
it('fails for an unknown post', async () => {
|
||||
const promise = client.app.bsky.feed.getPostThread(
|
||||
const promise = agent.api.app.bsky.feed.getPostThread(
|
||||
{ uri: 'does.not.exist' },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -79,18 +76,18 @@ describe('pds thread views', () => {
|
||||
})
|
||||
|
||||
it('includes the muted status of post authors.', async () => {
|
||||
await client.app.bsky.graph.mute(
|
||||
await agent.api.app.bsky.graph.mute(
|
||||
{ user: alice },
|
||||
{ headers: sc.getHeaders(bob), encoding: 'application/json' },
|
||||
)
|
||||
const thread = await client.app.bsky.feed.getPostThread(
|
||||
const thread = await agent.api.app.bsky.feed.getPostThread(
|
||||
{ uri: sc.posts[alice][1].ref.uriStr },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
|
||||
expect(forSnapshot(thread.data.thread)).toMatchSnapshot()
|
||||
|
||||
await client.app.bsky.graph.unmute(
|
||||
await agent.api.app.bsky.graph.unmute(
|
||||
{ user: alice },
|
||||
{ encoding: 'application/json', headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -124,7 +121,7 @@ describe('pds thread views', () => {
|
||||
)
|
||||
indexes.aliceReplyReply = sc.replies[alice].length - 1
|
||||
|
||||
const thread1 = await client.app.bsky.feed.getPostThread(
|
||||
const thread1 = await agent.api.app.bsky.feed.getPostThread(
|
||||
{ uri: sc.posts[alice][indexes.aliceRoot].ref.uriStr },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -132,13 +129,13 @@ describe('pds thread views', () => {
|
||||
|
||||
await sc.deletePost(bob, sc.replies[bob][indexes.bobReply].ref.uri)
|
||||
|
||||
const thread2 = await client.app.bsky.feed.getPostThread(
|
||||
const thread2 = await agent.api.app.bsky.feed.getPostThread(
|
||||
{ uri: sc.posts[alice][indexes.aliceRoot].ref.uriStr },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
expect(forSnapshot(thread2.data.thread)).toMatchSnapshot()
|
||||
|
||||
const thread3 = await client.app.bsky.feed.getPostThread(
|
||||
const thread3 = await agent.api.app.bsky.feed.getPostThread(
|
||||
{ uri: sc.replies[alice][indexes.aliceReplyReply].ref.uriStr },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -147,7 +144,7 @@ describe('pds thread views', () => {
|
||||
|
||||
it('blocks post by actor takedown', async () => {
|
||||
const { data: modAction } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -164,7 +161,7 @@ describe('pds thread views', () => {
|
||||
)
|
||||
|
||||
// Same as shallow post thread test, minus alice
|
||||
const promise = client.app.bsky.feed.getPostThread(
|
||||
const promise = agent.api.app.bsky.feed.getPostThread(
|
||||
{ depth: 1, uri: sc.posts[alice][1].ref.uriStr },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -174,7 +171,7 @@ describe('pds thread views', () => {
|
||||
)
|
||||
|
||||
// Cleanup
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: modAction.id,
|
||||
createdBy: 'X',
|
||||
@ -189,7 +186,7 @@ describe('pds thread views', () => {
|
||||
|
||||
it('blocks replies by actor takedown', async () => {
|
||||
const { data: modAction } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -206,7 +203,7 @@ describe('pds thread views', () => {
|
||||
)
|
||||
|
||||
// Same as deep post thread test, minus carol
|
||||
const thread = await client.app.bsky.feed.getPostThread(
|
||||
const thread = await agent.api.app.bsky.feed.getPostThread(
|
||||
{ uri: sc.posts[alice][1].ref.uriStr },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -214,7 +211,7 @@ describe('pds thread views', () => {
|
||||
expect(forSnapshot(thread.data.thread)).toMatchSnapshot()
|
||||
|
||||
// Cleanup
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: modAction.id,
|
||||
createdBy: 'X',
|
||||
@ -229,7 +226,7 @@ describe('pds thread views', () => {
|
||||
|
||||
it('blocks ancestors by actor takedown', async () => {
|
||||
const { data: modAction } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -246,7 +243,7 @@ describe('pds thread views', () => {
|
||||
)
|
||||
|
||||
// Same as ancestor post thread test, minus bob
|
||||
const thread = await client.app.bsky.feed.getPostThread(
|
||||
const thread = await agent.api.app.bsky.feed.getPostThread(
|
||||
{ depth: 1, uri: sc.replies[alice][0].ref.uriStr },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -254,7 +251,7 @@ describe('pds thread views', () => {
|
||||
expect(forSnapshot(thread.data.thread)).toMatchSnapshot()
|
||||
|
||||
// Cleanup
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: modAction.id,
|
||||
createdBy: 'X',
|
||||
@ -270,7 +267,7 @@ describe('pds thread views', () => {
|
||||
it('blocks post by record takedown', async () => {
|
||||
const postUri = sc.posts[alice][1].ref.uri
|
||||
const { data: modAction } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -286,7 +283,7 @@ describe('pds thread views', () => {
|
||||
},
|
||||
)
|
||||
|
||||
const promise = client.app.bsky.feed.getPostThread(
|
||||
const promise = agent.api.app.bsky.feed.getPostThread(
|
||||
{ depth: 1, uri: postUri.toString() },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -296,7 +293,7 @@ describe('pds thread views', () => {
|
||||
)
|
||||
|
||||
// Cleanup
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: modAction.id,
|
||||
createdBy: 'X',
|
||||
@ -310,7 +307,7 @@ describe('pds thread views', () => {
|
||||
})
|
||||
|
||||
it('blocks ancestors by record takedown', async () => {
|
||||
const threadPreTakedown = await client.app.bsky.feed.getPostThread(
|
||||
const threadPreTakedown = await agent.api.app.bsky.feed.getPostThread(
|
||||
{ depth: 1, uri: sc.replies[alice][0].ref.uriStr },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -319,7 +316,7 @@ describe('pds thread views', () => {
|
||||
)
|
||||
|
||||
const { data: modAction } =
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -336,7 +333,7 @@ describe('pds thread views', () => {
|
||||
)
|
||||
|
||||
// Same as ancestor post thread test, minus parent post
|
||||
const thread = await client.app.bsky.feed.getPostThread(
|
||||
const thread = await agent.api.app.bsky.feed.getPostThread(
|
||||
{ depth: 1, uri: sc.replies[alice][0].ref.uriStr },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -344,7 +341,7 @@ describe('pds thread views', () => {
|
||||
expect(forSnapshot(thread.data.thread)).toMatchSnapshot()
|
||||
|
||||
// Cleanup
|
||||
await client.com.atproto.admin.reverseModerationAction(
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: modAction.id,
|
||||
createdBy: 'X',
|
||||
@ -358,7 +355,7 @@ describe('pds thread views', () => {
|
||||
})
|
||||
|
||||
it('blocks replies by record takedown', async () => {
|
||||
const threadPreTakedown = await client.app.bsky.feed.getPostThread(
|
||||
const threadPreTakedown = await agent.api.app.bsky.feed.getPostThread(
|
||||
{ uri: sc.posts[alice][1].ref.uriStr },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -371,7 +368,7 @@ describe('pds thread views', () => {
|
||||
|
||||
const actionResults = await Promise.all(
|
||||
[postUri1, postUri2].map((postUri) =>
|
||||
client.com.atproto.admin.takeModerationAction(
|
||||
agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -390,7 +387,7 @@ describe('pds thread views', () => {
|
||||
)
|
||||
|
||||
// Same as deep post thread test, minus some replies
|
||||
const thread = await client.app.bsky.feed.getPostThread(
|
||||
const thread = await agent.api.app.bsky.feed.getPostThread(
|
||||
{ uri: sc.posts[alice][1].ref.uriStr },
|
||||
{ headers: sc.getHeaders(bob) },
|
||||
)
|
||||
@ -400,7 +397,7 @@ describe('pds thread views', () => {
|
||||
// Cleanup
|
||||
await Promise.all(
|
||||
actionResults.map((result) =>
|
||||
client.com.atproto.admin.reverseModerationAction(
|
||||
agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: result.data.id,
|
||||
createdBy: 'X',
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/moderationAction'
|
||||
import { Main as FeedViewPost } from '../../src/lexicon/types/app/bsky/feed/feedViewPost'
|
||||
import {
|
||||
@ -14,7 +14,7 @@ import basicSeed from '../seeds/basic'
|
||||
import { FeedAlgorithm } from '../../src/api/app/bsky/util/feed'
|
||||
|
||||
describe('timeline views', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
|
||||
@ -29,8 +29,8 @@ describe('timeline views', () => {
|
||||
dbPostgresSchema: 'views_home_feed',
|
||||
})
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await basicSeed(sc, server.ctx.messageQueue)
|
||||
alice = sc.dids.alice
|
||||
bob = sc.dids.bob
|
||||
@ -51,7 +51,7 @@ describe('timeline views', () => {
|
||||
}
|
||||
}
|
||||
|
||||
const aliceTL = await client.app.bsky.feed.getTimeline(
|
||||
const aliceTL = await agent.api.app.bsky.feed.getTimeline(
|
||||
{ algorithm: FeedAlgorithm.ReverseChronological },
|
||||
{
|
||||
headers: sc.getHeaders(alice),
|
||||
@ -61,7 +61,7 @@ describe('timeline views', () => {
|
||||
expect(forSnapshot(aliceTL.data.feed)).toMatchSnapshot()
|
||||
aliceTL.data.feed.forEach(expectOriginatorFollowedBy(alice))
|
||||
|
||||
const bobTL = await client.app.bsky.feed.getTimeline(
|
||||
const bobTL = await agent.api.app.bsky.feed.getTimeline(
|
||||
{ algorithm: FeedAlgorithm.ReverseChronological },
|
||||
{
|
||||
headers: sc.getHeaders(bob),
|
||||
@ -71,7 +71,7 @@ describe('timeline views', () => {
|
||||
expect(forSnapshot(bobTL.data.feed)).toMatchSnapshot()
|
||||
bobTL.data.feed.forEach(expectOriginatorFollowedBy(bob))
|
||||
|
||||
const carolTL = await client.app.bsky.feed.getTimeline(
|
||||
const carolTL = await agent.api.app.bsky.feed.getTimeline(
|
||||
{ algorithm: FeedAlgorithm.ReverseChronological },
|
||||
{
|
||||
headers: sc.getHeaders(carol),
|
||||
@ -81,7 +81,7 @@ describe('timeline views', () => {
|
||||
expect(forSnapshot(carolTL.data.feed)).toMatchSnapshot()
|
||||
carolTL.data.feed.forEach(expectOriginatorFollowedBy(carol))
|
||||
|
||||
const danTL = await client.app.bsky.feed.getTimeline(
|
||||
const danTL = await agent.api.app.bsky.feed.getTimeline(
|
||||
{ algorithm: FeedAlgorithm.ReverseChronological },
|
||||
{
|
||||
headers: sc.getHeaders(dan),
|
||||
@ -93,13 +93,13 @@ describe('timeline views', () => {
|
||||
})
|
||||
|
||||
it("fetches authenticated user's home feed w/ default algorithm", async () => {
|
||||
const defaultTL = await client.app.bsky.feed.getTimeline(
|
||||
const defaultTL = await agent.api.app.bsky.feed.getTimeline(
|
||||
{},
|
||||
{
|
||||
headers: sc.getHeaders(alice),
|
||||
},
|
||||
)
|
||||
const reverseChronologicalTL = await client.app.bsky.feed.getTimeline(
|
||||
const reverseChronologicalTL = await agent.api.app.bsky.feed.getTimeline(
|
||||
{ algorithm: FeedAlgorithm.ReverseChronological },
|
||||
{
|
||||
headers: sc.getHeaders(alice),
|
||||
@ -109,16 +109,16 @@ describe('timeline views', () => {
|
||||
})
|
||||
|
||||
it('omits posts and reposts of muted authors.', async () => {
|
||||
await client.app.bsky.graph.mute(
|
||||
await agent.api.app.bsky.graph.mute(
|
||||
{ user: bob },
|
||||
{ headers: sc.getHeaders(alice), encoding: 'application/json' },
|
||||
)
|
||||
await client.app.bsky.graph.mute(
|
||||
await agent.api.app.bsky.graph.mute(
|
||||
{ user: carol },
|
||||
{ headers: sc.getHeaders(alice), encoding: 'application/json' },
|
||||
)
|
||||
|
||||
const aliceTL = await client.app.bsky.feed.getTimeline(
|
||||
const aliceTL = await agent.api.app.bsky.feed.getTimeline(
|
||||
{ algorithm: FeedAlgorithm.ReverseChronological },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -126,11 +126,11 @@ describe('timeline views', () => {
|
||||
expect(forSnapshot(aliceTL.data.feed)).toMatchSnapshot()
|
||||
|
||||
// Cleanup
|
||||
await client.app.bsky.graph.unmute(
|
||||
await agent.api.app.bsky.graph.unmute(
|
||||
{ user: bob },
|
||||
{ encoding: 'application/json', headers: sc.getHeaders(alice) },
|
||||
)
|
||||
await client.app.bsky.graph.unmute(
|
||||
await agent.api.app.bsky.graph.unmute(
|
||||
{ user: carol },
|
||||
{ encoding: 'application/json', headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -139,7 +139,7 @@ describe('timeline views', () => {
|
||||
it('paginates reverse-chronological feed', async () => {
|
||||
const results = (results) => results.flatMap((res) => res.feed)
|
||||
const paginator = async (cursor?: string) => {
|
||||
const res = await client.app.bsky.feed.getTimeline(
|
||||
const res = await agent.api.app.bsky.feed.getTimeline(
|
||||
{
|
||||
algorithm: FeedAlgorithm.ReverseChronological,
|
||||
before: cursor,
|
||||
@ -155,7 +155,7 @@ describe('timeline views', () => {
|
||||
expect(res.feed.length).toBeLessThanOrEqual(4),
|
||||
)
|
||||
|
||||
const full = await client.app.bsky.feed.getTimeline(
|
||||
const full = await agent.api.app.bsky.feed.getTimeline(
|
||||
{
|
||||
algorithm: FeedAlgorithm.ReverseChronological,
|
||||
},
|
||||
@ -169,7 +169,7 @@ describe('timeline views', () => {
|
||||
it('blocks posts, reposts, replies by actor takedown', async () => {
|
||||
const actionResults = await Promise.all(
|
||||
[bob, dan].map((did) =>
|
||||
client.com.atproto.admin.takeModerationAction(
|
||||
agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -187,7 +187,7 @@ describe('timeline views', () => {
|
||||
),
|
||||
)
|
||||
|
||||
const aliceTL = await client.app.bsky.feed.getTimeline(
|
||||
const aliceTL = await agent.api.app.bsky.feed.getTimeline(
|
||||
{ algorithm: FeedAlgorithm.ReverseChronological },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -197,7 +197,7 @@ describe('timeline views', () => {
|
||||
// Cleanup
|
||||
await Promise.all(
|
||||
actionResults.map((result) =>
|
||||
client.com.atproto.admin.reverseModerationAction(
|
||||
agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: result.data.id,
|
||||
createdBy: 'X',
|
||||
@ -217,7 +217,7 @@ describe('timeline views', () => {
|
||||
const postUri2 = sc.replies[bob][0].ref.uri // Post and reply parent
|
||||
const actionResults = await Promise.all(
|
||||
[postUri1, postUri2].map((postUri) =>
|
||||
client.com.atproto.admin.takeModerationAction(
|
||||
agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -235,7 +235,7 @@ describe('timeline views', () => {
|
||||
),
|
||||
)
|
||||
|
||||
const aliceTL = await client.app.bsky.feed.getTimeline(
|
||||
const aliceTL = await agent.api.app.bsky.feed.getTimeline(
|
||||
{ algorithm: FeedAlgorithm.ReverseChronological },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -245,7 +245,7 @@ describe('timeline views', () => {
|
||||
// Cleanup
|
||||
await Promise.all(
|
||||
actionResults.map((result) =>
|
||||
client.com.atproto.admin.reverseModerationAction(
|
||||
agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: result.data.id,
|
||||
createdBy: 'X',
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpApi, { ServiceClient as AtpServiceClient } from '@atproto/api'
|
||||
import AtpAgent from '@atproto/api'
|
||||
import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/moderationAction'
|
||||
import {
|
||||
runTestServer,
|
||||
@ -12,7 +12,7 @@ import usersBulkSeed from '../seeds/users-bulk'
|
||||
import { Database } from '../../src'
|
||||
|
||||
describe('pds user search views', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let db: Database
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
@ -24,8 +24,8 @@ describe('pds user search views', () => {
|
||||
})
|
||||
close = server.close
|
||||
db = server.ctx.db
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await usersBulkSeed(sc)
|
||||
headers = sc.getHeaders(Object.values(sc.dids)[0])
|
||||
})
|
||||
@ -35,7 +35,7 @@ describe('pds user search views', () => {
|
||||
})
|
||||
|
||||
it('typeahead gives relevant results', async () => {
|
||||
const result = await client.app.bsky.actor.searchTypeahead(
|
||||
const result = await agent.api.app.bsky.actor.searchTypeahead(
|
||||
{ term: 'car' },
|
||||
{ headers },
|
||||
)
|
||||
@ -79,7 +79,7 @@ describe('pds user search views', () => {
|
||||
})
|
||||
|
||||
it('typeahead gives empty result set when provided empty term', async () => {
|
||||
const result = await client.app.bsky.actor.searchTypeahead(
|
||||
const result = await agent.api.app.bsky.actor.searchTypeahead(
|
||||
{ term: '' },
|
||||
{ headers },
|
||||
)
|
||||
@ -88,14 +88,14 @@ describe('pds user search views', () => {
|
||||
})
|
||||
|
||||
it('typeahead applies limit', async () => {
|
||||
const full = await client.app.bsky.actor.searchTypeahead(
|
||||
const full = await agent.api.app.bsky.actor.searchTypeahead(
|
||||
{ term: 'p' },
|
||||
{ headers },
|
||||
)
|
||||
|
||||
expect(full.data.users.length).toBeGreaterThan(5)
|
||||
|
||||
const limited = await client.app.bsky.actor.searchTypeahead(
|
||||
const limited = await agent.api.app.bsky.actor.searchTypeahead(
|
||||
{ term: 'p', limit: 5 },
|
||||
{ headers },
|
||||
)
|
||||
@ -104,7 +104,7 @@ describe('pds user search views', () => {
|
||||
})
|
||||
|
||||
it('search gives relevant results', async () => {
|
||||
const result = await client.app.bsky.actor.search(
|
||||
const result = await agent.api.app.bsky.actor.search(
|
||||
{ term: 'car' },
|
||||
{ headers },
|
||||
)
|
||||
@ -148,7 +148,10 @@ describe('pds user search views', () => {
|
||||
})
|
||||
|
||||
it('search gives empty result set when provided empty term', async () => {
|
||||
const result = await client.app.bsky.actor.search({ term: '' }, { headers })
|
||||
const result = await agent.api.app.bsky.actor.search(
|
||||
{ term: '' },
|
||||
{ headers },
|
||||
)
|
||||
|
||||
expect(result.data.users).toEqual([])
|
||||
})
|
||||
@ -156,7 +159,7 @@ describe('pds user search views', () => {
|
||||
it('paginates', async () => {
|
||||
const results = (results) => results.flatMap((res) => res.users)
|
||||
const paginator = async (cursor?: string) => {
|
||||
const res = await client.app.bsky.actor.search(
|
||||
const res = await agent.api.app.bsky.actor.search(
|
||||
{ term: 'p', before: cursor, limit: 3 },
|
||||
{ headers },
|
||||
)
|
||||
@ -168,7 +171,10 @@ describe('pds user search views', () => {
|
||||
expect(res.users.length).toBeLessThanOrEqual(3),
|
||||
)
|
||||
|
||||
const full = await client.app.bsky.actor.search({ term: 'p' }, { headers })
|
||||
const full = await agent.api.app.bsky.actor.search(
|
||||
{ term: 'p' },
|
||||
{ headers },
|
||||
)
|
||||
|
||||
expect(full.data.users.length).toBeGreaterThan(5)
|
||||
expect(results(paginatedAll)).toEqual(results([full.data]))
|
||||
@ -177,7 +183,7 @@ describe('pds user search views', () => {
|
||||
it('search handles bad input', async () => {
|
||||
// Mostly for sqlite's benefit, since it uses LIKE and these are special characters that will
|
||||
// get stripped. This input triggers a special case where there are no "safe" words for sqlite to search on.
|
||||
const result = await client.app.bsky.actor.search(
|
||||
const result = await agent.api.app.bsky.actor.search(
|
||||
{ term: ' % _ ' },
|
||||
{ headers },
|
||||
)
|
||||
@ -186,7 +192,7 @@ describe('pds user search views', () => {
|
||||
})
|
||||
|
||||
it('search blocks by actor takedown', async () => {
|
||||
await client.com.atproto.admin.takeModerationAction(
|
||||
await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
subject: {
|
||||
@ -201,7 +207,7 @@ describe('pds user search views', () => {
|
||||
headers: { authorization: adminAuth() },
|
||||
},
|
||||
)
|
||||
const result = await client.app.bsky.actor.searchTypeahead(
|
||||
const result = await agent.api.app.bsky.actor.searchTypeahead(
|
||||
{ term: 'car' },
|
||||
{ headers },
|
||||
)
|
||||
|
@ -1,7 +1,4 @@
|
||||
import AtpApi, {
|
||||
AppBskyFeedGetPostThread,
|
||||
ServiceClient as AtpServiceClient,
|
||||
} from '@atproto/api'
|
||||
import AtpAgent, { AppBskyFeedGetPostThread } from '@atproto/api'
|
||||
import { SeedClient } from '../seeds/client'
|
||||
import votesSeed from '../seeds/votes'
|
||||
import {
|
||||
@ -13,7 +10,7 @@ import {
|
||||
} from '../_util'
|
||||
|
||||
describe('pds vote views', () => {
|
||||
let client: AtpServiceClient
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
|
||||
@ -26,8 +23,8 @@ describe('pds vote views', () => {
|
||||
dbPostgresSchema: 'views_votes',
|
||||
})
|
||||
close = server.close
|
||||
client = AtpApi.service(server.url)
|
||||
sc = new SeedClient(client)
|
||||
agent = new AtpAgent({ service: server.url })
|
||||
sc = new SeedClient(agent)
|
||||
await votesSeed(sc)
|
||||
alice = sc.dids.alice
|
||||
bob = sc.dids.bob
|
||||
@ -46,7 +43,7 @@ describe('pds vote views', () => {
|
||||
const tstamp = (x: string) => new Date(x).getTime()
|
||||
|
||||
it('fetches post votes', async () => {
|
||||
const alicePost = await client.app.bsky.feed.getVotes(
|
||||
const alicePost = await agent.api.app.bsky.feed.getVotes(
|
||||
{ uri: sc.posts[alice][1].ref.uriStr },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -58,7 +55,7 @@ describe('pds vote views', () => {
|
||||
})
|
||||
|
||||
it('fetches reply votes', async () => {
|
||||
const bobReply = await client.app.bsky.feed.getVotes(
|
||||
const bobReply = await agent.api.app.bsky.feed.getVotes(
|
||||
{ uri: sc.replies[bob][0].ref.uriStr },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -72,7 +69,7 @@ describe('pds vote views', () => {
|
||||
it('paginates', async () => {
|
||||
const results = (results) => results.flatMap((res) => res.votes)
|
||||
const paginator = async (cursor?: string) => {
|
||||
const res = await client.app.bsky.feed.getVotes(
|
||||
const res = await agent.api.app.bsky.feed.getVotes(
|
||||
{
|
||||
uri: sc.posts[alice][1].ref.uriStr,
|
||||
before: cursor,
|
||||
@ -88,7 +85,7 @@ describe('pds vote views', () => {
|
||||
expect(res.votes.length).toBeLessThanOrEqual(2),
|
||||
)
|
||||
|
||||
const full = await client.app.bsky.feed.getVotes(
|
||||
const full = await agent.api.app.bsky.feed.getVotes(
|
||||
{ uri: sc.posts[alice][1].ref.uriStr },
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
@ -98,14 +95,14 @@ describe('pds vote views', () => {
|
||||
})
|
||||
|
||||
it('filters by direction', async () => {
|
||||
const full = await client.app.bsky.feed.getVotes(
|
||||
const full = await agent.api.app.bsky.feed.getVotes(
|
||||
{
|
||||
uri: sc.posts[alice][1].ref.uriStr,
|
||||
},
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
|
||||
const upvotes = await client.app.bsky.feed.getVotes(
|
||||
const upvotes = await agent.api.app.bsky.feed.getVotes(
|
||||
{
|
||||
uri: sc.posts[alice][1].ref.uriStr,
|
||||
direction: 'up',
|
||||
@ -113,7 +110,7 @@ describe('pds vote views', () => {
|
||||
{ headers: sc.getHeaders(alice) },
|
||||
)
|
||||
|
||||
const downvotes = await client.app.bsky.feed.getVotes(
|
||||
const downvotes = await agent.api.app.bsky.feed.getVotes(
|
||||
{
|
||||
uri: sc.posts[alice][1].ref.uriStr,
|
||||
direction: 'down',
|
||||
@ -150,7 +147,7 @@ describe('pds vote views', () => {
|
||||
|
||||
let post: AppBskyFeedGetPostThread.OutputSchema
|
||||
const getPost = async () => {
|
||||
const result = await client.app.bsky.feed.getPostThread(
|
||||
const result = await agent.api.app.bsky.feed.getPostThread(
|
||||
{
|
||||
uri: sc.posts[bob][0].ref.uriStr,
|
||||
depth: 0,
|
||||
@ -170,7 +167,7 @@ describe('pds vote views', () => {
|
||||
).toEqual({})
|
||||
|
||||
// Upvote
|
||||
const { data: upvoted } = await client.app.bsky.feed.setVote(
|
||||
const { data: upvoted } = await agent.api.app.bsky.feed.setVote(
|
||||
{
|
||||
direction: 'up',
|
||||
subject: {
|
||||
@ -196,7 +193,7 @@ describe('pds vote views', () => {
|
||||
).toEqual(upvoted)
|
||||
|
||||
// Downvote
|
||||
const { data: downvoted } = await client.app.bsky.feed.setVote(
|
||||
const { data: downvoted } = await agent.api.app.bsky.feed.setVote(
|
||||
{
|
||||
direction: 'down',
|
||||
subject: {
|
||||
@ -222,7 +219,7 @@ describe('pds vote views', () => {
|
||||
).toEqual(downvoted)
|
||||
|
||||
// No vote
|
||||
const { data: novoted } = await client.app.bsky.feed.setVote(
|
||||
const { data: novoted } = await agent.api.app.bsky.feed.setVote(
|
||||
{
|
||||
direction: 'none',
|
||||
subject: {
|
||||
@ -254,7 +251,7 @@ describe('pds vote views', () => {
|
||||
headers: sc.getHeaders(alice),
|
||||
} as const
|
||||
|
||||
const { data: upvotedA } = await client.app.bsky.feed.setVote(
|
||||
const { data: upvotedA } = await agent.api.app.bsky.feed.setVote(
|
||||
{
|
||||
direction: 'up',
|
||||
subject: {
|
||||
@ -265,7 +262,7 @@ describe('pds vote views', () => {
|
||||
asAlice,
|
||||
)
|
||||
|
||||
const { data: upvotedB } = await client.app.bsky.feed.setVote(
|
||||
const { data: upvotedB } = await agent.api.app.bsky.feed.setVote(
|
||||
{
|
||||
direction: 'up',
|
||||
subject: {
|
||||
|
@ -130,7 +130,7 @@ export class ServiceClient {
|
||||
}
|
||||
}
|
||||
|
||||
async function defaultFetchHandler(
|
||||
export async function defaultFetchHandler(
|
||||
httpUri: string,
|
||||
httpMethod: string,
|
||||
httpHeaders: Headers,
|
||||
|
Loading…
x
Reference in New Issue
Block a user