Skip to content

Commit

Permalink
refactor: update bot notification related samples with latest SDK (#977)
Browse files Browse the repository at this point in the history
  • Loading branch information
qinezh authored Aug 7, 2023
1 parent 2e420b6 commit 706fd1a
Show file tree
Hide file tree
Showing 12 changed files with 221 additions and 116 deletions.
6 changes: 3 additions & 3 deletions adaptive-card-notification/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ Adaptive Card Notification provides an easy way to send notification in Teams. T
};
```
1. Use Azure Blob Storage to persist notification connections
This sample provides an implementation of `NotificationTargetStorage` at `src/storage/blobsStorage.ts`, which connects to Azure Blob Storage to persist notification connections.
This sample provides an implementation of `NotificationTargetStorage` at `src/storage/blobsStore.ts`, which connects to Azure Blob Storage to persist notification connections.

To try it, uncomment the `notification.storage` settings of your bot in `src/internal/initialize.ts`, then enter your own connection string and container name.
To try it, uncomment the `notification.store` settings of your bot in `src/internal/initialize.ts`, then enter your own connection string and container name.
``` typescript
...
notification: {
enabled: true,
storage: new BlobsStorage("{your-connection-string}", "{your-container-name}"),
store: new BlobStore("{your-connection-string}", "{your-container-name}"),
},
...
```
Expand Down
17 changes: 12 additions & 5 deletions adaptive-card-notification/src/columnsetNotificationHttpTrigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,18 @@ const httpTrigger: AzureFunction = async function (
context: Context,
req: HttpRequest
): Promise<void> {
for (const target of await notificationApp.notification.installations()) {
await target.sendAdaptiveCard(
AdaptiveCards.declare<ColumnsetData>(notificationTemplate).render(data)
);
}
const pageSize = 100;
let continuationToken: string | undefined;
do {
const pagedInstallations = await notificationApp.notification.getPagedInstallations(pageSize, continuationToken);
continuationToken = pagedInstallations.continuationToken;
const targets = pagedInstallations.data;
for (const target of targets) {
await target.sendAdaptiveCard(
AdaptiveCards.declare<ColumnsetData>(notificationTemplate).render(data)
);
}
} while (continuationToken);

context.res = {};
};
Expand Down
17 changes: 12 additions & 5 deletions adaptive-card-notification/src/defaultNotificationHttpTrigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,18 @@ const httpTrigger: AzureFunction = async function (
context: Context,
req: HttpRequest
): Promise<void> {
for (const target of await notificationApp.notification.installations()) {
await target.sendAdaptiveCard(
AdaptiveCards.declare<CardData>(notificationTemplate).render(data)
);
}
const pageSize = 100;
let continuationToken: string | undefined;
do {
const pagedInstallations = await notificationApp.notification.getPagedInstallations(pageSize, continuationToken);
continuationToken = pagedInstallations.continuationToken;
const targets = pagedInstallations.data;
for (const target of targets) {
await target.sendAdaptiveCard(
AdaptiveCards.declare<CardData>(notificationTemplate).render(data)
);
}
} while (continuationToken);

context.res = {};
};
Expand Down
17 changes: 12 additions & 5 deletions adaptive-card-notification/src/factsetNotificationHttpTrigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,18 @@ const httpTrigger: AzureFunction = async function (
context: Context,
req: HttpRequest
): Promise<void> {
for (const target of await notificationApp.notification.installations()) {
await target.sendAdaptiveCard(
AdaptiveCards.declare<FactsetData>(notificationTemplate).render(data)
);
}
const pageSize = 100;
let continuationToken: string | undefined;
do {
const pagedInstallations = await notificationApp.notification.getPagedInstallations(pageSize, continuationToken);
continuationToken = pagedInstallations.continuationToken;
const targets = pagedInstallations.data;
for (const target of targets) {
await target.sendAdaptiveCard(
AdaptiveCards.declare<FactsetData>(notificationTemplate).render(data)
);
}
} while (continuationToken);

context.res = {};
};
Expand Down
5 changes: 3 additions & 2 deletions adaptive-card-notification/src/internal/initialize.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BotBuilderCloudAdapter } from "@microsoft/teamsfx";
import ConversationBot = BotBuilderCloudAdapter.ConversationBot;
import config from "./config";
import { BlobStore } from "../store/blobStore";

// Create bot.
export const notificationApp = new ConversationBot({
Expand All @@ -14,7 +15,7 @@ export const notificationApp = new ConversationBot({
// Enable notification
notification: {
enabled: true,
// uncomment following line to use your own blob storage
// storage: new BlobsStorage("{your-connection-string}", "{your-container-name}"),
// uncomment following line to use your own blob store
// store: new BlobStore("{your-connection-string}", "{your-container-name}"),
},
});
17 changes: 12 additions & 5 deletions adaptive-card-notification/src/listNotificationHttpTrigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,18 @@ const httpTrigger: AzureFunction = async function (
context: Context,
req: HttpRequest
): Promise<void> {
for (const target of await notificationApp.notification.installations()) {
await target.sendAdaptiveCard(
AdaptiveCards.declare<ListData>(notificationTemplate).render(data)
);
}
const pageSize = 100;
let continuationToken: string | undefined;
do {
const pagedInstallations = await notificationApp.notification.getPagedInstallations(pageSize, continuationToken);
continuationToken = pagedInstallations.continuationToken;
const targets = pagedInstallations.data;
for (const target of targets) {
await target.sendAdaptiveCard(
AdaptiveCards.declare<ListData>(notificationTemplate).render(data)
);
}
} while (continuationToken);

context.res = {};
};
Expand Down
36 changes: 25 additions & 11 deletions adaptive-card-notification/src/mentionNotificationHttpTrigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,35 @@ const httpTrigger: AzureFunction = async function (
context: Context,
req: HttpRequest
): Promise<void> {
for (const target of await notificationApp.notification.installations()) {
await target.sendAdaptiveCard(
AdaptiveCards.declare<MentionData>(notificationTemplate).render(data)
);

/** List all members then notify each member
for (const member of await target.members()) {
data.userId = member.account.email;
data.userName = member.account.name;
const pageSize = 100;
let continuationToken: string | undefined;
do {
const pagedInstallations = await notificationApp.notification.getPagedInstallations(pageSize, continuationToken);
continuationToken = pagedInstallations.continuationToken;
const targets = pagedInstallations.data;
for (const target of targets) {
await target.sendAdaptiveCard(
AdaptiveCards.declare<MentionData>(notificationTemplate).render(data)
);

/** List all members then notify each member
const memberPageSize = 10;
let memberContinuationToken: string | undefined;
do {
const pagedMembers = await target.getPagedMembers(memberPageSize, memberContinuationToken);
memberContinuationToken = pagedMembers.continuationToken;
const members = pagedMembers.data;
for (const member of members) {
data.userId = member.account.email;
data.userName = member.account.name;
await target.sendAdaptiveCard(
AdaptiveCards.declare<MentionData>(notificationTemplate).render(data)
);
}
} while (memberContinuationToken);
*/
}
*/
}
} while (continuationToken);

context.res = {};
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { ContainerClient } from "@azure/storage-blob";
import { NotificationTargetStorage } from "@microsoft/teamsfx";
import { ConversationReference } from "botbuilder";
import { ContainerClient, ContainerListBlobFlatSegmentResponse } from "@azure/storage-blob";
import { ConversationReferenceStore, ConversationReferenceStoreAddOptions, PagedData } from "@microsoft/teamsfx";

// A sample implementation to use Azure Blob Storage as notification target storage
export class BlobsStorage implements NotificationTargetStorage {
// A sample implementation to use Azure Blob Storage as conversation reference store
export class BlobStore implements ConversationReferenceStore {
private readonly client: ContainerClient;
private initializePromise?: Promise<unknown>;

Expand All @@ -11,7 +12,7 @@ export class BlobsStorage implements NotificationTargetStorage {
this.client = new ContainerClient(connectionString, containerName);
}

async read(key: string): Promise<{ [key: string]: unknown; }> {
async get(key: string): Promise<Partial<ConversationReference>> {
await this.initialize();

const blobName = this.normalizeKey(key);
Expand All @@ -29,55 +30,73 @@ export class BlobsStorage implements NotificationTargetStorage {
}
}

async list(): Promise<{ [key: string]: unknown; }[]> {
async add(
key: string,
reference: Partial<ConversationReference>,
options?: ConversationReferenceStoreAddOptions
): Promise<boolean> {
await this.initialize();

const result = [];
const blobsIter = this.client.listBlobsFlat();
let blobItem = await blobsIter.next();
while (!blobItem.done) {
try {
const stream = await this.client.getBlockBlobClient(blobItem.value.name).download();
const content = await this.streamToBuffer(stream.readableStreamBody);
result.push(JSON.parse(content.toString()));
} catch (error) {
if (error.statusCode !== 404) {
throw error;
}
const blobName = this.normalizeKey(key);

try {
const content = JSON.stringify(reference);
if (options.overwrite) {
await this.client.getBlockBlobClient(blobName).upload(content, Buffer.byteLength(content));
return true;
} else if (await this.get(key) === undefined) {
await this.client.getBlockBlobClient(blobName).upload(content, Buffer.byteLength(content));
return true;
}
} catch (error) {
if (error.statusCode !== 404) {
throw error;
}
blobItem = await blobsIter.next();
}

return result;
return false;
}

async write(key: string, object: { [key: string]: unknown; }): Promise<void> {
async remove(key: string, reference: Partial<ConversationReference>): Promise<boolean> {
await this.initialize();

const blobName = this.normalizeKey(key);

try {
const content = JSON.stringify(object);
await this.client.getBlockBlobClient(blobName).upload(content, Buffer.byteLength(content));
await this.client.getBlobClient(blobName).delete();
return true;
} catch (error) {
if (error.statusCode !== 404) {
throw error;
}

return false;
}
}

async delete(key: string): Promise<void> {
async list(pageSize?: number, continuationToken?: string): Promise<PagedData<Partial<ConversationReference>>> {
await this.initialize();

const blobName = this.normalizeKey(key);
const result = new Array<Partial<ConversationReference>>();
const iterator = this.client.listBlobsFlat().byPage({ maxPageSize: pageSize, continuationToken });
const response: ContainerListBlobFlatSegmentResponse = (await iterator.next()).value;

try {
await this.client.getBlobClient(blobName).delete();
} catch (error) {
if (error.statusCode !== 404) {
throw error;
for (const blob of response.segment.blobItems) {
try {
const stream = await this.client.getBlockBlobClient(blob.name).download();
const content = await this.streamToBuffer(stream.readableStreamBody);
result.push(JSON.parse(content.toString()));
} catch (error) {
if (error.statusCode !== 404) {
throw error;
}
}
}

return {
data: result,
continuationToken: response.continuationToken
};
}

// Initialize to create container if not exists yet
Expand Down
Loading

0 comments on commit 706fd1a

Please sign in to comment.