Skip to content
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

Additional channel/chat lifecycle events #2471

Merged
merged 15 commits into from
Jul 24, 2020
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 127 additions & 2 deletions libraries/botbuilder/src/teamsActivityHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,10 +368,25 @@ export class TeamsActivityHandler extends ActivityHandler {

case 'channelRenamed':
return await this.onTeamsChannelRenamed(context);


case 'teamArchived':
return await this.onTeamsTeamArchived(context);

case 'teamDeleted':
return await this.onTeamsTeamDeleted(context);

case 'teamHardDeleted':
return await this.onTeamsTeamHardDeleted(context);

case 'teamRenamed':
return await this.onTeamsTeamRenamed(context);


case 'teamRestored':
return await this.onTeamsTeamRestored(context);

case 'teamUnarchived':
return await this.onTeamsTeamUnarchived(context);

default:
return await super.dispatchConversationUpdateActivity(context);
}
Expand Down Expand Up @@ -468,6 +483,36 @@ export class TeamsActivityHandler extends ActivityHandler {
await this.handle(context, 'TeamsChannelRenamed', this.defaultNextEvent(context));
}

/**
* Invoked when a Team Archived event activity is received from the connector.
* Team Archived correspond to the user archiving a team.
* @param context The context for this turn.
* @returns A promise that represents the work queued.
*/
protected async onTeamsTeamArchived(context): Promise<void> {
await this.handle(context, 'TeamsTeamArchived', this.defaultNextEvent(context));
}

/**
* Invoked when a Team Deleted event activity is received from the connector.
* Team Deleted correspond to the user deleting a team.
* @param context The context for this turn.
* @returns A promise that represents the work queued.
*/
protected async onTeamsTeamDeleted(context): Promise<void> {
await this.handle(context, 'TeamsTeamDeleted', this.defaultNextEvent(context));
}

/**
* Invoked when a Team Hard Deleted event activity is received from the connector.
* Team Hard Deleted correspond to the user hard-deleting a team.
* @param context The context for this turn.
* @returns A promise that represents the work queued.
*/
protected async onTeamsTeamHardDeleted(context): Promise<void> {
await this.handle(context, 'TeamsTeamHardDeleted', this.defaultNextEvent(context));
}

/**
*
* @param context
Expand All @@ -476,6 +521,26 @@ export class TeamsActivityHandler extends ActivityHandler {
await this.handle(context, 'TeamsTeamRenamed', this.defaultNextEvent(context));
}

/**
* Invoked when a Team Restored event activity is received from the connector.
* Team Restored correspond to the user restoring a team.
* @param context The context for this turn.
* @returns A promise that represents the work queued.
*/
protected async onTeamsTeamRestored(context): Promise<void> {
await this.handle(context, 'TeamsTeamRestored', this.defaultNextEvent(context));
}

/**
* Invoked when a Team Unarchived event activity is received from the connector.
* Team Unarchived correspond to the user unarchiving a team.
* @param context The context for this turn.
* @returns A promise that represents the work queued.
*/
protected async onTeamsTeamUnarchived(context): Promise<void> {
await this.handle(context, 'TeamsTeamUnarchived', this.defaultNextEvent(context));
}

/**
*
* @param handler
Expand Down Expand Up @@ -530,6 +595,42 @@ export class TeamsActivityHandler extends ActivityHandler {
await handler(teamsChannelData.channel, teamsChannelData.team, context, next);
});
}

/**
* Override this in a derived class to provide logic for when a team is archived.
* @param handler
* @returns A promise that represents the work queued.
*/
public onTeamsTeamArchivedEvent(handler: (teamInfo: TeamInfo, context: TurnContext, next: () => Promise<void>) => Promise<void>): this {
return this.on('TeamsTeamArchived', async (context, next) => {
const teamsChannelData = context.activity.channelData as TeamsChannelData;
await handler(teamsChannelData.team, context, next);
});
}

/**
* Override this in a derived class to provide logic for when a team is deleted.
* @param handler
* @returns A promise that represents the work queued.
*/
public onTeamsTeamDeletedEvent(handler: (teamInfo: TeamInfo, context: TurnContext, next: () => Promise<void>) => Promise<void>): this {
return this.on('TeamsTeamDeleted', async (context, next) => {
const teamsChannelData = context.activity.channelData as TeamsChannelData;
await handler(teamsChannelData.team, context, next);
});
}

