Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/appview-v2' into appview-v2-testing
Browse files Browse the repository at this point in the history
  • Loading branch information
devinivy committed Jan 31, 2024
2 parents ebed885 + 1970529 commit c4ab360
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 20 deletions.
10 changes: 10 additions & 0 deletions packages/bsky/proto/bsky.proto
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,15 @@ message GetSuggestedFeedsResponse {
string cursor = 2;
}

message SearchFeedGeneratorsRequest {
string query = 1;
int32 limit = 2;
}

message SearchFeedGeneratorsResponse {
repeated string uris = 1;
}

// - Returns feed generator validity and online status with uris A, B, C…
// - Not currently being used, but could be worhthwhile.
message GetFeedGeneratorStatusRequest {
Expand Down Expand Up @@ -997,6 +1006,7 @@ service Service {
rpc GetActorFeeds(GetActorFeedsRequest) returns (GetActorFeedsResponse);
rpc GetSuggestedFeeds(GetSuggestedFeedsRequest) returns (GetSuggestedFeedsResponse);
rpc GetFeedGeneratorStatus(GetFeedGeneratorStatusRequest) returns (GetFeedGeneratorStatusResponse);
rpc SearchFeedGenerators(SearchFeedGeneratorsRequest) returns (SearchFeedGeneratorsResponse);

// Feeds
rpc GetAuthorFeed(GetAuthorFeedRequest) returns (GetAuthorFeedResponse);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,26 @@ export default function (server: Server, ctx: AppContext) {
}
}

const suggestedRes = await ctx.dataplane.getSuggestedFeeds({
actorDid: viewer ?? undefined,
limit: params.limit,
cursor: params.cursor,
})
const uris = suggestedRes.uris
let uris: string[]
let cursor: string | undefined

const query = params.query?.trim() ?? ''
if (query) {
const res = await ctx.dataplane.searchFeedGenerators({
query,
limit: params.limit,
})
uris = res.uris
} else {
const res = await ctx.dataplane.getSuggestedFeeds({
actorDid: viewer ?? undefined,
limit: params.limit,
cursor: params.cursor,
})
uris = res.uris
cursor = parseString(res.cursor)
}

const hydration = await ctx.hydrator.hydrateFeedGens(uris, viewer)
const feedViews = mapDefined(uris, (uri) =>
ctx.views.feedGenerator(uri, hydration),
Expand All @@ -35,7 +49,7 @@ export default function (server: Server, ctx: AppContext) {
encoding: 'application/json',
body: {
feeds: feedViews,
cursor: parseString(suggestedRes.cursor),
cursor,
},
}
},
Expand Down
25 changes: 24 additions & 1 deletion packages/bsky/src/data-plane/server/routes/feed-gens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,37 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
}
},

async getSuggestedFeeds() {
async getSuggestedFeeds(req) {
const feeds = await db.db
.selectFrom('suggested_feed')
.orderBy('suggested_feed.order', 'asc')
.if(!!req.cursor, (q) => q.where('order', '>', parseInt(req.cursor, 10)))
.limit(req.limit || 50)
.selectAll()
.execute()
return {
uris: feeds.map((f) => f.uri),
cursor: feeds.at(-1)?.order.toString(),
}
},

async searchFeedGenerators(req) {
const { ref } = db.db.dynamic
const limit = req.limit
const query = req.query.trim()
let builder = db.db
.selectFrom('feed_generator')
.if(!!query, (q) => q.where('displayName', 'ilike', `%${query}%`))
.selectAll()
const keyset = new TimeCidKeyset(
ref('feed_generator.createdAt'),
ref('feed_generator.cid'),
)
builder = paginate(builder, { limit, keyset })
const feeds = await builder.execute()
return {
uris: feeds.map((f) => f.uri),
cursor: keyset.packFromResult(feeds),
}
},

Expand Down
4 changes: 3 additions & 1 deletion packages/bsky/src/hydration/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ export type RelationshipPair = [didA: string, didB: string]

const dedupePairs = (pairs: RelationshipPair[]): RelationshipPair[] => {
const mapped = pairs.reduce((acc, cur) => {
const sorted = cur.sort()
const sorted = ([...cur] as RelationshipPair).sort()
acc[sorted.join('-')] = sorted
return acc
}, {} as Record<string, RelationshipPair>)
return Object.values(mapped)
}

export class Blocks {
_blocks: Map<string, boolean> = new Map()
constructor() {}
Expand All @@ -55,6 +56,7 @@ export class Blocks {
}

isBlocked(didA: string, didB: string): boolean {
if (didA === didB) return false // ignore self-blocks
const key = Blocks.key(didA, didB)
return this._blocks.get(key) ?? false
}
Expand Down
11 changes: 11 additions & 0 deletions packages/bsky/src/proto/bsky_connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ import {
PingResponse,
SearchActorsRequest,
SearchActorsResponse,
SearchFeedGeneratorsRequest,
SearchFeedGeneratorsResponse,
SearchPostsRequest,
SearchPostsResponse,
TakedownActorRequest,
Expand Down Expand Up @@ -620,6 +622,15 @@ export const Service = {
O: GetFeedGeneratorStatusResponse,
kind: MethodKind.Unary,
},
/**
* @generated from rpc bsky.Service.SearchFeedGenerators
*/
searchFeedGenerators: {
name: 'SearchFeedGenerators',
I: SearchFeedGeneratorsRequest,
O: SearchFeedGeneratorsResponse,
kind: MethodKind.Unary,
},
/**
* Feeds
*
Expand Down
125 changes: 125 additions & 0 deletions packages/bsky/src/proto/bsky_pb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6546,6 +6546,131 @@ export class GetSuggestedFeedsResponse extends Message<GetSuggestedFeedsResponse
}
}

/**
* @generated from message bsky.SearchFeedGeneratorsRequest
*/
export class SearchFeedGeneratorsRequest extends Message<SearchFeedGeneratorsRequest> {
/**
* @generated from field: string query = 1;
*/
query = ''

/**
* @generated from field: int32 limit = 2;
*/
limit = 0

constructor(data?: PartialMessage<SearchFeedGeneratorsRequest>) {
super()
proto3.util.initPartial(data, this)
}

static readonly runtime: typeof proto3 = proto3
static readonly typeName = 'bsky.SearchFeedGeneratorsRequest'
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: 'query', kind: 'scalar', T: 9 /* ScalarType.STRING */ },
{ no: 2, name: 'limit', kind: 'scalar', T: 5 /* ScalarType.INT32 */ },
])

static fromBinary(
bytes: Uint8Array,
options?: Partial<BinaryReadOptions>,
): SearchFeedGeneratorsRequest {
return new SearchFeedGeneratorsRequest().fromBinary(bytes, options)
}

static fromJson(
jsonValue: JsonValue,
options?: Partial<JsonReadOptions>,
): SearchFeedGeneratorsRequest {
return new SearchFeedGeneratorsRequest().fromJson(jsonValue, options)
}

static fromJsonString(
jsonString: string,
options?: Partial<JsonReadOptions>,
): SearchFeedGeneratorsRequest {
return new SearchFeedGeneratorsRequest().fromJsonString(jsonString, options)
}

static equals(
a:
| SearchFeedGeneratorsRequest
| PlainMessage<SearchFeedGeneratorsRequest>
| undefined,
b:
| SearchFeedGeneratorsRequest
| PlainMessage<SearchFeedGeneratorsRequest>
| undefined,
): boolean {
return proto3.util.equals(SearchFeedGeneratorsRequest, a, b)
}
}

