Speech Service Authentication With 'Credentials' Via Webchat on Bot Framework V4 - botframework

My bot is working fine while calling Speech Services using an authorization token + region pair.
Code snipped below.
webSpeechPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory({ authorizationToken, region });
However, the following warning message shows up on the browser:
botframework-webchat: "authorizationToken", "region", and "subscriptionKey" are deprecated and will be removed on or after 2020-12-17. Please use "credentials" instead.
How can migrate my authentication code to the new method?
Code samples are appreciated. Thx

This is the code I use. I run a server locally for calling the speech API that returns the token (and region).
In the Web Chat code, you simply need to pass the returned token and region in to createCognitiveServicesSpeechServicesPonyfillFactory(). Your code doesn't need to match mine, specifically. It is enough to do something like:
const webSpeechPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory( {
credentials: {
authorizationToken: authorizationToken,
region: region
}
} );
or, depending how you structure your credentials object,
{ credentials: credentials }
Sample code:
const path = require('path');
const restify = require('restify');
const request = require('request');
const bodyParser = require('body-parser');
const corsMiddleware = require('restify-cors-middleware');
const cors = corsMiddleware({
origins: ['*']
});
const ENV_FILE = path.join(__dirname, '.env');
require('dotenv').config({ path: ENV_FILE });
// Create HTTP server.
const server = restify.createServer();
server.pre(cors.preflight);
server.use(cors.actual);
server.use(bodyParser.json({
extended: false
}));
server.listen(process.env.port || process.env.PORT || 3500, function() {
console.log(`\n${ server.dl_name } listening to ${ server.url }.`);
});
server.post('/speechservices/token', async (req, res) => {
const options = {
method: 'POST',
uri: `https://${ process.env.SPEECH_SERVICES_REGION }.api.cognitive.microsoft.com/sts/v1.0/issueToken`,
headers: {
'Ocp-Apim-Subscription-Key': process.env.SPEECH_SERVICES_SUBSCRIPTION_KEY
}
};
request.post(options, (error, response, body) => {
if (!error && response.statusCode < 300) {
body = { region: process.env.SPEECH_SERVICES_REGION, authorizationToken: body };
res.send({
authorizationToken: body.authorizationToken,
region: body.region
});
console.log(`Someone requested a speech token...(${ response.statusCode })`);
} else if (response.statusCode >= 400 && response.statusCode < 500) {
res.send(response.statusCode);
} else if (response.statusCode >= 500) {
res.status(response.statusCode);
res.send('Call to retrieve token failed');
}
});
});
const getSpeechToken = async (credentials = {}) => {
const response = await fetch( `http://localhost:3500/speechservices/token`, {
method: 'POST',
} );
if ( response.status === 200 ) {
const { authorizationToken, region } = await response.json();
credentials['authorizationToken'] = authorizationToken;
credentials['region'] = region;
return credentials;
} else {
console.log('error')
}
}
const webSpeechPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory( {
credentials: await getSpeechToken()
} );
render(
<div>
<ReactWebChat
directLine={directLine}
selectVoice={( voices, activity ) =>
activity.locale === 'en-US' ?
voices.find( ( { name } ) => /KatjaNeural/iu.test( name ) )
:
voices.find( ( { name } ) => /KatjaNeural/iu.test( name ) )
|| voices.find( ( { name } ) => /Apollo/iu.test( name ) )}
webSpeechPonyfillFactory={webSpeechPonyfillFactory}
/>
</div>,
document.getElementById( 'webchat' )
);
Hope of help!

Related

Not getting Apollo Client subscription data

