Amazon Pinpoint Endpoints in putEvents-Method of the JavaScript SDK aren't working - node.js

I've built a AWS Pinpoint integration into my app using API Gateway and Events are properly coming into Pinpoint. However with every new request a new Endpoint is created although I supply the "address"-field.
I went through all the docs provided by AWS:
https://docs.aws.amazon.com/pinpoint/latest/apireference/apps-application-id-events.html
https://docs.aws.amazon.com/pinpoint/latest/developerguide/integrate-events.html
Primarily used this class doc which seems to have some missing info:
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Pinpoint.html
async function putEvent(clientRequest){
/* create the putEvents parameters */
var param = {
ApplicationId: PINPOINT_APP_ID,
EventsRequest: {
BatchItem: { }
}
};
/* create the event parameter */
var eventParam = {
Endpoint: {
Address: clientRequest.deviceId,
ChannelType: 'CUSTOM',
Demographic: {
AppVersion: clientRequest.app.version,
Locale: clientRequest.system.locale,
Make: clientRequest.device.manufacturer,
Model: clientRequest.device.model,
ModelVersion: clientRequest.device.version,
Platform: clientRequest.platform.name,
PlatformVersion: clientRequest.platform.version
}
}
};
/* add the location if its was provided */
if(clientRequest.hasOwnProperty('location')){
/* add the latitude and longitude values */
eventParam.Endpoint['Location'] = {
Latitude: clientRequest.location.latitude,
Longitude: clientRequest.location.longitude
}
/* check if a city and postal code was supplied
alongside the country value */
if(clientRequest.location.hasOwnProperty('cityName') == true
&& clientRequest.location.hasOwnProperty('countryCode') == true
&& clientRequest.location.hasOwnProperty('postalCode') == true){
/* attach to the location param */
eventParam.Endpoint.Location['Country'] = clientRequest.location.countryCode;
eventParam.Endpoint.Location['City'] = clientRequest.location.postalCode;
eventParam.Endpoint.Location['PostalCode'] = clientRequest.location.cityName;
}
}
/* check if the userId was supplied */
if(clientRequest.hasOwnProperty('userId')){
/* attach the hashed and salted user id */
eventParam.Endpoint['User'] = {UserId: getSHA512(clientRequest.userId+USERID_HASHSALT)};
}
/* attach the event values */
eventParam['Events'] = [{
EventType: clientRequest.event.name,
Timestamp: (new Date()).toISOString()
}];
/* create a unique request id */
var requestId = (new Date().getTime()) + Math.floor(Math.random() * 10);
param.EventsRequest.BatchItem[requestId] = eventParam;
/* flush an event to Pinpoint */
await Pinpoint.putEvents(param).promise();
}
After every request I do have a new Pinpoint Endpoint defined, although I provide a unique Address-value for each Endpoint.
a) What do I need to do have the Endpoints unique?
b) How can I report Sign-ins, Sign-out and the other Events?
^ Could not find them in the documentation

Agreed the Pinpoint docs / class document is incomplete leaving out desired information. From my experiencing testing & using the API this is what I have found which hopefully can be of use.
a) What do I need to do have the Endpoints unique?
Pinpoint.putEvents not only creates a new event for an endpoint but it also creates or updates endpoint data
The fact that Pinpoint.putEvents can create or update an endpoint is causing the error you've encountered where a new endpoint is created after every event request.
This is because you are accidentally creating a new endpoint equal to the random requestId for each event that you send when setting the keys of BatchItem. The object keys of BatchItem are actually supposed to be the endpointId the event is supposed to be associated with opposed to the requestId for the event (This is not mentioned at all in the docs!)
To keep endpoints unique you first need to know what the unique endpoint is for the user in addition to the address and unique UserId (This seems to be missing from pinpoint docs. I realized it when trying to update or delete an endpoint which you cannot do by address as you need the endpointId). From your example I would choose something related to the userId concatenated with the channel type if you plan on having multiple channels for a single user as pinpoint does allow messages to be sent through email, sms and recorded voice calls (you've listed "CUSTOM" but I'd try to use one of the enum's that is actually associated with how the message would be delivered. I believe this allows this endpoint to work better with different types of pinpoint campaigns and journeys to send messages to your users)
// Original code adding eventParam to param.EventsRequest.BatchItem incorrectly by random requestId
var requestId = (new Date().getTime()) + Math.floor(Math.random() * 10);
param.EventsRequest.BatchItem[requestId] = eventParam;
// correct code associating eventParam with a unique endpointId
var endpointId = eventParam.Endpoint.User.UserId+'CUSTOM'
param.EventsRequest.BatchItem[endpointId] = eventParam;
Additionally keep in mind that all of the information you have added to eventParam.endpoint will update / overwrite whatever is currently stored for those endpointId attributes when calling Pinpoint.putEvents so watch out for that
b) How can I report Sign-ins, Sign-out and the other Events?
I believe to report sign-ins / sign-outs that are visualized in the pinpoint dashboard follow the event naming convention in the Pinpoint app events documentation
so for sign-ins the event name is _userauth.sign_in
I don't think sign outs are displayed automatically on the Anlaytics -> Usage dashboard but you can use any consistent event name for sign outs and then use pinpoint filters to see those events through time.