/**
* Override this in a derived class to provide logic for when a team is hard-deleted.
* @param handler
* @returns A promise that represents the work queued.
*/
public onTeamsTeamHardDeletedEvent(handler: (teamInfo: TeamInfo, context: TurnContext, next: () => Promise<void>) => Promise<void>): this {
return this.on('TeamsTeamHardDeleted', async (context, next) => {
const teamsChannelData = context.activity.channelData as TeamsChannelData;
await handler(teamsChannelData.team, context, next);
});
}

/**
*
Expand All @@ -541,4 +642,28 @@ export class TeamsActivityHandler extends ActivityHandler {
await handler(teamsChannelData.team, context, next);
});
}

/**
* Override this in a derived class to provide logic for when a team is restored.
* @param handler
* @returns A promise that represents the work queued.
*/
public onTeamsTeamRestoredEvent(handler: (teamInfo: TeamInfo, context: TurnContext, next: () => Promise<void>) => Promise<void>): this {
return this.on('TeamsTeamRestored', async (context, next) => {
const teamsChannelData = context.activity.channelData as TeamsChannelData;
await handler(teamsChannelData.team, context, next);
});
}

/**
* Override this in a derived class to provide logic for when a team is unarchived.
* @param handler
* @returns A promise that represents the work queued.
*/
public onTeamsTeamUnarchivedEvent(handler: (teamInfo: TeamInfo, context: TurnContext, next: () => Promise<void>) => Promise<void>): this {
return this.on('TeamsTeamUnarchived', async (context, next) => {
const teamsChannelData = context.activity.channelData as TeamsChannelData;
await handler(teamsChannelData.team, context, next);
});
}
}
denscollo marked this conversation as resolved.
Show resolved Hide resolved
215 changes: 215 additions & 0 deletions libraries/botbuilder/tests/teamsActivityHandler.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1526,6 +1526,221 @@ describe('TeamsActivityHandler', () => {
.catch(err => done(err));
});

it('onTeamsTeamArchived routed activity', () => {
const bot = new TeamsActivityHandler();

let onTeamsTeamArchivedEventCalled = false;

const team = { id: 'team' };
const activity = createConvUpdateActivity({ team, eventType: 'teamArchived' });

bot.onConversationUpdate(async (context, next) => {
assert(context, 'context not found');
assert(next, 'next not found');
onConversationUpdateCalled = true;
await next();
});

bot.onTeamsTeamArchivedEvent(async (teamInfo, context, next) => {
assert(teamInfo, 'teamsInfo not found');
assert(context, 'context not found');
assert(next, 'next not found');
assert.strictEqual(teamInfo, team);
onTeamsTeamArchivedEventCalled = true;
await next();
});

bot.onDialog(async (context, next) => {
assert(context, 'context not found');
assert(next, 'next not found');
onDialogCalled = true;
await next();
});

const adapter = new TestAdapter(async context => {
await bot.run(context);
});

adapter.send(activity)
.then(() => {
assert(onTeamsTeamArchivedEventCalled, 'onTeamsTeamArchivedEvent handler not called');
assert(onConversationUpdateCalled, 'onConversationUpdate handler not called');
assert(onDialogCalled, 'onDialog handler not called');
});
denscollo marked this conversation as resolved.
Show resolved Hide resolved
});

it('onTeamsTeamDeleted routed activity', () => {
const bot = new TeamsActivityHandler();

let onTeamsTeamDeletedEventCalled = false;

const team = { id: 'team' };
const activity = createConvUpdateActivity({ team, eventType: 'teamDeleted' });

bot.onConversationUpdate(async (context, next) => {
assert(context, 'context not found');
assert(next, 'next not found');
onConversationUpdateCalled = true;
await next();
});

bot.onTeamsTeamDeletedEvent(async (teamInfo, context, next) => {
assert(teamInfo, 'teamsInfo not found');
assert(context, 'context not found');
assert(next, 'next not found');
assert.strictEqual(teamInfo, team);
onTeamsTeamDeletedEventCalled = true;
await next();
});

bot.onDialog(async (context, next) => {
assert(context, 'context not found');
assert(next, 'next not found');
onDialogCalled = true;
await next();
});

const adapter = new TestAdapter(async context => {
await bot.run(context);
});

adapter.send(activity)
.then(() => {
assert(onTeamsTeamDeletedEventCalled, 'onTeamsTeamDeletedEvent handler not called');
assert(onConversationUpdateCalled, 'onConversationUpdate handler not called');
assert(onDialogCalled, 'onDialog handler not called');
});
});

