diff --git a/CHANGELOG.md b/CHANGELOG.md index acc7a09204..180237031b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixes [#2510](https://github.com/microsoft/BotFramework-WebChat/issues/2510). Host hybrid-react and clear-after-idle samples, by [@corinagum](https://github.com/corinagum) in PR [#2798](https://github.com/microsoft/BotFramework-WebChat/pull/2798) - Fixes [#2772](https://github.com/microsoft/BotFramework-WebChat/issues/2772). Updated Adaptive Cards HostConfig to include container styles, by [@tdurnford](https://github.com/tdurnford) in PR [#28XX](https://github.com/microsoft/BotFramework-WebChat/pull/2810) - Fixes [#2145](https://github.com/microsoft/BotFramework-WebChat/issues/2145). Updated Adaptive Cards styles to include action styles, by [@tdurnford](https://github.com/tdurnford) in PR [#2810](https://github.com/microsoft/BotFramework-WebChat/pull/2810) +- Fixes [#2459](https://github.com/microsoft/BotFramework-WebChat/issues/2459). Updated Cognitive Services Speech Services to use latest fetch credentials signature, by [@compulim](https://github.com/compulim) in PR [#2740](https://github.com/microsoft/BotFramework-WebChat/pull/2759) ### Changed diff --git a/packages/bundle/src/createCognitiveServicesSpeechServicesPonyfillFactory.js b/packages/bundle/src/createCognitiveServicesSpeechServicesPonyfillFactory.js index 504f0d2afc..f2be67ed9e 100644 --- a/packages/bundle/src/createCognitiveServicesSpeechServicesPonyfillFactory.js +++ b/packages/bundle/src/createCognitiveServicesSpeechServicesPonyfillFactory.js @@ -1,9 +1,14 @@ import { AudioConfig } from 'microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Audio/AudioConfig'; import createPonyfill from 'web-speech-cognitive-services/lib/SpeechServices'; +async function resolveFunction(fnOrValue) { + return await (typeof fnOrValue === 'function' ? fnOrValue() : fnOrValue); +} + export default function createCognitiveServicesSpeechServicesPonyfillFactory({ audioConfig, authorizationToken, + credentials, enableTelemetry, region, speechRecognitionEndpointId, @@ -12,6 +17,26 @@ export default function createCognitiveServicesSpeechServicesPonyfillFactory({ subscriptionKey, textNormalization }) { + if (!credentials && (authorizationToken || region || subscriptionKey)) { + console.warn( + 'botframework-webchat: "authorizationToken", "region", and "subscriptionKey" are being deprecated and will be removed on or after 2020-12-17. Please use "credentials" instead.' + ); + + credentials = async () => { + if (authorizationToken) { + return { + authorizationToken: resolveFunction(authorizationToken), + region + }; + } else { + return { + region, + subscriptionKey: resolveFunction(subscriptionKey) + }; + } + }; + } + // HACK: We should prevent AudioContext object from being recreated because they may be blessed and UX-wise expensive to recreate. // In Cognitive Services SDK, if they detect the "end" function is falsy, they will not call "end" but "suspend" instead. // And on next recognition, they will re-use the AudioContext object. @@ -35,14 +60,12 @@ export default function createCognitiveServicesSpeechServicesPonyfillFactory({ return ({ referenceGrammarID }) => { const ponyfill = createPonyfill({ audioConfig, - authorizationToken, + credentials, enableTelemetry, referenceGrammars: [`luis/${referenceGrammarID}-PRODUCTION`], - region, speechRecognitionEndpointId, speechSynthesisDeploymentId, speechSynthesisOutputFormat, - subscriptionKey, textNormalization }); diff --git a/samples/03.speech/b.cognitive-speech-services-js/README.md b/samples/03.speech/b.cognitive-speech-services-js/README.md index 4f518836cf..d7cdc6fe42 100644 --- a/samples/03.speech/b.cognitive-speech-services-js/README.md +++ b/samples/03.speech/b.cognitive-speech-services-js/README.md @@ -60,17 +60,17 @@ Cognitive Services Speech Services has published a new API to provide speech rec + let lastPromise; + + return () => { -+ if (Date.now() > expireAfter) { -+ const speechServicesTokenResPromise = fetch( ++ const now = Date.now(); ++ ++ if (now > expireAfter) { ++ expireAfter = now + 300000; ++ lastPromise = fetch( + 'https://webchat-mockbot.azurewebsites.net/speechservices/token', + { method: 'POST' } -+ ); -+ -+ expireAfter = Date.now() + 300000; -+ lastPromise = speechServicesTokenResPromise.then( ++ ).then( + res => res.json(), + err => { -+ lastPromise = null; ++ expireAfter = 0; + + return Promise.reject(err); + } @@ -82,22 +82,13 @@ Cognitive Services Speech Services has published a new API to provide speech rec + } + + const fetchSpeechServicesCredentials = createFetchSpeechServicesCredentials(); -+ -+ async function fetchSpeechServicesRegion() { -+ return (await fetchSpeechServicesCredentials()).region; -+ } -+ -+ async function fetchSpeechServicesToken() { -+ return (await fetchSpeechServicesCredentials()).token; -+ } (async function () { const res = await fetch('https://webchat-mockbot.azurewebsites.net/directline/token', { method: 'POST' }); const { token } = await res.json(); + const webSpeechPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory({ -+ authorizationToken: fetchSpeechServicesToken, -+ region: await fetchSpeechServicesRegion() ++ credentials: fetchSpeechServicesCredentials + }); window.WebChat.renderWebChat({ diff --git a/samples/03.speech/b.cognitive-speech-services-js/index.html b/samples/03.speech/b.cognitive-speech-services-js/index.html index 10333f2204..59ceb86a8f 100644 --- a/samples/03.speech/b.cognitive-speech-services-js/index.html +++ b/samples/03.speech/b.cognitive-speech-services-js/index.html @@ -35,21 +35,20 @@ let lastPromise; return () => { + const now = Date.now(); + // Fetch a new token if the existing one is expiring. // The following article mentioned the token is only valid for 10 minutes. // We will invalidate the token after 5 minutes. // https://docs.microsoft.com/en-us/azure/cognitive-services/authentication#authenticate-with-an-authentication-token - if (Date.now() > expireAfter) { - const speechServicesTokenResPromise = fetch( - 'https://webchat-mockbot.azurewebsites.net/speechservices/token', - { method: 'POST' } - ); - - expireAfter = Date.now() + 300000; - lastPromise = speechServicesTokenResPromise.then( + if (now > expireAfter) { + expireAfter = now + 300000; + lastPromise = fetch('https://webchat-mockbot.azurewebsites.net/speechservices/token', { + method: 'POST' + }).then( res => res.json(), err => { - lastPromise = null; + expireAfter = 0; return Promise.reject(err); } @@ -62,14 +61,6 @@ const fetchSpeechServicesCredentials = createFetchSpeechServicesCredentials(); - async function fetchSpeechServicesRegion() { - return (await fetchSpeechServicesCredentials()).region; - } - - async function fetchSpeechServicesToken() { - return (await fetchSpeechServicesCredentials()).token; - } - (async function() { // In this demo, we are using Direct Line token from MockBot. // Your client code must provide either a secret or a token to talk to your bot. @@ -81,12 +72,9 @@ // Create the ponyfill factory function, which can be called to create a concrete implementation of the ponyfill. const webSpeechPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory({ - // We are passing the Promise function to the authorizationToken field. + // We are passing the Promise function to the "credentials" field. // This function will be called every time the token is being used. - authorizationToken: fetchSpeechServicesToken, - - // In contrast, we are only fetching the region once. - region: await fetchSpeechServicesRegion() + credentials: fetchSpeechServicesCredentials }); // Pass a Web Speech ponyfill factory to renderWebChat. diff --git a/samples/03.speech/c.cognitive-speech-services-with-lexical-result/index.html b/samples/03.speech/c.cognitive-speech-services-with-lexical-result/index.html index 51a1cebca1..8224227218 100644 --- a/samples/03.speech/c.cognitive-speech-services-with-lexical-result/index.html +++ b/samples/03.speech/c.cognitive-speech-services-with-lexical-result/index.html @@ -35,21 +35,20 @@ let lastPromise; return () => { + const now = Date.now(); + // Fetch a new token if the existing one is expiring. // The following article mentioned the token is only valid for 10 minutes. // We will invalidate the token after 5 minutes. // https://docs.microsoft.com/en-us/azure/cognitive-services/authentication#authenticate-with-an-authentication-token - if (Date.now() > expireAfter) { - const speechServicesTokenResPromise = fetch( - 'https://webchat-mockbot.azurewebsites.net/speechservices/token', - { method: 'POST' } - ); - - expireAfter = Date.now() + 300000; - lastPromise = speechServicesTokenResPromise.then( + if (now > expireAfter) { + expireAfter = now + 300000; + lastPromise = fetch('https://webchat-mockbot.azurewebsites.net/speechservices/token', { + method: 'POST' + }).then( res => res.json(), err => { - lastPromise = null; + expireAfter = 0; return Promise.reject(err); } @@ -62,14 +61,6 @@ const fetchSpeechServicesCredentials = createFetchSpeechServicesCredentials(); - async function fetchSpeechServicesRegion() { - return (await fetchSpeechServicesCredentials()).region; - } - - async function fetchSpeechServicesToken() { - return (await fetchSpeechServicesCredentials()).token; - } - (async function() { // In this demo, we are using Direct Line token from MockBot. // Your client code must provide either a secret or a token to talk to your bot. @@ -81,12 +72,9 @@ // Create the ponyfill factory function, which can be called to create a concrete implementation of the ponyfill. const webSpeechPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory({ - // We are passing the Promise function to the authorizationToken field. + // We are passing the Promise function to the "credentials" field. // This function will be called every time the token is being used. - authorizationToken: fetchSpeechServicesToken, - - // In contrast, we are only fetching the region once. - region: await fetchSpeechServicesRegion(), + credentials: fetchSpeechServicesCredentials, textNormalization: 'lexical' }); diff --git a/samples/03.speech/d.cognitive-speech-services-speech-recognition-only/README.md b/samples/03.speech/d.cognitive-speech-services-speech-recognition-only/README.md index cd7a6aef4c..04d91428e1 100644 --- a/samples/03.speech/d.cognitive-speech-services-speech-recognition-only/README.md +++ b/samples/03.speech/d.cognitive-speech-services-speech-recognition-only/README.md @@ -112,17 +112,17 @@ Here is the finished `index.html`: let lastPromise; return () => { - if (Date.now() > expireAfter) { - const speechServicesTokenResPromise = fetch( + const now = Date.now(); + + if (now > expireAfter) { + expireAfter = now + 300000; + lastPromise = fetch( 'https://webchat-mockbot.azurewebsites.net/speechservices/token', { method: 'POST' } - ); - - expireAfter = Date.now() + 300000; - lastPromise = speechServicesTokenResPromise.then( + ).then( res => res.json(), err => { - lastPromise = null; + expireAfter = 0; return Promise.reject(err); } @@ -135,14 +135,6 @@ Here is the finished `index.html`: const fetchSpeechServicesCredentials = createFetchSpeechServicesCredentials(); - async function fetchSpeechServicesRegion() { - return (await fetchSpeechServicesCredentials()).region; - } - - async function fetchSpeechServicesToken() { - return (await fetchSpeechServicesCredentials()).token; - } - (async function() { const directLineTokenRes = await fetch('https://webchat-mockbot.azurewebsites.net/directline/token', { method: 'POST' @@ -151,15 +143,13 @@ Here is the finished `index.html`: const { token } = await directLineTokenRes.json(); - const webSpeechPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory({ -- authorizationToken: fetchSpeechServicesToken, -- region: await fetchSpeechServicesRegion() +- credentials: fetchSpeechServicesCredentials - }); + async function createSpeechRecognitionOnlyPonyfillFactory() { + const speechServicesPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory( + { -+ authorizationToken: fetchSpeechServicesToken, -+ region: await fetchSpeechServicesRegion() ++ credentials: fetchSpeechServicesCredentials + } + ); + diff --git a/samples/03.speech/d.cognitive-speech-services-speech-recognition-only/index.html b/samples/03.speech/d.cognitive-speech-services-speech-recognition-only/index.html index 4ac4905015..c6d6fbbf0b 100644 --- a/samples/03.speech/d.cognitive-speech-services-speech-recognition-only/index.html +++ b/samples/03.speech/d.cognitive-speech-services-speech-recognition-only/index.html @@ -35,21 +35,20 @@ let lastPromise; return () => { + const now = Date.now(); + // Fetch a new token if the existing one is expiring. // The following article mentioned the token is only valid for 10 minutes. // We will invalidate the token after 5 minutes. // https://docs.microsoft.com/en-us/azure/cognitive-services/authentication#authenticate-with-an-authentication-token - if (Date.now() > expireAfter) { - const speechServicesTokenResPromise = fetch( - 'https://webchat-mockbot.azurewebsites.net/speechservices/token', - { method: 'POST' } - ); - - expireAfter = Date.now() + 300000; - lastPromise = speechServicesTokenResPromise.then( + if (now > expireAfter) { + expireAfter = now + 300000; + lastPromise = fetch('https://webchat-mockbot.azurewebsites.net/speechservices/token', { + method: 'POST' + }).then( res => res.json(), err => { - lastPromise = null; + expireAfter = 0; return Promise.reject(err); } @@ -62,14 +61,6 @@ const fetchSpeechServicesCredentials = createFetchSpeechServicesCredentials(); - async function fetchSpeechServicesRegion() { - return (await fetchSpeechServicesCredentials()).region; - } - - async function fetchSpeechServicesToken() { - return (await fetchSpeechServicesCredentials()).token; - } - (async function() { // In this demo, we are using Direct Line token from MockBot. // Your client code must provide either a secret or a token to talk to your bot. @@ -87,12 +78,9 @@ // Create the ponyfill factory function, which can be called to create a concrete implementation of the ponyfill. const speechServicesPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory( { - // We are passing the Promise function to the authorizationToken field. + // We are passing the Promise function to the "credentials" field. // This function will be called every time the token is used. - authorizationToken: fetchSpeechServicesToken, - - // In contrast, we are only fetching the region once. - region: await fetchSpeechServicesRegion() + credentials: fetchSpeechServicesCredentials } ); diff --git a/samples/03.speech/e.select-voice/index.html b/samples/03.speech/e.select-voice/index.html index 7a43870158..d1099ca1ef 100644 --- a/samples/03.speech/e.select-voice/index.html +++ b/samples/03.speech/e.select-voice/index.html @@ -35,21 +35,20 @@ let lastPromise; return () => { + const now = Date.now(); + // Fetch a new token if the existing one is expiring. // The following article mentioned the token is only valid for 10 minutes. // We will invalidate the token after 5 minutes. // https://docs.microsoft.com/en-us/azure/cognitive-services/authentication#authenticate-with-an-authentication-token - if (Date.now() > expireAfter) { - const speechServicesTokenResPromise = fetch( - 'https://webchat-mockbot.azurewebsites.net/speechservices/token', - { method: 'POST' } - ); - - expireAfter = Date.now() + 300000; - lastPromise = speechServicesTokenResPromise.then( + if (now > expireAfter) { + expireAfter = now + 300000; + lastPromise = fetch('https://webchat-mockbot.azurewebsites.net/speechservices/token', { + method: 'POST' + }).then( res => res.json(), err => { - lastPromise = null; + expireAfter = 0; return Promise.reject(err); } @@ -62,14 +61,6 @@ const fetchSpeechServicesCredentials = createFetchSpeechServicesCredentials(); - async function fetchSpeechServicesRegion() { - return (await fetchSpeechServicesCredentials()).region; - } - - async function fetchSpeechServicesToken() { - return (await fetchSpeechServicesCredentials()).token; - } - (async function() { // In this demo, we are using Direct Line token from MockBot. // Your client code must provide either a secret or a token to talk to your bot. @@ -83,12 +74,9 @@ // Create the ponyfill factory function, which can be called to create a concrete implementation of the ponyfill. const webSpeechPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory({ - // We are passing the Promise function to the authorizationToken field. + // We are passing the Promise function to the "credentials" field. // This function will be called every time the token is being used. - authorizationToken: fetchSpeechServicesToken, - - // In contrast, we are only fetching the region once. - region: await fetchSpeechServicesRegion() + credentials: fetchSpeechServicesCredentials }); window.WebChat.renderWebChat( diff --git a/samples/03.speech/g.hybrid-speech/README.md b/samples/03.speech/g.hybrid-speech/README.md index 2e27bd47be..d6ea99745a 100644 --- a/samples/03.speech/g.hybrid-speech/README.md +++ b/samples/03.speech/g.hybrid-speech/README.md @@ -113,17 +113,17 @@ Here is the finished `index.html`: let lastPromise; return () => { - if (Date.now() > expireAfter) { - const speechServicesTokenResPromise = fetch( + const now = Date.now(); + + if (now > expireAfter) { + expireAfter = now + 300000; + lastPromise = fetch( 'https://webchat-mockbot.azurewebsites.net/speechservices/token', { method: 'POST' } - ); - - expireAfter = Date.now() + 300000; - lastPromise = speechServicesTokenResPromise.then( + ).then( res => res.json(), err => { - lastPromise = null; + expireAfter = 0; return Promise.reject(err); } @@ -136,14 +136,6 @@ Here is the finished `index.html`: const fetchSpeechServicesCredentials = createFetchSpeechServicesCredentials(); - async function fetchSpeechServicesRegion() { - return (await fetchSpeechServicesCredentials()).region; - } - - async function fetchSpeechServicesToken() { - return (await fetchSpeechServicesCredentials()).token; - } - (async function () { const directLineTokenRes = await fetch('https://webchat-mockbot.azurewebsites.net/directline/token', { method: 'POST' }); const { token } = await directLineTokenRes.json(); @@ -152,14 +144,12 @@ Here is the finished `index.html`: const { region, token: authorizationToken } = await speechServicesTokenRes.json(); - const webSpeechPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory({ -- authorizationToken: fetchSpeechServicesToken, -- region: await fetchSpeechServicesRegion() +- credentials: fetchSpeechServicesCredentials - }); + async function createHybridPonyfillFactory({ authorizationToken, region }) { + const speechServicesPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory({ -+ authorizationToken: fetchSpeechServicesToken, -+ region: await fetchSpeechServicesRegion() ++ credentials: fetchSpeechServicesCredentials + }); + + const webSpeechPonyfillFactory = await window.WebChat.createBrowserWebSpeechPonyfillFactory(); diff --git a/samples/03.speech/g.hybrid-speech/index.html b/samples/03.speech/g.hybrid-speech/index.html index 11cb67ec8c..9eec60f88e 100644 --- a/samples/03.speech/g.hybrid-speech/index.html +++ b/samples/03.speech/g.hybrid-speech/index.html @@ -35,21 +35,20 @@ let lastPromise; return () => { + const now = Date.now(); + // Fetch a new token if the existing one is expiring. // The following article mentioned the token is only valid for 10 minutes. // We will invalidate the token after 5 minutes. // https://docs.microsoft.com/en-us/azure/cognitive-services/authentication#authenticate-with-an-authentication-token - if (Date.now() > expireAfter) { - const speechServicesTokenResPromise = fetch( - 'https://webchat-mockbot.azurewebsites.net/speechservices/token', - { method: 'POST' } - ); - - expireAfter = Date.now() + 300000; - lastPromise = speechServicesTokenResPromise.then( + if (now > expireAfter) { + expireAfter = now + 300000; + lastPromise = fetch('https://webchat-mockbot.azurewebsites.net/speechservices/token', { + method: 'POST' + }).then( res => res.json(), err => { - lastPromise = null; + expireAfter = 0; return Promise.reject(err); } @@ -62,14 +61,6 @@ const fetchSpeechServicesCredentials = createFetchSpeechServicesCredentials(); - async function fetchSpeechServicesRegion() { - return (await fetchSpeechServicesCredentials()).region; - } - - async function fetchSpeechServicesToken() { - return (await fetchSpeechServicesCredentials()).token; - } - (async function() { // In this demo, we are using Direct Line token from MockBot. // Your client code must provide either a secret or a token to talk to your bot. @@ -81,23 +72,14 @@ }); const { token } = await directLineTokenRes.json(); - // For this demo, we are using our authorization token. For your production code, you should use a token from your Cognitive Services subscription. - const speechServicesTokenRes = await fetch('https://webchat-mockbot.azurewebsites.net/speechservices/token', { - method: 'POST' - }); - const { region, token: authorizationToken } = await speechServicesTokenRes.json(); - // We are creating a hybrid ponyfill factory that will merge the result of two ponyfill factories together. - async function createHybridPonyfillFactory({ authorizationToken, region }) { + async function createHybridPonyfillFactory({ credentials }) { // Create the ponyfill factory function, which can be called to create a concrete implementation of the ponyfill. const speechServicesPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory( { - // We are passing the Promise function to the authorizationToken field. + // We are passing the Promise function to the "credentials" field. // This function will be called every time the token is being used. - authorizationToken: fetchSpeechServicesToken, - - // In contrast, we are only fetching the region once. - region: await fetchSpeechServicesRegion() + credentials } ); @@ -126,7 +108,7 @@ window.WebChat.renderWebChat( { directLine: window.WebChat.createDirectLine({ token }), - webSpeechPonyfillFactory: await createHybridPonyfillFactory({ authorizationToken, region }) + webSpeechPonyfillFactory: await createHybridPonyfillFactory({ credentials: fetchSpeechServicesCredentials }) }, document.getElementById('webchat') );