Related

How do you use GCP's NodeJS library to deploy new API Gateway OpenAPI Specs?

The problem I'm trying to solve, is this:
I want to be able to deploy a new API Gateway configuration-file, that is the OpenAPI 2.0 (Swagger) specification, which API Gateway uses to set up the endpoints. I want to do this through code and not the CLI because I require multiple members of a team to be able to deploy new revisions at high speed, and preferably automatically whenever, say, pushing to a main-branch.
I've looked at the following documentation so far:
https://github.com/googleapis/nodejs-api-gateway/tree/main/samples/generated/v1
Mainly, all their examples look more or less the same, i.e. as an example - getting an API Config:
import { ApiGatewayServiceClient } from '#google-cloud/api-gateway';
function main() {
// [START apigateway_v1_generated_ApiGatewayService_GetApiConfig_async]
/**
* TODO(developer): Uncomment these variables before running the sample.
*/
/**
* Required. Resource name of the form:
* `projects/* /locations/global/apis/* /configs/*`
*/
const name = ''projects/<my_project_id>/locations/global/apis/<my_API_name>/configs/<API_config_name>''
/**
* Specifies which fields of the API Config are returned in the response.
* Defaults to `BASIC` view.
*/
const view = {}
// Instantiates a client
const apigatewayClient = new ApiGatewayServiceClient();
async function callGetApiConfig() {
// Construct request
const request = {
name,
};
// Run request
const response = await apigatewayClient.getApiConfig(request);
console.log(response);
}
callGetApiConfig();
// [END apigateway_v1_generated_ApiGatewayService_GetApiConfig_async]
}
The result then looks like this:
Now what I don't understand, is why many of the fields of the response are empty (grcpServices, openapiDocuments, managedServiceConfigs), especially openapiDocuments, because I know for a fact that an OpenAPI YAML spec is present on GCP. For context, I was trying to see what the response would look like in order to find a solution to the main problem.
That leads me to my second question. As I mentioned, I'm trying to deploy/add a new OpenAPI spec (since you have to add a new one if you want to add new stuff) with the same library. The example on how to create an API Config, given from the documentation, is the following:
import { ApiGatewayServiceClient } from '#google-cloud/api-gateway';
function main() {
// [START apigateway_v1_generated_ApiGatewayService_CreateApiConfig_async]
/**
* TODO(developer): Uncomment these variables before running the sample.
*/
/**
* Required. Parent resource of the API Config, of the form:
* `projects/* /locations/global/apis/*`
*/
const parent = 'projects/<project_id>/locations/global/apis/<my_API_name>'
/**
* Required. Identifier to assign to the API Config. Must be unique within scope of
* the parent resource.
*/
const apiConfigId = 'unique-api-config-id-123'
/**
* Required. API resource.
*/
const apiConfig = {} // What to put here???
// Instantiates a client
const apigatewayClient = new ApiGatewayServiceClient();
async function callCreateApiConfig() {
// Construct request
const request = {
parent,
apiConfigId,
apiConfig,
};
// Run request
const [operation] = await apigatewayClient.createApiConfig(request);
const [response] = await operation.promise();
console.log(response);
}
callCreateApiConfig();
// [END apigateway_v1_generated_ApiGatewayService_CreateApiConfig_async]
}
Now if you try to run this code out of the box, you get the following error:
Error: 3 INVALID_ARGUMENT: Please specify one of: (service_rollout.rollout_id, openapi_documents, grpc_services)
I understand that the variable apiConfig shouldn't be empty, but I cannot find any living examples of what this is actually supposed to contain. I've dived into the source code of the library to try to find an answer, but to no prevail. Also where exactly to fulfill the requirements of specify one of service_rollout.rollout_id, openapi_documents, grpc_services is completely beyond me.
Any responses and help here would be greatly appreciated.
EDIT:
I found the answer to the first question. I had to edit the request-parameter, namely by the following:
import { google } from '#google-cloud/api-gateway/build/protos/protos';
const request = {name, google.cloud.apigateway.v1.GetApiConfigRequest.ConfigView.FULL}.
The second question remains open, but perhaps this first solution will guide me to the last.
Lo and behold, the answer to the first question lead me to the answer to the second.
The solution was to simply include the following in the request:
const apiConfigId = 'test-api-gateway-new-config-1';
const openApiDocument: google.cloud.apigateway.v1.ApiConfig.IOpenApiDocument = {
document: {
path: 'name_of_file.yaml',
contents: fs.readFileSync('path/to/file/you/want/to/upload'),
}
}
/**
* Required. API resource.
*/
const apiConfig: google.cloud.apigateway.v1.IApiConfig = {
openapiDocuments: [
openApiDocument,
]
}
After running this code, a new OpenAPI spec will have been uploaded to GCP's API Gateway with the name "test-api-gateway-new-config-1".

