From dac5c9e2023588ea71b89d367c65fe70e3161746 Mon Sep 17 00:00:00 2001 From: Daniel Holmgren Date: Wed, 24 Jan 2024 09:04:09 -0600 Subject: [PATCH] Tagged suggestions (#2077) * add tagged suggestions * fix codegen * rm unused err --- .../bsky/unspecced/getTaggedSuggestions.json | 42 ++++++++++++ packages/api/src/client/index.ts | 13 ++++ packages/api/src/client/lexicons.ts | 50 ++++++++++++++ .../bsky/unspecced/getTaggedSuggestions.ts | 55 ++++++++++++++++ .../bsky/unspecced/getTaggedSuggestions.ts | 21 ++++++ packages/bsky/src/api/index.ts | 2 + packages/bsky/src/db/database-schema.ts | 2 + .../20240124T023719200Z-tagged-suggestions.ts | 15 +++++ packages/bsky/src/db/migrations/index.ts | 1 + .../bsky/src/db/tables/tagged-suggestion.ts | 11 ++++ packages/bsky/src/lexicon/index.ts | 12 ++++ packages/bsky/src/lexicon/lexicons.ts | 50 ++++++++++++++ .../bsky/unspecced/getTaggedSuggestions.ts | 65 +++++++++++++++++++ packages/bsky/tests/views/suggestions.test.ts | 22 +++++++ packages/ozone/src/lexicon/index.ts | 12 ++++ packages/ozone/src/lexicon/lexicons.ts | 50 ++++++++++++++ .../bsky/unspecced/getTaggedSuggestions.ts | 65 +++++++++++++++++++ .../bsky/unspecced/getTaggedSuggestions.ts | 21 ++++++ .../pds/src/api/app/bsky/unspecced/index.ts | 2 + packages/pds/src/lexicon/index.ts | 12 ++++ packages/pds/src/lexicon/lexicons.ts | 50 ++++++++++++++ .../bsky/unspecced/getTaggedSuggestions.ts | 65 +++++++++++++++++++ 22 files changed, 638 insertions(+) create mode 100644 lexicons/app/bsky/unspecced/getTaggedSuggestions.json create mode 100644 packages/api/src/client/types/app/bsky/unspecced/getTaggedSuggestions.ts create mode 100644 packages/bsky/src/api/app/bsky/unspecced/getTaggedSuggestions.ts create mode 100644 packages/bsky/src/db/migrations/20240124T023719200Z-tagged-suggestions.ts create mode 100644 packages/bsky/src/db/tables/tagged-suggestion.ts create mode 100644 packages/bsky/src/lexicon/types/app/bsky/unspecced/getTaggedSuggestions.ts create mode 100644 packages/ozone/src/lexicon/types/app/bsky/unspecced/getTaggedSuggestions.ts create mode 100644 packages/pds/src/api/app/bsky/unspecced/getTaggedSuggestions.ts create mode 100644 packages/pds/src/lexicon/types/app/bsky/unspecced/getTaggedSuggestions.ts diff --git a/lexicons/app/bsky/unspecced/getTaggedSuggestions.json b/lexicons/app/bsky/unspecced/getTaggedSuggestions.json new file mode 100644 index 00000000000..9fd98ffefb0 --- /dev/null +++ b/lexicons/app/bsky/unspecced/getTaggedSuggestions.json @@ -0,0 +1,42 @@ +{ + "lexicon": 1, + "id": "app.bsky.unspecced.getTaggedSuggestions", + "defs": { + "main": { + "type": "query", + "description": "Get a list of suggestions (feeds and users) tagged with categories", + "parameters": { + "type": "params", + "properties": {} + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["suggestions"], + "properties": { + "suggestions": { + "type": "array", + "items": { + "type": "ref", + "ref": "#suggestion" + } + } + } + } + } + }, + "suggestion": { + "type": "object", + "required": ["tag", "subjectType", "subject"], + "properties": { + "tag": { "type": "string" }, + "subjectType": { + "type": "string", + "knownValues": ["actor", "feed"] + }, + "subject": { "type": "string", "format": "uri" } + } + } + } +} diff --git a/packages/api/src/client/index.ts b/packages/api/src/client/index.ts index 6a18f10ed6c..228d589e8d5 100644 --- a/packages/api/src/client/index.ts +++ b/packages/api/src/client/index.ts @@ -147,6 +147,7 @@ import * as AppBskyNotificationUpdateSeen from './types/app/bsky/notification/up import * as AppBskyRichtextFacet from './types/app/bsky/richtext/facet' import * as AppBskyUnspeccedDefs from './types/app/bsky/unspecced/defs' import * as AppBskyUnspeccedGetPopularFeedGenerators from './types/app/bsky/unspecced/getPopularFeedGenerators' +import * as AppBskyUnspeccedGetTaggedSuggestions from './types/app/bsky/unspecced/getTaggedSuggestions' import * as AppBskyUnspeccedGetTimelineSkeleton from './types/app/bsky/unspecced/getTimelineSkeleton' import * as AppBskyUnspeccedSearchActorsSkeleton from './types/app/bsky/unspecced/searchActorsSkeleton' import * as AppBskyUnspeccedSearchPostsSkeleton from './types/app/bsky/unspecced/searchPostsSkeleton' @@ -291,6 +292,7 @@ export * as AppBskyNotificationUpdateSeen from './types/app/bsky/notification/up export * as AppBskyRichtextFacet from './types/app/bsky/richtext/facet' export * as AppBskyUnspeccedDefs from './types/app/bsky/unspecced/defs' export * as AppBskyUnspeccedGetPopularFeedGenerators from './types/app/bsky/unspecced/getPopularFeedGenerators' +export * as AppBskyUnspeccedGetTaggedSuggestions from './types/app/bsky/unspecced/getTaggedSuggestions' export * as AppBskyUnspeccedGetTimelineSkeleton from './types/app/bsky/unspecced/getTimelineSkeleton' export * as AppBskyUnspeccedSearchActorsSkeleton from './types/app/bsky/unspecced/searchActorsSkeleton' export * as AppBskyUnspeccedSearchPostsSkeleton from './types/app/bsky/unspecced/searchPostsSkeleton' @@ -2508,6 +2510,17 @@ export class AppBskyUnspeccedNS { }) } + getTaggedSuggestions( + params?: AppBskyUnspeccedGetTaggedSuggestions.QueryParams, + opts?: AppBskyUnspeccedGetTaggedSuggestions.CallOptions, + ): Promise { + return this._service.xrpc + .call('app.bsky.unspecced.getTaggedSuggestions', params, undefined, opts) + .catch((e) => { + throw AppBskyUnspeccedGetTaggedSuggestions.toKnownErr(e) + }) + } + getTimelineSkeleton( params?: AppBskyUnspeccedGetTimelineSkeleton.QueryParams, opts?: AppBskyUnspeccedGetTimelineSkeleton.CallOptions, diff --git a/packages/api/src/client/lexicons.ts b/packages/api/src/client/lexicons.ts index a198bfe1211..0c757a80b0f 100644 --- a/packages/api/src/client/lexicons.ts +++ b/packages/api/src/client/lexicons.ts @@ -7908,6 +7908,54 @@ export const schemaDict = { }, }, }, + AppBskyUnspeccedGetTaggedSuggestions: { + lexicon: 1, + id: 'app.bsky.unspecced.getTaggedSuggestions', + defs: { + main: { + type: 'query', + description: + 'Get a list of suggestions (feeds and users) tagged with categories', + parameters: { + type: 'params', + properties: {}, + }, + output: { + encoding: 'application/json', + schema: { + type: 'object', + required: ['suggestions'], + properties: { + suggestions: { + type: 'array', + items: { + type: 'ref', + ref: 'lex:app.bsky.unspecced.getTaggedSuggestions#suggestion', + }, + }, + }, + }, + }, + }, + suggestion: { + type: 'object', + required: ['tag', 'subjectType', 'subject'], + properties: { + tag: { + type: 'string', + }, + subjectType: { + type: 'string', + knownValues: ['actor', 'feed'], + }, + subject: { + type: 'string', + format: 'uri', + }, + }, + }, + }, + }, AppBskyUnspeccedGetTimelineSkeleton: { lexicon: 1, id: 'app.bsky.unspecced.getTimelineSkeleton', @@ -8242,6 +8290,8 @@ export const ids = { AppBskyUnspeccedDefs: 'app.bsky.unspecced.defs', AppBskyUnspeccedGetPopularFeedGenerators: 'app.bsky.unspecced.getPopularFeedGenerators', + AppBskyUnspeccedGetTaggedSuggestions: + 'app.bsky.unspecced.getTaggedSuggestions', AppBskyUnspeccedGetTimelineSkeleton: 'app.bsky.unspecced.getTimelineSkeleton', AppBskyUnspeccedSearchActorsSkeleton: 'app.bsky.unspecced.searchActorsSkeleton', diff --git a/packages/api/src/client/types/app/bsky/unspecced/getTaggedSuggestions.ts b/packages/api/src/client/types/app/bsky/unspecced/getTaggedSuggestions.ts new file mode 100644 index 00000000000..a35e2411756 --- /dev/null +++ b/packages/api/src/client/types/app/bsky/unspecced/getTaggedSuggestions.ts @@ -0,0 +1,55 @@ +/** + * GENERATED CODE - DO NOT MODIFY + */ +import { Headers, XRPCError } from '@atproto/xrpc' +import { ValidationResult, BlobRef } from '@atproto/lexicon' +import { isObj, hasProp } from '../../../../util' +import { lexicons } from '../../../../lexicons' +import { CID } from 'multiformats/cid' + +export interface QueryParams {} + +export type InputSchema = undefined + +export interface OutputSchema { + suggestions: Suggestion[] + [k: string]: unknown +} + +export interface CallOptions { + headers?: Headers +} + +export interface Response { + success: boolean + headers: Headers + data: OutputSchema +} + +export function toKnownErr(e: any) { + if (e instanceof XRPCError) { + } + return e +} + +export interface Suggestion { + tag: string + subjectType: 'actor' | 'feed' | (string & {}) + subject: string + [k: string]: unknown +} + +export function isSuggestion(v: unknown): v is Suggestion { + return ( + isObj(v) && + hasProp(v, '$type') && + v.$type === 'app.bsky.unspecced.getTaggedSuggestions#suggestion' + ) +} + +export function validateSuggestion(v: unknown): ValidationResult { + return lexicons.validate( + 'app.bsky.unspecced.getTaggedSuggestions#suggestion', + v, + ) +} diff --git a/packages/bsky/src/api/app/bsky/unspecced/getTaggedSuggestions.ts b/packages/bsky/src/api/app/bsky/unspecced/getTaggedSuggestions.ts new file mode 100644 index 00000000000..4fd248e87ff --- /dev/null +++ b/packages/bsky/src/api/app/bsky/unspecced/getTaggedSuggestions.ts @@ -0,0 +1,21 @@ +import { Server } from '../../../../lexicon' +import AppContext from '../../../../context' + +// THIS IS A TEMPORARY UNSPECCED ROUTE +export default function (server: Server, ctx: AppContext) { + server.app.bsky.unspecced.getTaggedSuggestions({ + handler: async () => { + const suggestions = await ctx.db + .getReplica() + .db.selectFrom('tagged_suggestion') + .selectAll() + .execute() + return { + encoding: 'application/json', + body: { + suggestions, + }, + } + }, + }) +} diff --git a/packages/bsky/src/api/index.ts b/packages/bsky/src/api/index.ts index cd99f0ad4dd..7628770507f 100644 --- a/packages/bsky/src/api/index.ts +++ b/packages/bsky/src/api/index.ts @@ -40,6 +40,7 @@ import updateSeen from './app/bsky/notification/updateSeen' import registerPush from './app/bsky/notification/registerPush' import getPopularFeedGenerators from './app/bsky/unspecced/getPopularFeedGenerators' import getTimelineSkeleton from './app/bsky/unspecced/getTimelineSkeleton' +import getTaggedSuggestions from './app/bsky/unspecced/getTaggedSuggestions' import getSubjectStatus from './com/atproto/admin/getSubjectStatus' import updateSubjectStatus from './com/atproto/admin/updateSubjectStatus' import getAccountInfos from './com/atproto/admin/getAccountInfos' @@ -95,6 +96,7 @@ export default function (server: Server, ctx: AppContext) { registerPush(server, ctx) getPopularFeedGenerators(server, ctx) getTimelineSkeleton(server, ctx) + getTaggedSuggestions(server, ctx) // com.atproto getSubjectStatus(server, ctx) updateSubjectStatus(server, ctx) diff --git a/packages/bsky/src/db/database-schema.ts b/packages/bsky/src/db/database-schema.ts index 3dba50d39ea..df28c8b91d8 100644 --- a/packages/bsky/src/db/database-schema.ts +++ b/packages/bsky/src/db/database-schema.ts @@ -30,6 +30,7 @@ import * as algo from './tables/algo' import * as viewParam from './tables/view-param' import * as suggestedFollow from './tables/suggested-follow' import * as suggestedFeed from './tables/suggested-feed' +import * as taggedSuggestion from './tables/tagged-suggestion' import * as blobTakedown from './tables/blob-takedown' export type DatabaseSchemaType = duplicateRecord.PartialDB & @@ -63,6 +64,7 @@ export type DatabaseSchemaType = duplicateRecord.PartialDB & viewParam.PartialDB & suggestedFollow.PartialDB & suggestedFeed.PartialDB & + taggedSuggestion.PartialDB & blobTakedown.PartialDB export type DatabaseSchema = Kysely diff --git a/packages/bsky/src/db/migrations/20240124T023719200Z-tagged-suggestions.ts b/packages/bsky/src/db/migrations/20240124T023719200Z-tagged-suggestions.ts new file mode 100644 index 00000000000..a90b56268fe --- /dev/null +++ b/packages/bsky/src/db/migrations/20240124T023719200Z-tagged-suggestions.ts @@ -0,0 +1,15 @@ +import { Kysely } from 'kysely' + +export async function up(db: Kysely): Promise { + await db.schema + .createTable('tagged_suggestion') + .addColumn('tag', 'varchar', (col) => col.notNull()) + .addColumn('subject', 'varchar', (col) => col.notNull()) + .addColumn('subjectType', 'varchar', (col) => col.notNull()) + .addPrimaryKeyConstraint('tagged_suggestion_pkey', ['tag', 'subject']) + .execute() +} + +export async function down(db: Kysely): Promise { + await db.schema.dropTable('tagged_suggestion').execute() +} diff --git a/packages/bsky/src/db/migrations/index.ts b/packages/bsky/src/db/migrations/index.ts index 76272566514..e5a5b155e71 100644 --- a/packages/bsky/src/db/migrations/index.ts +++ b/packages/bsky/src/db/migrations/index.ts @@ -33,3 +33,4 @@ export * as _20230929T192920807Z from './20230929T192920807Z-record-cursor-index export * as _20231003T202833377Z from './20231003T202833377Z-create-moderation-subject-status' export * as _20231205T000257238Z from './20231205T000257238Z-remove-did-cache' export * as _20231220T225126090Z from './20231220T225126090Z-blob-takedowns' +export * as _20240124T023719200Z from './20240124T023719200Z-tagged-suggestions' diff --git a/packages/bsky/src/db/tables/tagged-suggestion.ts b/packages/bsky/src/db/tables/tagged-suggestion.ts new file mode 100644 index 00000000000..54f36ab2022 --- /dev/null +++ b/packages/bsky/src/db/tables/tagged-suggestion.ts @@ -0,0 +1,11 @@ +export const tableName = 'tagged_suggestion' + +export interface TaggedSuggestion { + tag: string + subject: string + subjectType: string +} + +export type PartialDB = { + [tableName]: TaggedSuggestion +} diff --git a/packages/bsky/src/lexicon/index.ts b/packages/bsky/src/lexicon/index.ts index 98bcd043520..efdb35c319e 100644 --- a/packages/bsky/src/lexicon/index.ts +++ b/packages/bsky/src/lexicon/index.ts @@ -124,6 +124,7 @@ import * as AppBskyNotificationListNotifications from './types/app/bsky/notifica import * as AppBskyNotificationRegisterPush from './types/app/bsky/notification/registerPush' import * as AppBskyNotificationUpdateSeen from './types/app/bsky/notification/updateSeen' import * as AppBskyUnspeccedGetPopularFeedGenerators from './types/app/bsky/unspecced/getPopularFeedGenerators' +import * as AppBskyUnspeccedGetTaggedSuggestions from './types/app/bsky/unspecced/getTaggedSuggestions' import * as AppBskyUnspeccedGetTimelineSkeleton from './types/app/bsky/unspecced/getTimelineSkeleton' import * as AppBskyUnspeccedSearchActorsSkeleton from './types/app/bsky/unspecced/searchActorsSkeleton' import * as AppBskyUnspeccedSearchPostsSkeleton from './types/app/bsky/unspecced/searchPostsSkeleton' @@ -1613,6 +1614,17 @@ export class AppBskyUnspeccedNS { return this._server.xrpc.method(nsid, cfg) } + getTaggedSuggestions( + cfg: ConfigOf< + AV, + AppBskyUnspeccedGetTaggedSuggestions.Handler>, + AppBskyUnspeccedGetTaggedSuggestions.HandlerReqCtx> + >, + ) { + const nsid = 'app.bsky.unspecced.getTaggedSuggestions' // @ts-ignore + return this._server.xrpc.method(nsid, cfg) + } + getTimelineSkeleton( cfg: ConfigOf< AV, diff --git a/packages/bsky/src/lexicon/lexicons.ts b/packages/bsky/src/lexicon/lexicons.ts index a198bfe1211..0c757a80b0f 100644 --- a/packages/bsky/src/lexicon/lexicons.ts +++ b/packages/bsky/src/lexicon/lexicons.ts @@ -7908,6 +7908,54 @@ export const schemaDict = { }, }, }, + AppBskyUnspeccedGetTaggedSuggestions: { + lexicon: 1, + id: 'app.bsky.unspecced.getTaggedSuggestions', + defs: { + main: { + type: 'query', + description: + 'Get a list of suggestions (feeds and users) tagged with categories', + parameters: { + type: 'params', + properties: {}, + }, + output: { + encoding: 'application/json', + schema: { + type: 'object', + required: ['suggestions'], + properties: { + suggestions: { + type: 'array', + items: { + type: 'ref', + ref: 'lex:app.bsky.unspecced.getTaggedSuggestions#suggestion', + }, + }, + }, + }, + }, + }, + suggestion: { + type: 'object', + required: ['tag', 'subjectType', 'subject'], + properties: { + tag: { + type: 'string', + }, + subjectType: { + type: 'string', + knownValues: ['actor', 'feed'], + }, + subject: { + type: 'string', + format: 'uri', + }, + }, + }, + }, + }, AppBskyUnspeccedGetTimelineSkeleton: { lexicon: 1, id: 'app.bsky.unspecced.getTimelineSkeleton', @@ -8242,6 +8290,8 @@ export const ids = { AppBskyUnspeccedDefs: 'app.bsky.unspecced.defs', AppBskyUnspeccedGetPopularFeedGenerators: 'app.bsky.unspecced.getPopularFeedGenerators', + AppBskyUnspeccedGetTaggedSuggestions: + 'app.bsky.unspecced.getTaggedSuggestions', AppBskyUnspeccedGetTimelineSkeleton: 'app.bsky.unspecced.getTimelineSkeleton', AppBskyUnspeccedSearchActorsSkeleton: 'app.bsky.unspecced.searchActorsSkeleton', diff --git a/packages/bsky/src/lexicon/types/app/bsky/unspecced/getTaggedSuggestions.ts b/packages/bsky/src/lexicon/types/app/bsky/unspecced/getTaggedSuggestions.ts new file mode 100644 index 00000000000..e6319c54b4e --- /dev/null +++ b/packages/bsky/src/lexicon/types/app/bsky/unspecced/getTaggedSuggestions.ts @@ -0,0 +1,65 @@ +/** + * GENERATED CODE - DO NOT MODIFY + */ +import express from 'express' +import { ValidationResult, BlobRef } from '@atproto/lexicon' +import { lexicons } from '../../../../lexicons' +import { isObj, hasProp } from '../../../../util' +import { CID } from 'multiformats/cid' +import { HandlerAuth } from '@atproto/xrpc-server' + +export interface QueryParams {} + +export type InputSchema = undefined + +export interface OutputSchema { + suggestions: Suggestion[] + [k: string]: unknown +} + +export type HandlerInput = undefined + +export interface HandlerSuccess { + encoding: 'application/json' + body: OutputSchema + headers?: { [key: string]: string } +} + +export interface HandlerError { + status: number + message?: string +} + +export type HandlerOutput = HandlerError | HandlerSuccess +export type HandlerReqCtx = { + auth: HA + params: QueryParams + input: HandlerInput + req: express.Request + res: express.Response +} +export type Handler = ( + ctx: HandlerReqCtx, +) => Promise | HandlerOutput + +export interface Suggestion { + tag: string + subjectType: 'actor' | 'feed' | (string & {}) + subject: string + [k: string]: unknown +} + +export function isSuggestion(v: unknown): v is Suggestion { + return ( + isObj(v) && + hasProp(v, '$type') && + v.$type === 'app.bsky.unspecced.getTaggedSuggestions#suggestion' + ) +} + +export function validateSuggestion(v: unknown): ValidationResult { + return lexicons.validate( + 'app.bsky.unspecced.getTaggedSuggestions#suggestion', + v, + ) +} diff --git a/packages/bsky/tests/views/suggestions.test.ts b/packages/bsky/tests/views/suggestions.test.ts index f79575c529e..bcae515ffb3 100644 --- a/packages/bsky/tests/views/suggestions.test.ts +++ b/packages/bsky/tests/views/suggestions.test.ts @@ -96,4 +96,26 @@ describe('pds user search views', () => { authed.actors.map(stripViewer), ) }) + + it('returns tagged suggestions', async () => { + const suggestions = [ + { + tag: 'test', + subject: 'did:example:test', + subjectType: 'actor', + }, + { + tag: 'another', + subject: 'at://did:example:another/app.bsky.feed.generator/my-feed', + subjectType: 'feed', + }, + ] + await network.bsky.ctx.db + .getPrimary() + .db.insertInto('tagged_suggestion') + .values(suggestions) + .execute() + const res = await agent.api.app.bsky.unspecced.getTaggedSuggestions() + expect(res.data.suggestions).toEqual(suggestions) + }) }) diff --git a/packages/ozone/src/lexicon/index.ts b/packages/ozone/src/lexicon/index.ts index 98bcd043520..efdb35c319e 100644 --- a/packages/ozone/src/lexicon/index.ts +++ b/packages/ozone/src/lexicon/index.ts @@ -124,6 +124,7 @@ import * as AppBskyNotificationListNotifications from './types/app/bsky/notifica import * as AppBskyNotificationRegisterPush from './types/app/bsky/notification/registerPush' import * as AppBskyNotificationUpdateSeen from './types/app/bsky/notification/updateSeen' import * as AppBskyUnspeccedGetPopularFeedGenerators from './types/app/bsky/unspecced/getPopularFeedGenerators' +import * as AppBskyUnspeccedGetTaggedSuggestions from './types/app/bsky/unspecced/getTaggedSuggestions' import * as AppBskyUnspeccedGetTimelineSkeleton from './types/app/bsky/unspecced/getTimelineSkeleton' import * as AppBskyUnspeccedSearchActorsSkeleton from './types/app/bsky/unspecced/searchActorsSkeleton' import * as AppBskyUnspeccedSearchPostsSkeleton from './types/app/bsky/unspecced/searchPostsSkeleton' @@ -1613,6 +1614,17 @@ export class AppBskyUnspeccedNS { return this._server.xrpc.method(nsid, cfg) } + getTaggedSuggestions( + cfg: ConfigOf< + AV, + AppBskyUnspeccedGetTaggedSuggestions.Handler>, + AppBskyUnspeccedGetTaggedSuggestions.HandlerReqCtx> + >, + ) { + const nsid = 'app.bsky.unspecced.getTaggedSuggestions' // @ts-ignore + return this._server.xrpc.method(nsid, cfg) + } + getTimelineSkeleton( cfg: ConfigOf< AV, diff --git a/packages/ozone/src/lexicon/lexicons.ts b/packages/ozone/src/lexicon/lexicons.ts index a198bfe1211..0c757a80b0f 100644 --- a/packages/ozone/src/lexicon/lexicons.ts +++ b/packages/ozone/src/lexicon/lexicons.ts @@ -7908,6 +7908,54 @@ export const schemaDict = { }, }, }, + AppBskyUnspeccedGetTaggedSuggestions: { + lexicon: 1, + id: 'app.bsky.unspecced.getTaggedSuggestions', + defs: { + main: { + type: 'query', + description: + 'Get a list of suggestions (feeds and users) tagged with categories', + parameters: { + type: 'params', + properties: {}, + }, + output: { + encoding: 'application/json', + schema: { + type: 'object', + required: ['suggestions'], + properties: { + suggestions: { + type: 'array', + items: { + type: 'ref', + ref: 'lex:app.bsky.unspecced.getTaggedSuggestions#suggestion', + }, + }, + }, + }, + }, + }, + suggestion: { + type: 'object', + required: ['tag', 'subjectType', 'subject'], + properties: { + tag: { + type: 'string', + }, + subjectType: { + type: 'string', + knownValues: ['actor', 'feed'], + }, + subject: { + type: 'string', + format: 'uri', + }, + }, + }, + }, + }, AppBskyUnspeccedGetTimelineSkeleton: { lexicon: 1, id: 'app.bsky.unspecced.getTimelineSkeleton', @@ -8242,6 +8290,8 @@ export const ids = { AppBskyUnspeccedDefs: 'app.bsky.unspecced.defs', AppBskyUnspeccedGetPopularFeedGenerators: 'app.bsky.unspecced.getPopularFeedGenerators', + AppBskyUnspeccedGetTaggedSuggestions: + 'app.bsky.unspecced.getTaggedSuggestions', AppBskyUnspeccedGetTimelineSkeleton: 'app.bsky.unspecced.getTimelineSkeleton', AppBskyUnspeccedSearchActorsSkeleton: 'app.bsky.unspecced.searchActorsSkeleton', diff --git a/packages/ozone/src/lexicon/types/app/bsky/unspecced/getTaggedSuggestions.ts b/packages/ozone/src/lexicon/types/app/bsky/unspecced/getTaggedSuggestions.ts new file mode 100644 index 00000000000..e6319c54b4e --- /dev/null +++ b/packages/ozone/src/lexicon/types/app/bsky/unspecced/getTaggedSuggestions.ts @@ -0,0 +1,65 @@ +/** + * GENERATED CODE - DO NOT MODIFY + */ +import express from 'express' +import { ValidationResult, BlobRef } from '@atproto/lexicon' +import { lexicons } from '../../../../lexicons' +import { isObj, hasProp } from '../../../../util' +import { CID } from 'multiformats/cid' +import { HandlerAuth } from '@atproto/xrpc-server' + +export interface QueryParams {} + +export type InputSchema = undefined + +export interface OutputSchema { + suggestions: Suggestion[] + [k: string]: unknown +} + +export type HandlerInput = undefined + +export interface HandlerSuccess { + encoding: 'application/json' + body: OutputSchema + headers?: { [key: string]: string } +} + +export interface HandlerError { + status: number + message?: string +} + +export type HandlerOutput = HandlerError | HandlerSuccess +export type HandlerReqCtx = { + auth: HA + params: QueryParams + input: HandlerInput + req: express.Request + res: express.Response +} +export type Handler = ( + ctx: HandlerReqCtx, +) => Promise | HandlerOutput + +export interface Suggestion { + tag: string + subjectType: 'actor' | 'feed' | (string & {}) + subject: string + [k: string]: unknown +} + +export function isSuggestion(v: unknown): v is Suggestion { + return ( + isObj(v) && + hasProp(v, '$type') && + v.$type === 'app.bsky.unspecced.getTaggedSuggestions#suggestion' + ) +} + +export function validateSuggestion(v: unknown): ValidationResult { + return lexicons.validate( + 'app.bsky.unspecced.getTaggedSuggestions#suggestion', + v, + ) +} diff --git a/packages/pds/src/api/app/bsky/unspecced/getTaggedSuggestions.ts b/packages/pds/src/api/app/bsky/unspecced/getTaggedSuggestions.ts new file mode 100644 index 00000000000..cd43177b83d --- /dev/null +++ b/packages/pds/src/api/app/bsky/unspecced/getTaggedSuggestions.ts @@ -0,0 +1,21 @@ +import { Server } from '../../../../lexicon' +import AppContext from '../../../../context' + +// THIS IS A TEMPORARY UNSPECCED ROUTE +export default function (server: Server, ctx: AppContext) { + server.app.bsky.unspecced.getTaggedSuggestions({ + auth: ctx.authVerifier.access, + handler: async ({ auth, params }) => { + const requester = auth.credentials.did + const res = + await ctx.appViewAgent.api.app.bsky.unspecced.getTaggedSuggestions( + params, + await ctx.appviewAuthHeaders(requester), + ) + return { + encoding: 'application/json', + body: res.data, + } + }, + }) +} diff --git a/packages/pds/src/api/app/bsky/unspecced/index.ts b/packages/pds/src/api/app/bsky/unspecced/index.ts index 208efcc93a9..2c942aabae4 100644 --- a/packages/pds/src/api/app/bsky/unspecced/index.ts +++ b/packages/pds/src/api/app/bsky/unspecced/index.ts @@ -1,8 +1,10 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' import getPopularFeedGenerators from './getPopularFeedGenerators' +import getTaggedSuggestions from './getTaggedSuggestions' // THIS IS A TEMPORARY UNSPECCED ROUTE export default function (server: Server, ctx: AppContext) { getPopularFeedGenerators(server, ctx) + getTaggedSuggestions(server, ctx) } diff --git a/packages/pds/src/lexicon/index.ts b/packages/pds/src/lexicon/index.ts index 98bcd043520..efdb35c319e 100644 --- a/packages/pds/src/lexicon/index.ts +++ b/packages/pds/src/lexicon/index.ts @@ -124,6 +124,7 @@ import * as AppBskyNotificationListNotifications from './types/app/bsky/notifica import * as AppBskyNotificationRegisterPush from './types/app/bsky/notification/registerPush' import * as AppBskyNotificationUpdateSeen from './types/app/bsky/notification/updateSeen' import * as AppBskyUnspeccedGetPopularFeedGenerators from './types/app/bsky/unspecced/getPopularFeedGenerators' +import * as AppBskyUnspeccedGetTaggedSuggestions from './types/app/bsky/unspecced/getTaggedSuggestions' import * as AppBskyUnspeccedGetTimelineSkeleton from './types/app/bsky/unspecced/getTimelineSkeleton' import * as AppBskyUnspeccedSearchActorsSkeleton from './types/app/bsky/unspecced/searchActorsSkeleton' import * as AppBskyUnspeccedSearchPostsSkeleton from './types/app/bsky/unspecced/searchPostsSkeleton' @@ -1613,6 +1614,17 @@ export class AppBskyUnspeccedNS { return this._server.xrpc.method(nsid, cfg) } + getTaggedSuggestions( + cfg: ConfigOf< + AV, + AppBskyUnspeccedGetTaggedSuggestions.Handler>, + AppBskyUnspeccedGetTaggedSuggestions.HandlerReqCtx> + >, + ) { + const nsid = 'app.bsky.unspecced.getTaggedSuggestions' // @ts-ignore + return this._server.xrpc.method(nsid, cfg) + } + getTimelineSkeleton( cfg: ConfigOf< AV, diff --git a/packages/pds/src/lexicon/lexicons.ts b/packages/pds/src/lexicon/lexicons.ts index a198bfe1211..0c757a80b0f 100644 --- a/packages/pds/src/lexicon/lexicons.ts +++ b/packages/pds/src/lexicon/lexicons.ts @@ -7908,6 +7908,54 @@ export const schemaDict = { }, }, }, + AppBskyUnspeccedGetTaggedSuggestions: { + lexicon: 1, + id: 'app.bsky.unspecced.getTaggedSuggestions', + defs: { + main: { + type: 'query', + description: + 'Get a list of suggestions (feeds and users) tagged with categories', + parameters: { + type: 'params', + properties: {}, + }, + output: { + encoding: 'application/json', + schema: { + type: 'object', + required: ['suggestions'], + properties: { + suggestions: { + type: 'array', + items: { + type: 'ref', + ref: 'lex:app.bsky.unspecced.getTaggedSuggestions#suggestion', + }, + }, + }, + }, + }, + }, + suggestion: { + type: 'object', + required: ['tag', 'subjectType', 'subject'], + properties: { + tag: { + type: 'string', + }, + subjectType: { + type: 'string', + knownValues: ['actor', 'feed'], + }, + subject: { + type: 'string', + format: 'uri', + }, + }, + }, + }, + }, AppBskyUnspeccedGetTimelineSkeleton: { lexicon: 1, id: 'app.bsky.unspecced.getTimelineSkeleton', @@ -8242,6 +8290,8 @@ export const ids = { AppBskyUnspeccedDefs: 'app.bsky.unspecced.defs', AppBskyUnspeccedGetPopularFeedGenerators: 'app.bsky.unspecced.getPopularFeedGenerators', + AppBskyUnspeccedGetTaggedSuggestions: + 'app.bsky.unspecced.getTaggedSuggestions', AppBskyUnspeccedGetTimelineSkeleton: 'app.bsky.unspecced.getTimelineSkeleton', AppBskyUnspeccedSearchActorsSkeleton: 'app.bsky.unspecced.searchActorsSkeleton', diff --git a/packages/pds/src/lexicon/types/app/bsky/unspecced/getTaggedSuggestions.ts b/packages/pds/src/lexicon/types/app/bsky/unspecced/getTaggedSuggestions.ts new file mode 100644 index 00000000000..e6319c54b4e --- /dev/null +++ b/packages/pds/src/lexicon/types/app/bsky/unspecced/getTaggedSuggestions.ts @@ -0,0 +1,65 @@ +/** + * GENERATED CODE - DO NOT MODIFY + */ +import express from 'express' +import { ValidationResult, BlobRef } from '@atproto/lexicon' +import { lexicons } from '../../../../lexicons' +import { isObj, hasProp } from '../../../../util' +import { CID } from 'multiformats/cid' +import { HandlerAuth } from '@atproto/xrpc-server' + +export interface QueryParams {} + +export type InputSchema = undefined + +export interface OutputSchema { + suggestions: Suggestion[] + [k: string]: unknown +} + +export type HandlerInput = undefined + +export interface HandlerSuccess { + encoding: 'application/json' + body: OutputSchema + headers?: { [key: string]: string } +} + +export interface HandlerError { + status: number + message?: string +} + +export type HandlerOutput = HandlerError | HandlerSuccess +export type HandlerReqCtx = { + auth: HA + params: QueryParams + input: HandlerInput + req: express.Request + res: express.Response +} +export type Handler = ( + ctx: HandlerReqCtx, +) => Promise | HandlerOutput + +export interface Suggestion { + tag: string + subjectType: 'actor' | 'feed' | (string & {}) + subject: string + [k: string]: unknown +} + +export function isSuggestion(v: unknown): v is Suggestion { + return ( + isObj(v) && + hasProp(v, '$type') && + v.$type === 'app.bsky.unspecced.getTaggedSuggestions#suggestion' + ) +} + +export function validateSuggestion(v: unknown): ValidationResult { + return lexicons.validate( + 'app.bsky.unspecced.getTaggedSuggestions#suggestion', + v, + ) +}