/**
* @generated from message bsky.SearchFeedGeneratorsResponse
*/
export class SearchFeedGeneratorsResponse extends Message<SearchFeedGeneratorsResponse> {
/**
* @generated from field: repeated string uris = 1;
*/
uris: string[] = []

constructor(data?: PartialMessage<SearchFeedGeneratorsResponse>) {
super()
proto3.util.initPartial(data, this)
}

static readonly runtime: typeof proto3 = proto3
static readonly typeName = 'bsky.SearchFeedGeneratorsResponse'
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{
no: 1,
name: 'uris',
kind: 'scalar',
T: 9 /* ScalarType.STRING */,
repeated: true,
},
])

static fromBinary(
bytes: Uint8Array,
options?: Partial<BinaryReadOptions>,
): SearchFeedGeneratorsResponse {
return new SearchFeedGeneratorsResponse().fromBinary(bytes, options)
}

static fromJson(
jsonValue: JsonValue,
options?: Partial<JsonReadOptions>,
): SearchFeedGeneratorsResponse {
return new SearchFeedGeneratorsResponse().fromJson(jsonValue, options)
}

static fromJsonString(
jsonString: string,
options?: Partial<JsonReadOptions>,
): SearchFeedGeneratorsResponse {
return new SearchFeedGeneratorsResponse().fromJsonString(
jsonString,
options,
)
}

static equals(
a:
| SearchFeedGeneratorsResponse
| PlainMessage<SearchFeedGeneratorsResponse>
| undefined,
b:
| SearchFeedGeneratorsResponse
| PlainMessage<SearchFeedGeneratorsResponse>
| undefined,
): boolean {
return proto3.util.equals(SearchFeedGeneratorsResponse, a, b)
}
}

/**
* - Returns feed generator validity and online status with uris A, B, C…
* - Not currently being used, but could be worhthwhile.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1597,6 +1597,7 @@ Object {

exports[`feed generation getSuggestedFeeds returns list of suggested feed generators 1`] = `
Object {
"cursor": "4",
"feeds": Array [
Object {
"cid": "cids(0)",
Expand Down
29 changes: 18 additions & 11 deletions packages/bsky/tests/feed-generation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,18 +348,26 @@ describe('feed generation', () => {
})
})

// @TODO support from dataplane
describe.skip('getPopularFeedGenerators', () => {
describe('getPopularFeedGenerators', () => {
it('gets popular feed generators', async () => {
const resEven =
await agent.api.app.bsky.unspecced.getPopularFeedGenerators(
{},
{ headers: await network.serviceHeaders(sc.dids.bob) },
)
expect(resEven.data.feeds.map((f) => f.likeCount)).toEqual([
2, 0, 0, 0, 0,
const res = await agent.api.app.bsky.unspecced.getPopularFeedGenerators(
{},
{ headers: await network.serviceHeaders(sc.dids.bob) },
)
expect(res.data.feeds.map((f) => f.uri)).not.toContain(feedUriPrime) // taken-down
expect(res.data.feeds.map((f) => f.uri)).toEqual([
feedUriAll,
feedUriEven,
feedUriBadPagination,
])
expect(resEven.data.feeds.map((f) => f.uri)).not.toContain(feedUriPrime) // taken-down
})

it('searches feed generators', async () => {
const res = await agent.api.app.bsky.unspecced.getPopularFeedGenerators(
{ query: 'all' },
{ headers: await network.serviceHeaders(sc.dids.bob) },
)
expect(res.data.feeds.map((f) => f.uri)).toEqual([feedUriAll])
})

it('paginates', async () => {
Expand All @@ -368,7 +376,6 @@ describe('feed generation', () => {
{},
{ headers: await network.serviceHeaders(sc.dids.bob) },
)

const resOne =
await agent.api.app.bsky.unspecced.getPopularFeedGenerators(
{ limit: 2 },
Expand Down

0 comments on commit c4ab360

Please sign in to comment.