I'm trying to get subscription data. The server gives the data if you look through the Explorer.
Client:
const httpLink = createHttpLink({
uri
});
const wsLink =
typeof window !== "undefined"
? new GraphQLWsLink(
createClient({
url,
on: {
connected: () => console.log("Connected client!"),
closed: () => console.log("Closed ws-connection!"),
},
})
)
: null;
const splitLink =
typeof window !== "undefined" && wsLink != null
? split(
({ query }) => {
const def = getMainDefinition(query);
return (
def.kind === "OperationDefinition" &&
def.operation === "subscription"
);
},
wsLink,
httpLink
)
: httpLink;
const authLink = setContext((_, { headers }) => {
const {token} = useTokenFromCookie();
return {
headers: {
...headers,
authorization: token ? `Bearer ${token()}` : "",
}
}
});
const client = new ApolloClient({
link: authLink.concat(splitLink),
cache: new InMemoryCache({addTypename: false}),
defaultOptions: {
mutate: { errorPolicy: 'all' },
},
});
gql:
subscription ReadRegisterData($equipmentId: Int!, $addressRegistry: Int!) {
readRegisterData(equipment_id: $equipmentId, address_registry: $addressRegistry) {
equipment_id
address_registry
type_of
data_from_controller
}
}
Hook:
const useSubscriptionReadRegisterData = (equipment_ip:number, address_registry: number) => {
const { data, error, loading} = useSubscription(READ_REGISTER_DATA, {
variables: {
equipmentIp: equipment_ip,
addressRegistry: address_registry
}
});
console.log("data", data)
const dataRegisterSubscription = (data) ? data.readRegisterData : null;
return { dataRegisterSubscription, error, loading }
}
export default useSubscriptionReadRegisterData;
In the console writes:
The connection is established, then immediately the connection is closed
WS connection status 101
When you start listening to a subscription through Explorer, on the server, when outputting data to the log, you can see that there is 1 Listener. When you run in the application, it does not show any Listener

SocketIO 4 - won't emit to the room

I have following server code:
const path = require("path");
const http = require("http");
const express = require("express");
const {instrument} = require('#socket.io/admin-ui')
const {jwtDecode, jwtVerify, resignJwt} = require('jwt-js-decode')
const secret =
"xxxxxxx";
const app = express()
const server = http.createServer(app)
const io = require("socket.io")(server, {
cors: {
origin: ["https://admin.socket.io", "http://localhost:3001"],
credentials: true
},
});
let servantID = ''
io.use((socket, next) => {
const header = socket.handshake.headers["authorization"];
jwtVerify(header, secret).then((res) => {
if (res === true)
{
const jwt = jwtDecode(header);
servantID = jwt.payload.iss;
return next()
}
return next(new Error("authentication error"));
});
});
instrument(io, { auth: false });
server.listen(3000, () =>
console.log('connected')
)
io.on('connection', socket => {
socket.on("join", (room, cb) => {
console.log('Joined ' + room);
socket.join(room);
cb(`Joined the best room ${room}`)
});
socket.on('newOrder', function (data) {
socket.to('servant').emit('this', data);
console.log(data);
})
socket.on("thisNew", function (data) {
console.log('this new');
});
socket.on('disconnect', () => {
console.log('user disconnected');
});
})
And client side code:
socket.emit('join', 'servant', message => {
console.log(message)
})
socket.on('this', () => {
console.log('this event')
})
socket.emit('newOrder', 'data')
When I emit like this:
socket.to('servant').emit('this', data);
the client doesn't receive anything, but if I emit without room:
socket.emit('this', data);
the event and data are received on the client side.
What am I doing wrong here?

How to get error from backend with axios?

I'm trying to display an error I recieve in my backend to the user in my JSX frontend file.
This is the initial call from frontend
dispatch(createGoal({ values }))
Goalslice, directly called from JSX:
export const createGoal = createAsyncThunk(
'goals/create',
async (goalData, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token
return await goalService.createGoal(goalData, token)
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString()
return thunkAPI.rejectWithValue(message)
}
}
)
Goalservice, directly called from goalslice:
const createGoal = async (goalData, token) => {
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
}
const response = await axios.post(API_URL, goalData, config)
return response.data
}
Goalcontroller, my backend:
const setGoal = asyncHandler(async (req, res) => {
const goals = await Goal.find({ user: req.user.id })
var count = Object.keys(goals).length
if(count >2){
res.status(400)
throw new Error('Maximum of 3 trackers per user')
}
if (!req.body.values) { //if this isnt there. check if the body is there.
res.status(400) //This is an error
throw new Error('Please add a date field') //this is express error handler
}
console.log(req.body.values.dates)
const goal = await Goal.create({
values: req.body.values.dates, //get from request body
permit: req.body.values.permits,
numpermit: req.body.values.num,
user: req.user.id,
})
res.status(200).json(goal)
})
I want to display this error:
throw new Error('Maximum of 3 trackers per user')
I tried a try/catch method, but I'm very new to this and I feel like i'm missing a very key point in how it all fits together.
This is my custom error handler if it helps:
const errorHandler = (err, req, res, next) => { //overwrite express error handler, next to handle any new req
const statusCode = res.statusCode ? res.statusCode : 500 //500 is server error. conditional
res.status(statusCode)
res.json({
message: err.message,
stack: process.env.NODE_ENV === 'production' ? null : err.stack, //gives additional info if in development mode : is else
})
}
module.exports = { //export for others to use
errorHandler,
}

