I am having Websocket API Gateway and I trying to send message to connections using the ApiGatewayManagementApi.
First I configure it:
const apigwManagementApi = new AWS.ApiGatewayManagementApi({
apiVersion: "2018-11-29",
endpoint: connectionsUrl
});
And this is the code for postToConnection()
try {
const params = {
ConnectionId: connection.connectionId,
Data: JSON.stringify(data.payload)
};
console.log(params, connectionsUrl);
//THIS PRINTS: { ConnectionId: 'ckd609nog0009sgp847cj3lgy', Data: '{}' }
https://xxxxxxxx.execute-api.xxxxxx.amazonaws.com/socket-test
await apigwManagementApi.postToConnection(params).promise();
} catch (e) {
console.error('APIGW Post To Connection ERROR:', e);
if (e.statusCode == 410) {
let res = await this.DisconnectConnection(connection.connectionId);
if (res.isError()) {
return ServiceResponse.Error(res.error)
}
return ServiceResponse.Error(res.error)
} else {
return ServiceResponse.Error({message: e.message});
}
}
The ERROR I am getting in the catch where I am logging it is:
**APIGW Post To Connection ERROR: BadRequestException: Invalid connectionId: ckd609nog0009sgp847cj3lgy
**
This is not stale connection. This use case is where I am connected to the WebSocket and I send message to socket, it does something and returns results vie postToConnection Method.
What is the problem ?
Related
I'm integration amazon connect platform to maintain a two-way flow communication in our own chatbot where customer will be the user in our platform and agents will be there on amazon connect platform to communicate. I'm using websockets for the communication now but it is giving me Forbidden error. Details are mentioned below
Initially, I have used aws-sdk and #aws-sdk/client-connectparticipant library to make the connection with aws and then multiple SDKs in order are used further to send the messages.
startChatContact -> Used AWS library to make the connection with AWS and then using it to retrieve participation token
createParticipantConnection -> Using participation token to retrieve connection token from this sdk using Type: [ 'CONNECTION_CREDENTIALS' ]
sendEvent -> Using connection token and ContentType: 'application/vnd.amazonaws.connect.event.connection.acknowledged' to send the event
sendMessage -> After sending the event, sending the message with connection token and ContentType: 'text/plain'
import * as AWS from 'aws-sdk';
import * as AWSConnectParticipant from "#aws-sdk/client-connectparticipant";
private messageText = "";
private connectionToken = "";
private connectParticipant = new AWSConnectParticipant.ConnectParticipant({
credentials: {
accessKeyId: '...',
secretAccessKey: '...'
},
region: '...'
});
// It will get called when user sends a message on the chat window
public sendMessage(text: string): void {
this.messageText = text || "";
if (this.connectionToken) {
this.sendEventOnAWSConnect();
} else {
this.startChatContact();
}
}
startChatContact() {
const connect = new AWS.Connect({
accessKeyId: '...',
secretAccessKey: '...',
region: '...'
});
const params = {
ContactFlowId: '...',
InstanceId: '...',
ParticipantDetails: {
DisplayName: 'Customer'
}
};
connect.startChatContact(params, (err: any, data: any) => {
if (data) {
this.createParticipantConnection(data);
}
});
}
createParticipantConnection(startChatContactRes: any) {
const params = {
ParticipantToken: startChatContactRes.ParticipantToken,
Type: [ 'CONNECTION_CREDENTIALS' ]
};
this.connectParticipant.createParticipantConnection(params, (err: any, data: any) => {
if (data) {
this.connectionToken = data.ConnectionCredentials.ConnectionToken;
this.sendEventOnAWSConnect();
this.checkAgentMessage(data.Websocket.Url);
}
});
}
sendEventOnAWSConnect() {
const params = {
ConnectionToken: this.connectionToken,
ContentType: 'application/vnd.amazonaws.connect.event.connection.acknowledged'
};
this.connectParticipant.sendEvent(params, (err: any, data: any) => {
if (data) {
this.sendMessageOnAWSConnect();
}
});
}
sendMessageOnAWSConnect() {
const params = {
ConnectionToken: this.connectionToken,
Content: this.messageText,
ContentType: 'text/plain'
};
this.connectParticipant.sendMessage(params, (err: any, data: any) => {
if (data) {
console.log("Agent connected");
}
});
}
It is working fine as expected. I'm able to send messages on amazon connection with the following code. But I'm facing some issues on receiving agent messages back. I have search for any events which I can trigger on my end or any webhook, but unable to find anything on the same.
Issue on 1st method: Not a good approach. Looking for a better solution
So, I have used polling technique initally where I have used getTranscript SDK from #aws-sdk/client-connectparticipant and calling the api on every 2 seconds to check for any new agent messages but I'm looking for a better method now on the same.
Issue on 2nd method: getting connect.core.getWebSocketManager() as undefined
After exploring, I have also found that there is an onMessage event, which I can trigger using amazon-connect-streams and amazon-connect-chatjs library after creating agent session but
connect.core.getWebSocketManager() as undefined. Also, code after connect.contact is not getting executed, so I have commented that also. I have also created customer session similarly but there also **onMessage **event is not getting triggered. I'm calling its method i.e. checkAgentMessage after I get response from createParticipantConnection method successfully since I'm using contact id, participant id and participant token in checkAgentMessage method, which I'm getting from createParticipantConnection method. Below is the code.
import "amazon-connect-streams";
import "amazon-connect-chatjs";
createParticipantConnection(startChatContactRes: any) {
const params = {
ParticipantToken: startChatContactRes.ParticipantToken,
Type: [ 'CONNECTION_CREDENTIALS' ]
};
this.connectParticipant.createParticipantConnection(params, (err: any, data: any) => {
if (data) {
this.connectionToken = data.ConnectionCredentials.ConnectionToken;
this.sendEventOnAWSConnect();
this.checkAgentMessage(data);
}
});
}
checkAgentMessage(startChatContactRes: any): void {
// for type customer
// const customerChatSession = connect.ChatSession.create({
// chatDetails: {
// contactId: startChatContactRes.ContactId,
// participantId: startChatContactRes.ParticipantId,
// participantToken: startChatContactRes.ParticipantToken,
// },
// type: connect.ChatSession.SessionTypes.CUSTOMER
// });
// for type agent
// connect.contact(contact => {
// if (contact.getType() !== connect.ContactType.CHAT) {
// // applies only to CHAT contacts
// return;
// }
// alternative: if you want control over the args of `connect.ChatSession.setGlobalConfig()` and `connect.ChatSession.create()`
// contact.onAccepted(() => {
const agentChatSession = connect.ChatSession.create({
chatDetails: {
contactId: startChatContactRes.ContactId,
participantId: startChatContactRes.ParticipantId,
participantToken: startChatContactRes.ParticipantToken,
},
options: { // REQUIRED
region: "...", // REQUIRED, must match the value provided to `connect.core.initCCP()`
},
type: connect.ChatSession.SessionTypes.AGENT, // REQUIRED
websocketManager: connect.core.getWebSocketManager() // REQUIRED
})
agentChatSession.onMessage(event => {
console.log("event", event);
});
// });
// });
}
I have checked if I can set connect.core.getWebSocketManager() from somewhere, but got nothing help on the same.
Issue on 3rd method: getting Forbidden as error or message
I have also come across another solution and that is from web sockets. So, I'm implementing the same but there I'm getting error as Forbidden
I have changed my createParticipantConnection function with something as below:
createParticipantConnection(startChatContactRes: any) {
const params = {
ParticipantToken: startChatContactRes.ParticipantToken,
Type: [ 'WEBSOCKET' ]
};
this.connectParticipant.createParticipantConnection(params, (err: any, data: any) => {
if (data) {
let socket = new WebSocket(data.Websocket.Url);
socket.onopen = function(e) {
console.log("[open] Connection established");
console.log("Sending to server");
socket.send("My name is John");
};
socket.onmessage = function(event) {
console.log("event", event);
console.log(`[message] Data received from server: ${event.data}`);
};
socket.onclose = function(event) {
if (event.wasClean) {
console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
} else {
// e.g. server process killed or network down
// event.code is usually 1006 in this case
console.log('[close] Connection died');
}
};
socket.onerror = function(error) {
console.log(`[error]`);
};
// this.connectionToken = data.ConnectionCredentials.ConnectionToken;
// this.sendEventOnAWSConnect();
// this.checkAgentMessage(data);
}
});
}
Changed Type from CONNECTION_CREDENTIALS to WEBSOCKET to retrieve the websocket url. Getting output on the same as:
[open] Connection established
Sending to server
event MessageEventĀ {...}
[message] Data received from server: {"message": "Forbidden", "connectionId":"...", "requestId":"..."}
It is throwing Forbidden as error or message. Please let me know if there is anything I have left which needs to be also implemented or I have done anything wrong here. Also please let me know, if anybody have the solution for the issue on 2nd method or if there is any other method to retrieve agent messages as well.
After calling CreateParticipantConnection, you need to send a subscribe message. You'll then start receiving messages & events on the websocket.
From https://docs.aws.amazon.com/connect-participant/latest/APIReference/API_CreateParticipantConnection.html :
For chat, you need to publish the following on the established websocket connection:
{"topic":"aws/subscribe","content":{"topics":["aws/chat"]}}
I am trying to use Firebase Functions in my koltin Android app to send a message from a client device to another.
Firebase Function within index.js
exports.callUser = functions.https.onCall((data) => {
console.log('Call request received.');
// Message text passed from the client.
const registrationToken = data.registrationToken;
functions.logger.log('Calling', registrationToken);
var message = {
data: {
doctor: 'foo',
patient: 'bar',
room: 'foobar'
},
token: registrationToken
};
return admin.messaging().send(message)
.then((response) => {
console.log('Successfully sent incoming call message:', response);
return "Sent";
})
.catch((error) => {
console.log('Error sending message:', error);
throw new functions.https.HttpsError('unknown', error.message, error);
});
});
Firebase function client call
private fun makeCall(registrationToken: String): Task<String> {
// Create the arguments to the callable function.
val data = hashMapOf(
"registrationToken" to registrationToken
)
Log.d(TAG, "callUser data input: $data")
return Firebase.functions
.getHttpsCallable("callUser")
.call(data)
.continueWith { task ->
val result = task.result?.data as String
result
}
}
Exception Handling from function
makeCall(registrationToken)
.addOnCompleteListener(OnCompleteListener { task ->
if (!task.isSuccessful) {
val e = task.exception
if (e is FirebaseFunctionsException) {
val code = e.code
val details = e.details
Log.w(TAG, "$code")
}
// [START_EXCLUDE]
Log.w(TAG, "addMessage:onFailure", e)
showSnackbar("An error occurred.")
return#OnCompleteListener
// [END_EXCLUDE]
}
// [START_EXCLUDE]
val result = task.result
Log.d(TAG,"MakeCall result: $result")
val intent = Intent(activity, VideoActivity::class.java)
startActivity(intent)
// [END_EXCLUDE]
})
I have been able to write simple https.onRequest() functions which work as expected but I cannot figure out what I am doing wrong with this callback function.
Logs
W/HomeFragment: NOT_FOUND
W/HomeFragment: addMessage:onFailure
com.google.firebase.functions.FirebaseFunctionsException: NOT_FOUND
at com.google.firebase.functions.FirebaseFunctions$2.onResponse(com.google.firebase:firebase-
functions##19.0.2:281)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:206)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
I am using the Firebase emulator to test, there are no logs from that as the function never gets called successfully.
It is because I was using the Firebase Emulator. Before accessing 'Firebase.functions' you must include 'FirebaseFunctions.getInstance().useFunctionsEmulator("http://10.0.2.2:5001")' if you wish to use the emulator.
See: https://firebase.google.com/docs/emulator-suite/connect_functions#callable_functions
I am trying to create a Slack bot that will add a developer to a release queue using a CloudFlare worker. Example: /releasenext <your name>, Slack bot will add you to the queue and will tell you who else is in that queue.
I would like to use Cloudflare cache to cache names coming from the POST request. So I am using a javascript array. I am trying to cache that array, and anytime somebody is adding their name to the queue, the Slack bot should reveal the persons already in the queue and the new person who added their name.
So far, I managed to create the communication between Cloudflare and Slack via a command on Slack /releasenext <your name>, and Slack answers me with: the next person to release is: <your name>.
I found the documentation and some examples but I am not understanding how it works exactly.
here is the documentation: https://workers.cloudflare.com/docs/reference/workers-concepts/using-cache/
And here is my code:
const SLACK_TOKEN = "secret"
const BOT_NAME = "Deploy-bot š¤"
let jsonHeaders = new Headers([["Content-Type", "application/json"]])
let resp
addEventListener('fetch', event => {
event.respondWith(slackWebhookHandler(event.request))
resp = fetch(event.request, { cf: {cacheEverything: true} })
resp = fetch(event.request, { cf: { cacheTtl: 300 } })
})
// simpleResponse generates a simple JSON response
// with the given status code and message.
function simpleResponse(statusCode, message) {
let resp = {
message: message,
status: statusCode
}
return new Response(JSON.stringify(resp), {
headers: jsonHeaders,
status: statusCode
})
}
// slackResponse builds a message for Slack with the given text
// #param {string} text - the message text to return
function slackResponse(text) {
let content = {
response_type: "in_channel",
text: text
}
try {
return new Response(JSON.stringify(content), {
headers: jsonHeaders,
status: 200
})
} catch (e) {
return simpleResponse(
200,
"Sorry, I had an issue generating a response. Try again in a bit!"
)
}
}
// parseMessage parses the selected name from the Slack message.
// #return {string} - the name.
function parseMessage(message) {
try {
const name = message.get("text").trim()
return {name: name}
} catch (e) {
return null
}
}
async function addPersonToQueue(name) {
try {
let cachedResponse = false
if (resp.headers.get("cf-cache-status").toLowerCase() === "hit") {
cachedResponse = true
}
const nameQueue = []
nameQueue.push({name: name}, {cached: cachedResponse})
let output = nameQueue.map(item => {return item.name}).join(', ')
return output
} catch (e) {
throw new Error(`could not fetch the selected name: ${e}`)
}
}
// slackWebhookHandler handles an incoming Slack webhook and generates a response.
// #param {Request} request
async function slackWebhookHandler(request) {
if (request.method !== "POST") {
return simpleResponse(
200,
`Hi, I'm ${BOT_NAME}, a Slack bot for fetching the latest person name to release`
)
}
let formData
try {
formData = await request.formData()
if (formData.get("token").toString() !== SLACK_TOKEN) {
return simpleResponse(403, "Invalid Slack verification token")
}
} catch (e) {
return simpleResponse(400, "could not decode POST form data")
}
try {
let parsed = parseMessage(formData)
if (parsed === null) {
throw new Error("could not parse your message")
}
let reply = await addPersonToQueue(parsed.name)
return slackResponse(
`The next person to release is: *${reply}*`,
`the cache is: ${reply.cached}`
)
} catch (e) {
return simpleResponse(
200,
`Sorry, I had an issue retrieving names from the release queue ${e}`
)
}
}
The error message I have now is
{"message":"Sorry, I had an issue retrieving names from the release queue Error: could not fetch the selected name: TypeError: Cannot read property 'get' of undefined","status":200}
The expected output is: The next person to release is: person1, <your name>.
Assuming that another person already added their name to the queue first.
I submitted a transaction to Hyperledger Fabric, but I'd like to get object created by this.
The object that I get from this is Undefined.
Obs: The transaction is successfully created in Hyperledger Fabric.
async submit(resource, method) {
try{
this.businessNetworkDefinition = await this.bizNetworkConnection.connect(cardname);
if (!this.businessNetworkDefinition) {
console.log("Error in network connection");
throw "Error in network connection";
}
let factory = this.businessNetworkDefinition.getFactory();
let transaction = factory.newTransaction(NS, method);
Object.assign(transaction, resource)
return await this.bizNetworkConnection.submitTransaction(transaction);
}catch(error){
console.log(error);
throw error;
}
}
Currently the submitTransaction function is not returning anything. It is a bug or working as intended.
To go into more detail: When you delve through the source code of the composer you will finally get to the following code in composer-connector-hlfv1.
invokeChainCode(securityContext, functionName, args, options) {
const method = 'invokeChainCode';
LOG.entry(method, securityContext, functionName, args, options);
if (!this.businessNetworkIdentifier) {
return Promise.reject(new Error('No business network has been specified for this connection'));
}
// Check that a valid security context has been specified.
HLFUtil.securityCheck(securityContext);
// Validate all the arguments.
if (!functionName) {
return Promise.reject(new Error('functionName not specified'));
} else if (!Array.isArray(args)) {
return Promise.reject(new Error('args not specified'));
}
try {
args.forEach((arg) => {
if (typeof arg !== 'string') {
throw new Error('invalid arg specified: ' + arg);
}
});
} catch(error) {
return Promise.reject(error);
}
let txId = this._validateTxId(options);
let eventHandler;
// initialize the channel if it hasn't been initialized already otherwise verification will fail.
LOG.debug(method, 'loading channel configuration');
return this._initializeChannel()
.then(() => {
// check the event hubs and reconnect if possible. Do it here as the connection attempts are asynchronous
this._checkEventhubs();
// Submit the transaction to the endorsers.
const request = {
chaincodeId: this.businessNetworkIdentifier,
txId: txId,
fcn: functionName,
args: args
};
return this.channel.sendTransactionProposal(request); // node sdk will target all peers on the channel that are endorsingPeer
})
.then((results) => {
// Validate the endorsement results.
LOG.debug(method, `Received ${results.length} result(s) from invoking the composer runtime chaincode`, results);
const proposalResponses = results[0];
let {validResponses} = this._validatePeerResponses(proposalResponses, true);
// Submit the endorsed transaction to the primary orderers.
const proposal = results[1];
const header = results[2];
// check that we have a Chaincode listener setup and ready.
this._checkCCListener();
eventHandler = HLFConnection.createTxEventHandler(this.eventHubs, txId.getTransactionID(), this.commitTimeout);
eventHandler.startListening();
return this.channel.sendTransaction({
proposalResponses: validResponses,
proposal: proposal,
header: header
});
})
.then((response) => {
// If the transaction was successful, wait for it to be committed.
LOG.debug(method, 'Received response from orderer', response);
if (response.status !== 'SUCCESS') {
eventHandler.cancelListening();
throw new Error(`Failed to send peer responses for transaction '${txId.getTransactionID()}' to orderer. Response status '${response.status}'`);
}
return eventHandler.waitForEvents();
})
.then(() => {
LOG.exit(method);
})
.catch((error) => {
const newError = new Error('Error trying invoke business network. ' + error);
LOG.error(method, newError);
throw newError;
});
}
As you can see at the end, all that is happening is waiting for Events and Log.exit which return nothing. So currently you have to get your transaction result in another way.
The only way I could get something from my chaincode is through events. There's native interface that might be able to query for transaction data or something like this, but i haven't looked into it yet.
I'm using zardak:soap package in Meteor to connect with Magento SOAP v2 API. I've created a file inside the 'server' folder where I create a soap connection on Meteor.startup. Then I run a ticker that invokes random soap method every 30sec just to keep the connection up.
let soapConnection;
Meteor.startup(() => {
soapConnection = createAPIConnection('http://magento.site.com/api/v2_soap/?wsdl=1', {username: 'user', apiKey: 'password'});
});
function createAPIConnection(url, credentials) {
try {
let client = Soap.createClient(url);
let loginResult = client.login(credentials);
let sessionId = loginResult.loginReturn.$value;
return {
conn: client,
sessionId: sessionId
};
} catch (e) {
if (e.error === 'soap-creation') {
console.log('SOAP Client creation failed');
}
return null;
}
}
function tick() {
try {
soapConnection.conn.catalogCategoryInfo({
sessionId: soapConnection.sessionId,
categoryId: 1
}, (err, result) => { });
} catch (e) { }
}
Then I have a Meteor method that is called from the client. When it is called, the soap method call fails and I'm getting a 'soap error' message in console.
Meteor.methods({
'createMagentoCustomer'(customer) {
try {
soapConnection.conn.customerCustomerCreate({
sessionId: soapConnection.sessionId,
customerData: customer
}, (err, res) => {
if (err)
console.log('soap error');
else
console.log(res);
});
} catch (e) {
console.log('SOAP Method <customerCustomerCreate> call failed');
}
},
});
So, the ticker works well with no problems, but when I try to call soap via Meteor method, it fails. Notice that the soapConnection method is not null and I do receive error in the soap method callback.
Any suggestions?
Meteor version 1.3.4.1