-
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
Conversation
packages/ozone/src/daemon/context.ts
Outdated
const teamProfileSynchronizer = new TeamProfileSynchronizer( | ||
backgroundQueue, | ||
{ | ||
createAuthHeaders: (method: string) => |
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.
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 comment
The reason will be displayed to describe this comment to others. Learn more.
It might make sense to pass the appviewAgent
and appviewDid
as argument to the new TeamService()
constructor, then used as:
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 comment
The reason will be displayed to describe this comment to others. Learn more.
I like that pretty well too 👍
packages/ozone/src/team/index.ts
Outdated
|
||
export type TeamServiceCreator = (db: Database) => TeamService | ||
export type AppviewCreator = { |
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.
Refactoring away from passing the whole AppContext
since we want to be able to run this from both app and daemon.
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.
Since you take the opportunity to refactor, you should change this from being a method argument to a constructor argument:
class TeamService {
constructor(
protected readonly db: Database,
protected readonly appviewAgent: AtpAgent,
protected readonly appviewDid: string,
) {}
async getProfiles(dids: string[]) {
const {headers} = createAuthHeaders(this.appviewDid, method)
const result = await this.appviewAgent(...)
}
}
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.
(this is the same comment as above)
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 comment
The 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)
if (signal.aborted) return |
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.
I think that a minor refactor here could make things a little more neat.
We should probably name the handle
and displayName
with a cached
prefix to clearly indicate that these are not a real source of thruth.
packages/ozone/tests/team.test.ts
Outdated
@@ -11,6 +11,9 @@ describe('team management', () => { | |||
beforeAll(async () => { | |||
network = await TestNetwork.create({ | |||
dbPostgresSchema: 'ozone_team_test', | |||
ozone: { | |||
dbTeamProfileRefreshIntervalMs: 1000, |
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.
There probably need to be a "wait for 1 second since the member was added to the list" in the tests below here somewhere to prevent tests from being flaky on fast machines.
You could event change this interval to something smaller.
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.
Since the information is fetched when the member is added, I don't think the refresh interval is ever really used (or perhaps I missed something). Could we disable it altogether? (Also—isn't it only used by the daemon and not ozone proper?)
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.
i believe the daemon processes are run when processAll()
is called in test network and for this test, we are creating the members with handle
and displayName
null to kinda implicitly test that the background sync works.
packages/ozone/src/daemon/context.ts
Outdated
const teamProfileSynchronizer = new TeamProfileSynchronizer( | ||
backgroundQueue, | ||
{ | ||
createAuthHeaders: (method: string) => |
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 might make sense to pass the appviewAgent
and appviewDid
as argument to the new TeamService()
constructor, then used as:
const options = createAuthHeaders(this.appviewDid, 'foo.bar')
this.agent.foo.bar(params, options)
See my other comment for a more detailed example
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 comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that these indexes can be used for the ilike
queries, if that is their purpose.
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.
oh right. I'm gonna go with trgm, i think i saw an example of that somewhere else.
packages/ozone/src/daemon/context.ts
Outdated
const teamProfileSynchronizer = new TeamProfileSynchronizer( | ||
backgroundQueue, | ||
{ | ||
createAuthHeaders: (method: string) => |
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.
I like that pretty well too 👍
packages/ozone/src/team/index.ts
Outdated
const { count } = await this.db.db | ||
.selectFrom('member') | ||
.select(this.db.db.fn.count<number>('did').as('count')) | ||
.executeTakeFirstOrThrow() | ||
|
||
let lastDid = '' | ||
// Max 25 profiles can be fetched at a time so let's pull 25 members at a time from the db and update their profile details | ||
for (let i = 0; i < count; i += 25) { |
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.
Rather than fetching the count
and relying on it not changing, you could alternately only use the lastDid
to iterate through. I think it's sort of nice since you're already iterating by lastDid
. It would look more like:
let lastDid = ''
do {
// ...
lastDid = dids.at(-1) || ''
} while (lastDid)
agent: ctx.appviewAgent, | ||
createAuthHeaders: ctx.appviewAuth, | ||
} | ||
const profiles = await teamService.getProfiles([did], appviewCreator) |
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.
This PR adds
handle
anddisplayName
columns to the member table and keeps those values in sync with member profile through a new daemon that runs every 24hrs by default.This allows searching for team members using handle/display name.