Skip to content

Commit e941cfd

Browse files
authored
Merge pull request #1608 from OfficeDev/dev
chore: merge dev into main (samples)
2 parents e069f12 + e23b505 commit e941cfd

File tree

8 files changed

+70
-79
lines changed

8 files changed

+70
-79
lines changed

.config/samples-config-v3.json

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -735,27 +735,6 @@
735735
"dir": "samples/msgext-link-unfurling-reddit/nodejs"
736736
}
737737
},
738-
{
739-
"id": "intelligent-data-chart-generator",
740-
"shortId": "data-chart-generator",
741-
"onboardDate": "2024-01-31",
742-
"title": "Intelligent Data Chart Generator",
743-
"shortDescription": "The Microsoft Teams Tab app, powered by Azure OpenAI, interprets user inputs to generate data charts from Azure SQL.",
744-
"fullDescription": "This intelligent Microsoft Teams Tab app is powered by Azure OpenAI, which helps users generating data charts based on users' description of the data chart in human language. There is an input box where users tell the Tab app what data chart they want to generate, and the app interprets user's requirements then generate visual charts. The data is from Azure SQL. This sample demonstrates how to build a seamless and intuitive experience for accessing and interpreting data.",
745-
"types": [
746-
"Tab"
747-
],
748-
"tags": [
749-
"Tab",
750-
"TS",
751-
"Azure Functions"
752-
],
753-
"time": "30 mins to run",
754-
"configuration": "Manual configurations required",
755-
"thumbnailPath": "assets/thumbnail.png",
756-
"gifPath": "assets/sampleDemo.gif",
757-
"suggested": false
758-
},
759738
{
760739
"id": "bot-sso-docker",
761740
"shortId": "bot-sso-docker",

large-scale-notification/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ This sample demonstrates the architecture of a Teams notfication bot app created
5454
Since there are usually at most 25 users in Microsoft 365 E3/E5 subscription in a tenant, duplicate the installation data can be used to mock a large amount of users.
5555
5656
1. Copy the value of `STORAGE_ACCOUNT_NAME` in `env/.env.dev` and paste it to variable `storageAccount` in `script/mockInstallationData.ts`.
57-
2. Please ensure you are logged into the Azure account used for deploying the app.
57+
2. Please ensure you are logged into the Azure account used for deploying the app and set environment variable `AZURE_TOKEN_CREDENTIALS` to `dev`.
5858
3. Run command in project root folder: `npx ts-node script/mockInstallationData.ts`.
5959
4. Update `storageTableName` in `src/internal/initialize.ts` to `installationMockTableName`.
6060
5. Deploy the code to Azure Function by selecting `Deploy` from the Microsoft 365 Agents Toolkit sidebar. Your app should now use the mock data.

large-scale-notification/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"dependencies": {
2626
"@azure/data-tables": "^13.2.2",
2727
"@azure/functions": "^4.6.0",
28-
"@azure/identity": "^4.4.1",
28+
"@azure/identity": "^4.13.0",
2929
"@azure/service-bus": "^7.9.0",
3030
"@microsoft/agents-activity": "^0.4.3",
3131
"@microsoft/agents-hosting": "^0.4.3",

large-scale-notification/script/mockInstallationData.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,20 @@ const storageTableName = "installation";
66
const installationMockTableName = "installationmock";
77

88
async function copy() {
9-
const credential = new DefaultAzureCredential();
9+
const credential = new DefaultAzureCredential({
10+
requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"],
11+
});
1012

1113
const originInstallationTableClient = new TableClient(
1214
`https://${storageAccount}.table.core.windows.net`,
1315
`${storageTableName}`,
14-
credential
16+
credential,
1517
);
1618

1719
const mockInstallationTableClient = new TableClient(
1820
`https://${storageAccount}.table.core.windows.net`,
1921
`${installationMockTableName}`,
20-
credential
22+
credential,
2123
);
2224

2325
const pages = await originInstallationTableClient

large-scale-notification/src/functions/durableFunctionHttpTrigger.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import * as df from "durable-functions";
2-
import { DefaultAzureCredential } from "@azure/identity";
3-
import {
1+
import {
42
app,
53
HttpHandler,
64
HttpRequest,
75
HttpResponse,
86
InvocationContext,
97
} from "@azure/functions";
8+
import { ManagedIdentityCredential } from "@azure/identity";
109
import { ServiceBusAdministrationClient } from "@azure/service-bus";
10+
import * as df from "durable-functions";
1111
import {
1212
managedIdentityId,
1313
serviceBusMessageQueueName,
@@ -16,18 +16,18 @@ import {
1616

1717
const durableHttpStart: HttpHandler = async (
1818
request: HttpRequest,
19-
context: InvocationContext
19+
context: InvocationContext,
2020
): Promise<HttpResponse> => {
2121
const client = df.getClient(context);
22-
const credential = new DefaultAzureCredential({
23-
managedIdentityClientId: managedIdentityId,
22+
const credential = new ManagedIdentityCredential({
23+
clientId: managedIdentityId,
2424
});
2525
const sbAdminClient = new ServiceBusAdministrationClient(
2626
`${serviceBusNamespace}.servicebus.windows.net`,
27-
credential
27+
credential,
2828
);
2929
let queueRuntimeProperties = await sbAdminClient.getQueueRuntimeProperties(
30-
serviceBusMessageQueueName
30+
serviceBusMessageQueueName,
3131
);
3232
const instanceId = await client.startNew("sendNotifications", {
3333
instanceId: undefined,

large-scale-notification/src/functions/enqueueTasksForInstallationsActivity.ts

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import { ActivityHandler } from "durable-functions";
88
import { chunk } from "lodash";
99
import { DateTime } from "luxon";
1010

11-
import { DefaultAzureCredential } from "@azure/identity";
1211
import { InvocationContext } from "@azure/functions";
12+
import { ManagedIdentityCredential } from "@azure/identity";
1313
import {
1414
ServiceBusAdministrationClient,
1515
ServiceBusClient,
@@ -31,29 +31,28 @@ import { extractKeyDataFromConversationReference } from "../util";
3131

3232
const enqueueTasksForInstallationsActivity: ActivityHandler = async (
3333
triggerInput: any,
34-
context: InvocationContext
34+
context: InvocationContext,
3535
): Promise<{ sendStatus: SendStatus; continuationToken: string }> => {
3636
const input = triggerInput as {
3737
continuationToken: string;
3838
sendStatus: SendStatus;
3939
};
40-
const credential = new DefaultAzureCredential({
41-
managedIdentityClientId: managedIdentityId,
40+
const credential = new ManagedIdentityCredential({
41+
clientId: managedIdentityId,
4242
});
4343
let token = input.continuationToken;
4444
let installations: TeamsBotInstallation[] = [];
4545
let lastSendTime: Date = undefined;
4646
let newStatus = { ...input.sendStatus };
4747
for (let iter = 0; iter < iterateTime; iter++) {
4848
context.warn(
49-
`${new Date().toISOString()} #${iter} [enqueueTasksForInstallationsActivity] continue ${token}`
49+
`${new Date().toISOString()} #${iter} [enqueueTasksForInstallationsActivity] continue ${token}`,
50+
);
51+
const installationResult = await notificationApp.getPagedInstallations(
52+
maxPageSize,
53+
token,
54+
false,
5055
);
51-
const installationResult =
52-
await notificationApp.getPagedInstallations(
53-
maxPageSize,
54-
token,
55-
false
56-
);
5756
installations = installationResult.data;
5857
token = installationResult.continuationToken;
5958

@@ -64,13 +63,13 @@ const enqueueTasksForInstallationsActivity: ActivityHandler = async (
6463
context.warn(
6564
`${new Date().toISOString()} #${iter} [enqueueTasksForInstallationsActivity] found ${
6665
installations.length
67-
} installations`
66+
} installations`,
6867
);
6968
newStatus.totalMessageCount += installations.length;
7069

7170
const sbClient = new ServiceBusClient(
7271
`${serviceBusNamespace}.servicebus.windows.net`,
73-
credential
72+
credential,
7473
);
7574
const sender = sbClient.createSender(serviceBusMessageQueueName);
7675
const chunks = chunk(installations, RPS * batchSendingInterval);
@@ -79,8 +78,8 @@ const enqueueTasksForInstallationsActivity: ActivityHandler = async (
7978
for (const conversation of chunk) {
8079
const messageBody = JSON.stringify(
8180
extractKeyDataFromConversationReference(
82-
conversation.conversationReference
83-
)
81+
conversation.conversationReference,
82+
),
8483
);
8584
if (!batch.tryAddMessage({ body: messageBody })) {
8685
// if it can't be added to the batch, the message is probably too big to fit in a batch
@@ -95,12 +94,12 @@ const enqueueTasksForInstallationsActivity: ActivityHandler = async (
9594
.plus({ second: batchSendingInterval })
9695
.toJSDate();
9796
context.warn(
98-
`[enqueueTasksForInstallationsActivity] ${new Date().toISOString()} next enqueue time ${nextEnqueueTime.toISOString()}`
97+
`[enqueueTasksForInstallationsActivity] ${new Date().toISOString()} next enqueue time ${nextEnqueueTime.toISOString()}`,
9998
);
10099
if (nextEnqueueTime > new Date()) {
101100
const waitMs = nextEnqueueTime.getTime() - new Date().getTime();
102101
context.warn(
103-
`[enqueueTasksForInstallationsActivity] wait to ${nextEnqueueTime.toISOString()}, ${waitMs} ms`
102+
`[enqueueTasksForInstallationsActivity] wait to ${nextEnqueueTime.toISOString()}, ${waitMs} ms`,
104103
);
105104
await new Promise((r) => setTimeout(r, waitMs));
106105
}
@@ -110,13 +109,13 @@ const enqueueTasksForInstallationsActivity: ActivityHandler = async (
110109
context.warn(
111110
`[enqueueTasksForInstallationsActivity] ${lastSendTime.toISOString()} sending ${
112111
chunk[0].conversationReference.user.id
113-
}`
112+
}`,
114113
);
115114
await sender.sendMessages(batch);
116115
context.warn(
117116
`[enqueueTasksForInstallationsActivity] sent task to queue ${
118117
chunk[0].conversationReference.user.id
119-
}, cost ${new Date().getTime() - lastSendTime.getTime()} ms}`
118+
}, cost ${new Date().getTime() - lastSendTime.getTime()} ms}`,
120119
);
121120
}
122121

@@ -127,13 +126,13 @@ const enqueueTasksForInstallationsActivity: ActivityHandler = async (
127126

128127
const sbAdminClient = new ServiceBusAdministrationClient(
129128
`${serviceBusNamespace}.servicebus.windows.net`,
130-
credential
129+
credential,
131130
);
132131
const runtimeProperties = await sbAdminClient.getQueueRuntimeProperties(
133-
serviceBusMessageQueueName
132+
serviceBusMessageQueueName,
134133
);
135134
context.warn(
136-
`[enqueueTasksForInstallationsActivity] active messages: ${runtimeProperties.activeMessageCount}`
135+
`[enqueueTasksForInstallationsActivity] active messages: ${runtimeProperties.activeMessageCount}`,
137136
);
138137
newStatus.sentMessageCount =
139138
newStatus.totalMessageCount - runtimeProperties.activeMessageCount;

large-scale-notification/src/functions/waitSendingFinishActivity.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
import * as df from "durable-functions";
1212
import { ActivityHandler } from "durable-functions";
1313

14-
import { DefaultAzureCredential } from "@azure/identity";
1514
import { InvocationContext } from "@azure/functions";
15+
import { ManagedIdentityCredential } from "@azure/identity";
1616
import { ServiceBusAdministrationClient } from "@azure/service-bus";
1717

1818
import {
@@ -24,27 +24,27 @@ import { SendStatus } from "../types/sendStatus";
2424

2525
const waitSendingFinishActivity: ActivityHandler = async (
2626
triggerInput: any,
27-
context: InvocationContext
27+
context: InvocationContext,
2828
): Promise<SendStatus> => {
29-
const credential = new DefaultAzureCredential({
30-
managedIdentityClientId: managedIdentityId,
29+
const credential = new ManagedIdentityCredential({
30+
clientId: managedIdentityId,
3131
});
3232
const input = triggerInput as SendStatus;
3333
const sbAdminClient = new ServiceBusAdministrationClient(
3434
`${serviceBusNamespace}.servicebus.windows.net`,
35-
credential
35+
credential,
3636
);
3737
let runtimeProperties = await sbAdminClient.getQueueRuntimeProperties(
38-
serviceBusMessageQueueName
38+
serviceBusMessageQueueName,
3939
);
4040
context.warn(`[waitSendingFinishActivity] checking.`);
4141
while (runtimeProperties.activeMessageCount > 0) {
4242
context.warn(
43-
`[waitSendingFinishActivity] active messages: ${runtimeProperties.activeMessageCount}`
43+
`[waitSendingFinishActivity] active messages: ${runtimeProperties.activeMessageCount}`,
4444
);
4545
await new Promise((r) => setTimeout(r, 5000));
4646
runtimeProperties = await sbAdminClient.getQueueRuntimeProperties(
47-
serviceBusMessageQueueName
47+
serviceBusMessageQueueName,
4848
);
4949
}
5050
input.failedMessageCount =

large-scale-notification/src/internal/tableStore.ts

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,57 @@
1-
import { ConversationReference } from "@microsoft/agents-activity";
2-
import { DefaultAzureCredential } from "@azure/identity";
31
import { TableClient } from "@azure/data-tables";
2+
import { ManagedIdentityCredential } from "@azure/identity";
3+
import { ConversationReference } from "@microsoft/agents-activity";
44
import { IStorage, PagedData } from "../notification/interface";
55

6+
import { InstallationReference } from "../types/installationReference";
67
import {
78
constructConversationReference,
89
extractKeyDataFromConversationReference,
910
} from "../util";
10-
import { InstallationReference } from "../types/installationReference";
1111

1212
export class TableStore implements IStorage {
1313
private readonly client: TableClient;
1414

1515
constructor(
1616
managedIdentityId: string,
1717
storageAccountURL: string,
18-
storageTableName: string
18+
storageTableName: string,
1919
) {
20-
const credential = new DefaultAzureCredential({
21-
managedIdentityClientId: managedIdentityId,
20+
const credential = new ManagedIdentityCredential({
21+
clientId: managedIdentityId,
2222
});
2323

2424
this.client = new TableClient(
2525
`${storageAccountURL}`,
2626
`${storageTableName}`,
2727
credential,
28-
{ allowInsecureConnection: true }
28+
{ allowInsecureConnection: true },
2929
);
3030
}
3131

32-
public async read(keys: string[]): Promise<{ [key: string]: Partial<ConversationReference> }> {
32+
public async read(
33+
keys: string[],
34+
): Promise<{ [key: string]: Partial<ConversationReference> }> {
3335
const results: { [key: string]: Partial<ConversationReference> } = {};
3436
try {
35-
await Promise.all(keys.map(async (key) => {
36-
const entity: InstallationReference = await this.client.getEntity(this.hash(key), key);
37-
results[key] = constructConversationReference(entity);
38-
}));
37+
await Promise.all(
38+
keys.map(async (key) => {
39+
const entity: InstallationReference = await this.client.getEntity(
40+
this.hash(key),
41+
key,
42+
);
43+
results[key] = constructConversationReference(entity);
44+
}),
45+
);
3946
} catch (e: any) {
4047
// Optionally handle error
4148
}
4249
return results;
4350
}
4451

45-
public async write(changes: { [key: string]: Partial<ConversationReference> }): Promise<void> {
52+
public async write(changes: {
53+
[key: string]: Partial<ConversationReference>;
54+
}): Promise<void> {
4655
/*
4756
* {
4857
* "activityId":"f:4c06e7be-31d2-27d3-2c3f-e2c2ff775e0a",
@@ -79,15 +88,17 @@ export class TableStore implements IStorage {
7988

8089
public async delete(keys: string[]): Promise<void> {
8190
try {
82-
await Promise.all(keys.map(key => this.client.deleteEntity(this.hash(key), key)));
91+
await Promise.all(
92+
keys.map((key) => this.client.deleteEntity(this.hash(key), key)),
93+
);
8394
} catch (e: unknown) {
8495
// Optionally handle error
8596
}
8697
}
8798

8899
public async list(
89100
pageSize?: number,
90-
continuationToken?: string
101+
continuationToken?: string,
91102
): Promise<PagedData<Partial<ConversationReference>>> {
92103
const entities = await this.client
93104
.listEntities()

0 commit comments

Comments
 (0)