Lost ability to capture unique Conversation_ID for each new session

Using Bot Builder 4.11.1 .Net and seemed to have lost the ability to capture any unique identifier for each new session. I need a unique identifier to keep state with the AI engine I am using to respond to input. Any suggestions? So, to expand, if I have a slack bot, for example, each time a user logs into Slack and starts a conversation with MyBot, I need a new unique identifier.
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var welcomeText = "Hello and welcome!";
Random rnd1 = new Random();
foreach (var member in membersAdded)
{
if (member.Id != turnContext.Activity.Recipient.Id)
{
await turnContext.SendActivityAsync(MessageFactory.Text(welcomeText, welcomeText), cancellationToken);
}
}
}
}
Unless I'm missing something, you should be able to get the information you need from TurnContext. In my code I'm running this in onMessage only (as in my case I only will message the user if they have sent the bot a message), but I don't see why you couldn't use this in onMembersAdded as well. I don't know how channels like Slack work, but in Microsoft Teams the user is just "added" when they first talk to the bot, and you don't end up in onMembersAdded unless they remove and read the bot in Teams. So if you may want to grab the conversation reference in the future, you may want to have it in onMessage or in both places. Also if you need the activity ID for some reason, as this will obviously update with each activity (though I haven't had any need for this information). Here is how you get the conversation reference. I am storing this in my conversation state and have assumed you are familiar with that but let me know if you need further help there. I also store in an Azure table to be accessed outside of the bot (e.g. I have an Azure Function that uses this to send proactive followups).
const { TurnContext } = require('botbuilder');
const conversationData = await this.dialogState.get(context, {});
conversationData.conversationReference = TurnContext.getConversationReference(context.activity);
await this.conversationState.saveChanges(context);
And that's it! Here is a sample conversation reference. Note that if you are storing this in Azure Tables or similar, you'll likely need to stringify it and re-parse when you pull it out.
{
"activityId":"ACTIVITY_ID",
"user": {
"id":"USER_ID",
"name":"USER_NAME",
"aadObjectId":"AAD_OBJECT_ID",
"role":"user"
},
"bot": {
"id":"BOT_ID",
"name":"BOT_NAME"
},
"conversation": {
"conversationType":"personal",
"tenantId":"YOUR_TENANT_ID",
"id":"YOUR_CONVERSATION_ID"
},
"channelId":"msteams",
"locale":"en-US",
"serviceUrl":"https://smba.trafficmanager.net/amer/"
}
What you are looking for (I think) is conversationReference.conversation.id. Different channels are going to have different attributes in the conversation reference, but this ID should always be there.

Azure Topic Subscription Rule created with Service Bus Explorer not triggering

I am using Service Bus Explorer as a quick way of testing a rule that does not work when deployed via ARM.
In JavaScript in the Azure Function I am setting the Topic message to:
context.bindings.outputSbMsg = { Indicator: 'Itinerary'};
In Service Bus Explorer I am setting a Rule on a Subscription with this string:
Indicator = 'Itinerary'
But messages sent to the Topic do not go to this Subscription ( they go to another with the rule 1 = 1)
Question: What am I missing here?
Supplementary info:
I do not seem to have access to the Indicator property. As a test I created an action on the 1=1 rule that appended to the Indicator property and the result was empty.
I am able to access the Indicator property in JavaScript if I have a Function that is triggered by the 1 = 1 rule, so the property is there.
The rule doesn't work because
The rule works against system or user-defined properties rather than message body.
What js function outputs is merely message body, i.e. context.bindings.outputSbMsg = { Indicator: 'Itinerary'}; sends a message { Indicator: 'Itinerary'} and no property is set by us.
And the default rule with 1=1 true filter enables all messages to be selected into the subscription, so you see messages went there all the time. Check doc of topic filters for more details.
For now, it's by design that js function output can't populate message properties. To make the filter work, we have to send messages with property using SDK instead. Install azure-sb package then try sample code below.
const azuresb = require("azure-sb");
const connStr = "ServiceBusConnectionString";
const mytopic = "mytopic";
var serviceBus = azuresb.createServiceBusService(connStr);
const msg =
{
body: "Testing",
customProperties: {
Indicator: 'Itinerary'
}
};
serviceBus.sendTopicMessage(mytopic, msg, function(error) {
if (error) {
context.log(error);
}
else{
context.log("Message Sent");
}
});

Guidewire- unable to save details to entity

i am trying to save some details in policy center. i am getting those details from billing center and immediately trying to save it but it is giving me exception.
i have called "issuePolicyPeriod" function of billingAPI and returned some values and i need to save this in Policy center.
Exception:
Exception came while saving commision detais: java.lang.RuntimeException: com.guidewire.pl.system.exception.TransactionException: commitBundle must not be called in a transaction.
Code
/**
* Issue a policy period in Billing Center
* #param period: the policy period
* #param transactionID: the unique transaction id to make this call idempotent
*/
override function createPolicyPeriod(period: PolicyPeriod, transactionID : String) : String {
var issuePolicyInfo = new IssuePolicyInfo()
issuePolicyInfo.sync(period)
PCLoggerCategory.BILLING_SYSTEM_PLUGIN.info("Sending policy ${period} to Billing System")
var commissionList = callUpdate(\ b -> b.issuePolicyPeriod(issuePolicyInfo, transactionID))
gw.transaction.Transaction.runWithNewBundle(\ bundle -> {
var commission = new CommissionDetails()
bundle.add(commission)
commission.Commission_SI = new BigDecimal( commissionList.get(1))
commission.CommissionGST_SI = new BigDecimal(commissionList.get(2))
commission.CommissionRate_SI = commissionList.get(3).toString()
})
return commissionList.get(0)
}
I am calling issuePolicyPeriod() function from PC using BillingAPI and returning commission details from BC and trying to save it in PC entity immediately.
Hi,
thank you for your answer. I tried the above code but it is not saving to entity and nor giving any exception. I have a doubt here will current bundle still available? because here we are calling billing center and don't know when BC responds.
in web service calls will guidewire hold the current bundle until response comes back?
When guidewire commits the current bundle in web service calls?
Try to use the current transaction instead of create new.
var commissionList = callUpdate(\ b -> b.issuePolicyPeriod(issuePolicyInfo, transactionID))
var bundle = gw.transaction.Transaction.Current
var commission = new CommissionDetails(bundle)
commission.Commission_SI = new BigDecimal( commissionList.get(1))
commission.CommissionGST_SI = new BigDecimal(commissionList.get(2))
commission.CommissionRate_SI = commissionList.get(3).toString()
return commissionList.get(0)
When you use runWithNewBundle, after block execution ends bundle.commit() it's called
I had a similar error with a bundle (java.lang.RuntimeException: com.guidewire.pl.system.exception.TransactionException: commitBundle must not be called in a transaction)
and I could be solved with this steps
quit or comment this: gw.transaction.Transaction.runWithNewBundle(\ bundle -> {})
The problem was that I had an existing bundle, so I only needed to call it with getCurrent()
var bundle = gw.transaction.Transaction.getCurrent()

Pubnub - removing a message in history (storage)

I'm working in a instant messaging using pubnub as backend on iOS. After some googling, I found this way but for scala.
http://scalabl3.github.io/pubnub-design-patterns/2015/02/26/Message-Update-Delete.html
I wonder if there is a exist way in API to archive this?
PubNub allows delete messages from the channel
eg:
let startDate = NSNumber(value: (15101397027611671 as CUnsignedLongLong))
let endDate = NSNumber(value: (15101397427611671 as CUnsignedLongLong))
self.client.deleteMessage().channel("channel").start( startDate ).end(
endDate).performWithCompletion ( { (status) in
if !status.isError {
// Messages within specified time frame has been removed.
} else {
/**
* Handle message history download error. Check 'category' property to find out possible
* issue because of which request did fail.
*
* Request can be resent using: status.retry()
*/
}
})
Refer pubnub documentation for more details
Pubnub does not currently support deleting things from message history.

Resources