Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 3 additions & 2 deletions packages/apps/src/app.oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import * as graph from '@microsoft/teams.graph';
import { App } from './app';
import * as contexts from './contexts';
import { IPlugin } from './types';
import { PluginAdditionalContext } from './types/app-routing';

export async function onTokenExchange<TPlugin extends IPlugin>(
this: App<TPlugin>,
ctx: contexts.IActivityContext<ISignInTokenExchangeInvokeActivity>
ctx: contexts.IActivityContext<ISignInTokenExchangeInvokeActivity, PluginAdditionalContext<TPlugin>>
) {
const { api, activity, log } = ctx;

Expand Down Expand Up @@ -62,7 +63,7 @@ export async function onTokenExchange<TPlugin extends IPlugin>(

export async function onVerifyState<TPlugin extends IPlugin>(
this: App<TPlugin>,
ctx: contexts.IActivityContext<ISignInVerifyStateInvokeActivity>
ctx: contexts.IActivityContext<ISignInVerifyStateInvokeActivity, PluginAdditionalContext<TPlugin>>
) {
const { log, api, activity } = ctx;

Expand Down
21 changes: 16 additions & 5 deletions packages/apps/src/app.process.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { ActivityLike, ConversationReference, isInvokeResponse } from '@microsoft/teams.api';




import { ApiClient, GraphClient } from './api';
import { App } from './app';
import { ActivityContext, IActivityContext } from './contexts';
Expand Down Expand Up @@ -70,18 +67,31 @@ export async function $process<TPlugin extends IPlugin>(

const routes = this.router.select(activity);

let pluginContexts: {} = {};
for (let i = this.plugins.length - 1; i > -1; i--) {
const plugin = this.plugins[i];

if (plugin.onActivity) {
routes.unshift(({ next }) => {
plugin.onActivity!({
routes.unshift(async ({ next }) => {
const additionalPluginContext = await plugin.onActivity!({
...ref,
sender: sender,
activity,
token,
});

if (additionalPluginContext) {
for (const key in additionalPluginContext) {
if (key in pluginContexts) {
this.log.warn(`Plugin context key "${key}" already exists. Overriding.`);
}
}
pluginContexts = {
...pluginContexts,
...additionalPluginContext,
};
}

return next();
});
}
Expand Down Expand Up @@ -117,6 +127,7 @@ export async function $process<TPlugin extends IPlugin>(
storage: this.storage,
isSignedIn: !!userToken,
connectionName: this.oauth.defaultConnectionName,
...pluginContexts
});

if (routes.length === 0) {
Expand Down
15 changes: 10 additions & 5 deletions packages/apps/src/app.routing.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import { InvokeResponse } from '@microsoft/teams.api';
import { Activity, InvokeResponse } from '@microsoft/teams.api';

import { App } from './app';
import { IActivityContext } from './contexts';
import { IRoutes } from './routes';
import { IPlugin, RouteHandler } from './types';
import { PluginAdditionalContext } from './types/app-routing';

type AppPlugin<TApp extends App> = TApp extends App<infer TPlugin> ? TPlugin : never;

export type AppRoutingHandler<Name extends keyof IRoutes, TApp extends App<any>> = Exclude<IRoutes<PluginAdditionalContext<AppPlugin<TApp>>>[Name], undefined>;

/**
* subscribe to an event
* @param name event to subscribe to
* @param cb callback to invoke
*/
export function on<TPlugin extends IPlugin, Name extends keyof IRoutes>(
export function on<TPlugin extends IPlugin, Name extends keyof IRoutes,>(
this: App<TPlugin>,
name: Name,
cb: Exclude<IRoutes[Name], undefined>
cb: Exclude<IRoutes<PluginAdditionalContext<TPlugin>>[Name], undefined>
) {
this.router.on(name, cb);
return this;
Expand All @@ -27,7 +32,7 @@ export function on<TPlugin extends IPlugin, Name extends keyof IRoutes>(
export function message<TPlugin extends IPlugin>(
this: App<TPlugin>,
pattern: string | RegExp,
cb: Exclude<IRoutes['message'], undefined>
cb: Exclude<IRoutes<PluginAdditionalContext<TPlugin>>['message'], undefined>
) {
this.router.register<'message'>({
select: (activity) => {
Expand All @@ -49,7 +54,7 @@ export function message<TPlugin extends IPlugin>(
*/
export function use<TPlugin extends IPlugin>(
this: App<TPlugin>,
cb: RouteHandler<IActivityContext, void | InvokeResponse>
cb: RouteHandler<IActivityContext<Activity, PluginAdditionalContext<TPlugin>>, void | InvokeResponse>
) {
this.router.use(cb);
return this;
Expand Down
7 changes: 4 additions & 3 deletions packages/apps/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
IToken,
JsonWebToken,
StripMentionsTextOptions,
toActivityParams,
toActivityParams
} from '@microsoft/teams.api';
import { EventEmitter } from '@microsoft/teams.common/events';
import * as http from '@microsoft/teams.common/http';
Expand Down Expand Up @@ -41,6 +41,7 @@ import { DEFAULT_OAUTH_SETTINGS, OAuthSettings } from './oauth';
import { HttpPlugin } from './plugins';
import { Router } from './router';
import { AppEvents, IPlugin } from './types';
import { PluginAdditionalContext } from './types/app-routing';

/**
* App initialization options
Expand Down Expand Up @@ -181,7 +182,7 @@ export class App<TPlugin extends IPlugin = IPlugin> {

protected container = new Container();
protected plugins: Array<TPlugin> = [];
protected router = new Router();
protected router = new Router<PluginAdditionalContext<TPlugin>>();
protected tenantTokens = new LocalStorage<string>({}, { max: 20000 });
protected events = new EventEmitter<AppEvents<TPlugin>>();
protected startedAt?: Date;
Expand Down Expand Up @@ -552,4 +553,4 @@ export class App<TPlugin extends IPlugin = IPlugin> {

return appToken;
}
}
}
2 changes: 1 addition & 1 deletion packages/apps/src/contexts/activity-signin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
import { IActivityContext } from './activity';

export interface IActivitySignInContext
extends IActivityContext<ISignInTokenExchangeInvokeActivity | ISignInVerifyStateInvokeActivity> {
extends IActivityContext<ISignInTokenExchangeInvokeActivity | ISignInVerifyStateInvokeActivity, any> {
/**
* the token response of the signin request
*/
Expand Down
19 changes: 12 additions & 7 deletions packages/apps/src/contexts/activity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { IStorage } from '@microsoft/teams.common/storage';
import { ApiClient, GraphClient } from '../api';
import { ISender, IStreamer } from '../types';

export interface IActivityContextOptions<T extends Activity = Activity> {
export interface IBaseActivityContextOptions<T extends Activity = Activity, TExtraCtx extends Record<string, any> = Record<string, any>> {
/**
* the app id of the bot
*/
Expand Down Expand Up @@ -89,10 +89,12 @@ export interface IActivityContextOptions<T extends Activity = Activity> {
* call the next event/middleware handler
*/
next: (
context?: IActivityContext
context?: IActivityContext & TExtraCtx
) => (void | InvokeResponse) | Promise<void | InvokeResponse>;
}

export type IActivityContextOptions<T extends Activity = Activity, TExtraCtx extends Record<string, any> = Record<string, any>> = IBaseActivityContextOptions<T, TExtraCtx> & TExtraCtx;

type SignInOptions = {
/**
* The text to display on the oauth card
Expand All @@ -116,8 +118,8 @@ type SignInOptions = {
) => ActivityLike;
};

export interface IActivityContext<T extends Activity = Activity>
extends IActivityContextOptions<T> {
export interface IBaseActivityContext<T extends Activity = Activity, TExtraCtx extends Record<string, any> = Record<string, any>>
extends IBaseActivityContextOptions<T, TExtraCtx> {
/**
* a stream that can emit activity chunks
*/
Expand Down Expand Up @@ -149,13 +151,16 @@ export interface IActivityContext<T extends Activity = Activity>
signout: (name?: string) => Promise<void>;
}

export type IActivityContext<T extends Activity = Activity, TExtraContext = unknown> =
IBaseActivityContext<T> & (TExtraContext extends Record<string, any> ? TExtraContext : {});

export const DEFAULT_SIGNIN_OPTIONS: SignInOptions = {
oauthCardText: 'Please Sign In...',
signInButtonText: 'Sign In',
};

export class ActivityContext<T extends Activity = Activity>
implements IActivityContext<T> {
export class ActivityContext<T extends Activity = Activity, TExtraCtx extends {} = {}>
implements IBaseActivityContext<T, TExtraCtx> {
appId!: string;
activity!: T;
ref!: ConversationReference;
Expand All @@ -177,7 +182,7 @@ export class ActivityContext<T extends Activity = Activity>
context?: IActivityContext
) => (void | InvokeResponse) | Promise<void | InvokeResponse>;

constructor(plugin: ISender, value: IActivityContextOptions) {
constructor(plugin: ISender, value: IBaseActivityContextOptions) {
Object.assign(this, value);
this._plugin = plugin;
this.stream = plugin.createStream(value.ref);
Expand Down
2 changes: 1 addition & 1 deletion packages/apps/src/middleware/strip-mentions-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as api from '@microsoft/teams.api';
import { IActivityContext } from '../contexts';

export function stripMentionsText(options?: api.StripMentionsTextOptions) {
return ({ activity, next }: IActivityContext) => {
return ({ activity, next }: IActivityContext<api.Activity, any>) => {
if (
activity.type === 'message' ||
activity.type === 'messageUpdate' ||
Expand Down
18 changes: 9 additions & 9 deletions packages/apps/src/router.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { Activity } from '@microsoft/teams.api';
import { Activity, InvokeResponse } from '@microsoft/teams.api';

import { IActivityContext } from './contexts';
import { EVENT_ALIASES, IRoutes, INVOKE_ALIASES } from './routes';
import { EVENT_ALIASES, INVOKE_ALIASES, IRoutes } from './routes';
import { RouteHandler } from './types';

type Route<Name extends keyof IRoutes = keyof IRoutes> = {
type Route<Name extends keyof IRoutes = keyof IRoutes, TExtraCtx extends Record<string, any> = Record<string, any>> = {
readonly name?: Name;
readonly select: (activity: Activity) => boolean;
readonly callback: IRoutes[Name];
readonly callback: IRoutes<TExtraCtx>[Name];
};

export class Router {
protected readonly routes: Route[] = [];
export class Router<TExtraCtx extends Record<string, any> = Record<string, any>> {
protected readonly routes: Route<keyof IRoutes, TExtraCtx>[] = [];

/**
* select routes that match the inbound activity
Expand All @@ -27,7 +27,7 @@ export class Router {
* register a new route
* @param route the route to register
*/
register<Name extends keyof IRoutes>(route: Route<Name>) {
register<Name extends keyof IRoutes>(route: Route<Name, TExtraCtx>) {
this.routes.push(route);
return this;
}
Expand All @@ -36,7 +36,7 @@ export class Router {
* register a middleware
* @param callback the callback to invoke
*/
use(callback: RouteHandler<IActivityContext, any>) {
use(callback: RouteHandler<IActivityContext<Activity, TExtraCtx>, void | InvokeResponse>) {
this.register({
select: () => true,
callback,
Expand All @@ -50,7 +50,7 @@ export class Router {
* @param event event to subscribe to
* @param callback the callback to invoke
*/
on<Name extends keyof IRoutes>(event: Name, callback: Exclude<IRoutes[Name], undefined>) {
on<Name extends keyof IRoutes>(event: Name, callback: Exclude<IRoutes<TExtraCtx>[Name], undefined>) {
this.register({
name: event,
select: (activity) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/apps/src/routes/activity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ import { Activity } from '@microsoft/teams.api';
import { IActivityContext } from '../contexts';
import { RouteHandler } from '../types';

export type ActivityRoutes = {
[K in Activity['type']]?: RouteHandler<IActivityContext<Extract<Activity, { type: K }>>>;
export type ActivityRoutes<TExtraCtx extends Record<string, any> = Record<string, any>> = {
[K in Activity['type']]?: RouteHandler<IActivityContext<Extract<Activity, { type: K }>, TExtraCtx>>;
};
6 changes: 3 additions & 3 deletions packages/apps/src/routes/conversation-update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { IConversationUpdateActivity } from '@microsoft/teams.api';
import { IActivityContext } from '../contexts';
import { RouteHandler } from '../types';

export type ConversationUpdateActivityRoutes = {
[K in IConversationUpdateActivity['channelData']['eventType'] as K]?: RouteHandler<
IActivityContext<IConversationUpdateActivity>,
export type ConversationUpdateActivityRoutes<TExtraCtx extends Record<string, any> = Record<string, any>> = {
[K in IConversationUpdateActivity['channelData']['eventType']as K]?: RouteHandler<
IActivityContext<IConversationUpdateActivity, TExtraCtx>,
void
>;
};
6 changes: 3 additions & 3 deletions packages/apps/src/routes/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { EventActivity } from '@microsoft/teams.api';
import { IActivityContext } from '../contexts';
import { RouteHandler } from '../types';

export type EventActivityRoutes = {
[K in EventActivity['name'] as EventAliases[K]]?: RouteHandler<
IActivityContext<Extract<EventActivity, { name: K }>>,
export type EventActivityRoutes<TExtraCtx extends Record<string, any> = Record<string, any>> = {
[K in EventActivity['name']as EventAliases[K]]?: RouteHandler<
IActivityContext<Extract<EventActivity, { name: K }>, TExtraCtx>,
void
>;
};
Expand Down
29 changes: 16 additions & 13 deletions packages/apps/src/routes/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Activity } from '@microsoft/teams.api';

import { IActivityContext } from '../contexts';
import { RouteHandler } from '../types';

Expand All @@ -9,22 +11,23 @@ import { InvokeActivityRoutes } from './invoke';
import { MessageDeleteActivityRoutes } from './message-delete';
import { MessageUpdateActivityRoutes } from './message-update';

export interface IRoutes
extends ActivityRoutes,
InvokeActivityRoutes,
InstallActivityRoutes,
ConversationUpdateActivityRoutes,
MessageUpdateActivityRoutes,
MessageDeleteActivityRoutes,
EventActivityRoutes {
mention?: RouteHandler<IActivityContext>;
activity?: RouteHandler<IActivityContext>;
export interface IRoutes<TExtraCtx extends Record<string, any> = Record<string, any>>
extends ActivityRoutes<TExtraCtx>,
InvokeActivityRoutes<TExtraCtx>,
InstallActivityRoutes<TExtraCtx>,
ConversationUpdateActivityRoutes<TExtraCtx>,
MessageUpdateActivityRoutes<TExtraCtx>,
MessageDeleteActivityRoutes<TExtraCtx>,
EventActivityRoutes<TExtraCtx> {
mention?: RouteHandler<IActivityContext<Activity, TExtraCtx>>;
activity?: RouteHandler<IActivityContext<Activity, TExtraCtx>>;
}

export * from './activity';
export * from './conversation-update';
export * from './event';
export * from './install';
export * from './invoke';
export * from './conversation-update';
export * from './message-update';
export * from './message-delete';
export * from './event';
export * from './message-update';

6 changes: 3 additions & 3 deletions packages/apps/src/routes/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { InstallUpdateActivity } from '@microsoft/teams.api';
import { IActivityContext } from '../contexts';
import { RouteHandler } from '../types';

export type InstallActivityRoutes = {
[K in InstallUpdateActivity['action'] as `install.${K}`]?: RouteHandler<
IActivityContext<Extract<InstallUpdateActivity, { action: K }>>,
export type InstallActivityRoutes<TExtraCtx extends Record<string, any> = Record<string, any>> = {
[K in InstallUpdateActivity['action']as `install.${K}`]?: RouteHandler<
IActivityContext<Extract<InstallUpdateActivity, { action: K }>, TExtraCtx>,
void
>;
};
7 changes: 4 additions & 3 deletions packages/apps/src/routes/invoke/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { FileConsentActivityRoutes } from './file-consent';
import { MessageExtensionSubmitActivityRoutes } from './message-extension-submit';
import { MessageSubmitActivityRoutes } from './message-submit';

export type InvokeActivityRoutes = {
[K in InvokeActivity['name'] as InvokeAliases[K]]?: RouteHandler<
IActivityContext<Extract<InvokeActivity, { name: K }>>,
export type InvokeActivityRoutes<TExtraCtx extends Record<string, any> = Record<string, any>> = {
[K in InvokeActivity['name']as InvokeAliases[K]]?: RouteHandler<
IActivityContext<Extract<InvokeActivity, { name: K }>, TExtraCtx>,
InvokeResponse<K> | InvokeResponse<K>['body']
>;
} & FileConsentActivityRoutes &
Expand Down Expand Up @@ -69,3 +69,4 @@ export const INVOKE_ALIASES: InvokeAliases = {
export * from './file-consent';
export * from './message-extension-submit';
export * from './message-submit';

Loading