-
Notifications
You must be signed in to change notification settings - Fork 635
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
✨ Add team member handle and displayName #3587
Changes from 2 commits
244f487
82a821e
bf4280e
6df698a
8c509f0
e8dfd2f
d0d74d7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,10 +7,12 @@ import { BackgroundQueue } from '../background' | |
import { OzoneConfig, OzoneSecrets } from '../config' | ||
import { Database } from '../db' | ||
import { ModerationService } from '../mod-service' | ||
import { TeamService } from '../team' | ||
import { getSigningKeyId } from '../util' | ||
import { EventPusher } from './event-pusher' | ||
import { EventReverser } from './event-reverser' | ||
import { MaterializedViewRefresher } from './materialized-view-refresher' | ||
import { TeamProfileSynchronizer } from './team-profile-synchronizer' | ||
|
||
export type DaemonContextOptions = { | ||
db: Database | ||
|
@@ -20,6 +22,7 @@ export type DaemonContextOptions = { | |
eventPusher: EventPusher | ||
eventReverser: EventReverser | ||
materializedViewRefresher: MaterializedViewRefresher | ||
teamProfileSynchronizer: TeamProfileSynchronizer | ||
} | ||
|
||
export class DaemonContext { | ||
|
@@ -67,6 +70,17 @@ export class DaemonContext { | |
appviewAgent, | ||
createAuthHeaders, | ||
) | ||
const teamService = TeamService.creator() | ||
const teamProfileSynchronizer = new TeamProfileSynchronizer( | ||
backgroundQueue, | ||
{ | ||
createAuthHeaders: (method: string) => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This reads a bit weird but basically can't come up with a better, more context aware name that would work elsewhere as well as here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might make sense to pass the const options = createAuthHeaders(this.appviewDid, 'foo.bar')
this.agent.foo.bar(params, options) See my other comment for a more detailed example There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like that pretty well too 👍 |
||
createAuthHeaders(cfg.appview.did, method), | ||
agent: appviewAgent, | ||
}, | ||
teamService(db), | ||
cfg.db.teamProfileRefreshIntervalMs, | ||
) | ||
|
||
const eventReverser = new EventReverser(db, modService) | ||
|
||
|
@@ -83,6 +97,7 @@ export class DaemonContext { | |
eventPusher, | ||
eventReverser, | ||
materializedViewRefresher, | ||
teamProfileSynchronizer, | ||
...(overrides ?? {}), | ||
}) | ||
} | ||
|
@@ -111,16 +126,22 @@ export class DaemonContext { | |
return this.opts.materializedViewRefresher | ||
} | ||
|
||
get teamProfileSynchronizer(): TeamProfileSynchronizer { | ||
return this.opts.teamProfileSynchronizer | ||
} | ||
|
||
async start() { | ||
this.eventPusher.start() | ||
this.eventReverser.start() | ||
this.materializedViewRefresher.start() | ||
this.teamProfileSynchronizer.start() | ||
} | ||
|
||
async processAll() { | ||
// Sequential because the materialized view values depend on the events. | ||
await this.eventPusher.processAll() | ||
await this.materializedViewRefresher.run() | ||
await this.teamProfileSynchronizer.run() | ||
} | ||
|
||
async destroy() { | ||
|
@@ -129,6 +150,7 @@ export class DaemonContext { | |
this.eventReverser.destroy(), | ||
this.eventPusher.destroy(), | ||
this.materializedViewRefresher.destroy(), | ||
this.teamProfileSynchronizer.destroy(), | ||
]) | ||
} finally { | ||
await this.backgroundQueue.destroy() | ||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,17 @@ | ||||
import { HOUR } from '@atproto/common' | ||||
import { BackgroundQueue, PeriodicBackgroundTask } from '../background' | ||||
import { AppviewCreator, TeamService } from '../team' | ||||
|
||||
export class TeamProfileSynchronizer extends PeriodicBackgroundTask { | ||||
constructor( | ||||
backgroundQueue: BackgroundQueue, | ||||
appviewCreator: AppviewCreator, | ||||
teamService: TeamService, | ||||
interval = 24 * HOUR, | ||||
) { | ||||
super(backgroundQueue, interval, async (_, signal) => { | ||||
if (signal.aborted) return | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no need to check if the signal is aborted here. This check is only needed between tasks (if multiple statements, or during loops)
Suggested change
|
||||
await teamService.syncMemberProfiles(appviewCreator) | ||||
}) | ||||
} | ||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { Kysely } from 'kysely' | ||
|
||
export async function up(db: Kysely<unknown>): Promise<void> { | ||
await db.schema.alterTable('member').addColumn('handle', 'text').execute() | ||
await db.schema | ||
.alterTable('member') | ||
.addColumn('displayName', 'text') | ||
.execute() | ||
await db.schema | ||
.createIndex('member_display_name_idx') | ||
.on('member') | ||
.column('displayName') | ||
.execute() | ||
await db.schema | ||
.createIndex('member_handle_idx') | ||
.on('member') | ||
.column('handle') | ||
.execute() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think that these indexes can be used for the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh right. I'm gonna go with trgm, i think i saw an example of that somewhere else. |
||
} | ||
|
||
export async function down(db: Kysely<unknown>): Promise<void> { | ||
await db.schema.alterTable('member').dropColumn('handle').execute() | ||
await db.schema.alterTable('member').dropColumn('displayName').execute() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be kind of nice if adding a member would still complete even if fresh profile information couldn't be fetched.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah
teamService.getProfiles
handles failures internally and won't throw error if fetching profile fails.