How do I get the message using Office.js in a react add-in

I am building a new react Outlook add-in and need to be able to download the current email.
The Office.js API has the getFileAsync method off the Office.context.document object but not the Office.context.mailbox.item object.
also as a requirement this needs to work in both Office online and local installs of Outlook.
In the existing com add-in I had direct access to the mail item.
Here is the code that I currently have to call into the API, but this only retrieves metadata.
/*
https://learn.microsoft.com/en-us/outlook/add-ins/use-rest-api#get-the-item-id
*/
public getMessageViaRest = () => {
const context: Office.AsyncContextOptions & { isRest: boolean } = {
isRest: true
};
Office.context.mailbox.getCallbackTokenAsync(context, (tokenResults) => {
if (tokenResults.status === Office.AsyncResultStatus.Failed) {
this.setState({ error: 'Failed to get rest api auth token' });
return;
}
const apiId: string = Office.context.mailbox.convertToRestId(Office.context.mailbox.item.itemId, 'v2.0');
const apiUrl = Office.context.mailbox.restUrl + '/v2.0/me/messages/' + apiId;
try {
fetch(apiUrl, {
method: 'GET',
headers: new Headers({
Authorization: 'Bearer ' + tokenResults.value
})
}).then((response) => {
response.json().then((body) => {
for (const key in body) {
this.state.details.push({ name: key, value: JSON.stringify(body[key]) });
}
this.forceUpdate();
});
});
} catch (error) {
this.setState({ error: JSON.stringify(error) });
}
});
}
Its not perfect but the REST Api does have an end point that will return the file's EML contents.
public downloadViaRest = () => {
const context: Office.AsyncContextOptions & { isRest: boolean } = {
isRest: true
};
Office.context.mailbox.getCallbackTokenAsync(context, (tokenResults) => {
if (tokenResults.status === Office.AsyncResultStatus.Failed) {
this.setState({ error: 'Failed to get rest api auth token' });
return;
}
const apiId: string = Office.context.mailbox.convertToRestId(Office.context.mailbox.item.itemId, 'v2.0');
const apiUrl = Office.context.mailbox.restUrl + '/v2.0/me/messages/' + apiId + '/$value';
try {
fetch(apiUrl, {
method: 'GET',
headers: new Headers({
Authorization: 'Bearer ' + tokenResults.value
})
}).then((response) => {
response.blob().then((blob) => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'Message.eml';
a.click();
});
});
} catch (error) {
}
});
}

window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory(...).then is not a function

I'm using webchat-es5.js and its throwing error like window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory(...).then is not a function. Need IE support also using es5.js.
window.fetch('https://webchat-mockbot.azurewebsites.net/directline/token', { method: 'POST' })
.then(function (res) {
return res.json();
})
.then(function (json) {
const token = json.token;
window.fetch('https://webchat-mockbot.azurewebsites.net/speechservices/token', { method: 'POST' })
.then(function (res) {
return res.json();
})
.then(function (json) {
const region = json.region;
const authorizationToken = json.token;
window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory({ authorizationToken: authorizationToken , region: region })
.then(function (webSpeechPonyfillFactory) {
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token: token }),
webSpeechPonyfillFactory: webSpeechPonyfillFactory
}, document.getElementById('webchat'));
document.querySelector('#webchat > *').focus();
});
});
});
Thanks in advance.
This is the setup that is working for me. I run a server locally to fetch the actual tokens. This way I don't need to include any secrets in the client.
window.fetch( 'http://localhost:3500/directline/token', { method: 'POST' } )
.then( function ( res ) {
return res.json();
} )
.then( function ( res ) {
let token = res.token;
window.fetch( 'http://localhost:3500/speechservices/token', { method: 'POST' } )
.then( function ( res ) {
return res.json();
} )
.then( function ( res ) {
let authorizationToken = res.authorizationToken;
window.WebChat.renderWebChat( {
directLine: window.WebChat.createDirectLine( {
token: token,
webSocket: true
} ),
webSpeechPonyfillFactory: window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory( {
region: 'westus2',
authorizationToken: authorizationToken
} )
}, document.getElementById( 'webchat' ) );
});
} );
Hope of help!

Resources