it('onTeamsTeamHardDeleted routed activity', () => {
const bot = new TeamsActivityHandler();

let onTeamsTeamHardDeletedEventCalled = false;

const team = { id: 'team' };
const activity = createConvUpdateActivity({ team, eventType: 'teamHardDeleted' });

bot.onConversationUpdate(async (context, next) => {
assert(context, 'context not found');
assert(next, 'next not found');
onConversationUpdateCalled = true;
await next();
});

bot.onTeamsTeamHardDeletedEvent(async (teamInfo, context, next) => {
assert(teamInfo, 'teamsInfo not found');
assert(context, 'context not found');
assert(next, 'next not found');
assert.strictEqual(teamInfo, team);
onTeamsTeamHardDeletedEventCalled = true;
await next();
});

bot.onDialog(async (context, next) => {
assert(context, 'context not found');
assert(next, 'next not found');
onDialogCalled = true;
await next();
});

const adapter = new TestAdapter(async context => {
await bot.run(context);
});

adapter.send(activity)
.then(() => {
assert(onTeamsTeamHardDeletedEventCalled, 'onTeamsTeamHardDeletedEvent handler not called');
assert(onConversationUpdateCalled, 'onConversationUpdate handler not called');
assert(onDialogCalled, 'onDialog handler not called');
});
});

it('onTeamsTeamRestored routed activity', () => {
const bot = new TeamsActivityHandler();

let onTeamsTeamRestoredEventCalled = false;

const team = { id: 'team' };
const activity = createConvUpdateActivity({ team, eventType: 'teamRestored' });

bot.onConversationUpdate(async (context, next) => {
assert(context, 'context not found');
assert(next, 'next not found');
onConversationUpdateCalled = true;
await next();
});

bot.onTeamsTeamRestoredEvent(async (teamInfo, context, next) => {
assert(teamInfo, 'teamsInfo not found');
assert(context, 'context not found');
assert(next, 'next not found');
assert.strictEqual(teamInfo, team);
onTeamsTeamRestoredEventCalled = true;
await next();
});

bot.onDialog(async (context, next) => {
assert(context, 'context not found');
assert(next, 'next not found');
onDialogCalled = true;
await next();
});

const adapter = new TestAdapter(async context => {
await bot.run(context);
});

adapter.send(activity)
.then(() => {
assert(onTeamsTeamRestoredEventCalled, 'onTeamsTeamRestoredEvent handler not called');
assert(onConversationUpdateCalled, 'onConversationUpdate handler not called');
assert(onDialogCalled, 'onDialog handler not called');
});
});

it('onTeamsTeamUnarchived routed activity', () => {
const bot = new TeamsActivityHandler();

let onTeamsTeamUnarchivedEventCalled = false;

const team = { id: 'team' };
const activity = createConvUpdateActivity({ team, eventType: 'teamUnarchived' });

bot.onConversationUpdate(async (context, next) => {
assert(context, 'context not found');
assert(next, 'next not found');
onConversationUpdateCalled = true;
await next();
});

bot.onTeamsTeamUnarchivedEvent(async (teamInfo, context, next) => {
assert(teamInfo, 'teamsInfo not found');
assert(context, 'context not found');
assert(next, 'next not found');
assert.strictEqual(teamInfo, team);
onTeamsTeamUnarchivedEventCalled = true;
await next();
});

bot.onDialog(async (context, next) => {
assert(context, 'context not found');
assert(next, 'next not found');
onDialogCalled = true;
await next();
});

const adapter = new TestAdapter(async context => {
await bot.run(context);
});

adapter.send(activity)
.then(() => {
assert(onTeamsTeamUnarchivedEventCalled, 'onTeamsTeamUnarchivedEvent handler not called');
assert(onConversationUpdateCalled, 'onConversationUpdate handler not called');
assert(onDialogCalled, 'onDialog handler not called');
});
});

it('signin/verifyState routed activity', done => {
onDialogCalled = false;
handleTeamsSigninVerifyStateCalled = false;
Expand Down