Delay using Promise.all() in a for - promise

I need to add a delay when executing each promises in Promise.all().
My code :
for (let i = 0; i < stockDiff; i++) {
var invoiceObject = {
sellProducts: [{
quantity: 1,
description: '<p style="margin: 0;">' + findedProduct.name + '</p>',
digital_access: findedProduct.digital_access,
forced_ttc_price: parseFloat(findedProduct.ttc_price, 10),
offer_amount: null
}],
totalHt: findedProduct.ht_price,
totalTtc: findedProduct.ttc_price
};
promises.push(InvoiceController.createInvoice('À la commande', null, null, 2, null, null, null, null, null, null, invoiceObject.sellProducts));
}
Promise.all(promises).then(function() {
request.flash('success', 'Stock mis à jour !');
response.redirect('/product/_index');
});
My function InvoiceController.createInvoice() returns a promise :
function createInvoice(rule, publicNote, privateNote, state, customerId, userId, address, complementaryAddress, postalCode, city, products, services, productPackages) {
var productsPromise, servicesPromise, productPackagesPromise,
promises = [];
return InvoiceModel.create({
date: new Date(),
rule: rule,
public_note: publicNote,
private_note: privateNote,
state: 1,
address: address,
complementary_address: complementaryAddress,
postal_code: postalCode,
city: city,
customerId: customerId,
userId: userId
}).then(function(createdInvoice) {
if (products) {
productsPromise = _.map(products, function(product) {
return InvoiceProductModel.create({
invoiceId: createdInvoice.id,
quantity: product.quantity,
description: product.description,
forced_ttc_price: product.forced_ttc_price,
offer_amount: product.offer_amount
});
});
}
if (services) {
servicesPromise = _.map(services, function(service) {
return InvoiceServiceModel.create({
invoiceId: createdInvoice.id,
quantity: service.quantity,
description: service.description,
forced_ttc_price: service.forced_ttc_price,
offer_amount: service.offer_amount,
start: service.start,
end: service.end
});
});
}
if (productPackages) {
productPackagesPromise = _.map(productPackages, function(productPackage) {
return InvoiceServiceModel.create({
invoiceId: createdInvoice.id,
description: productPackage.description,
forced_ttc_price: productPackage.forced_ttc_price,
offer_amount: productPackage.offer_amount,
start: productPackage.start,
end: productPackage.end
});
});
}
promises = _.flattenDeep(productsPromise.concat(servicesPromise).concat(productPackagesPromise));
return Promise.all(promises).then(function() {
return createdInvoice.update({
state: state
}).then(function(updatedInvoice) {
return Promise.resolve(updatedInvoice);
});
});
});
}

Related

Unable to store timestamp in Dynamo DB using Nodejs 16 version Lambda

I am unable to store timestamp in Dynamo DB using Node JS Lambda. i am new to Node JS.
while run the test case, it was stored. but while trigger this lambda function getting error.
i was try to insert below timestamp
LastUpdateTimestamp
The date and time this contact was last updated, in UTC time.
Type: String (yyyy-mm-ddThh:mm:ssZ)
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
const AWS = require('aws-sdk');
const ddb = new AWS.DynamoDB();
exports.handler = async (event) => {
var surveyResults = {};
var data = event.Details.ContactData.Attributes; // extract the contact attributes from the Amazon Connect event
Object.keys(data).forEach(element => {
if (element.startsWith("survey_result_")) {
surveyResults[element] = { S: data[element] };
}
});
var params = {
TableName: process.env.TABLE,
Item: {
contactId: { S: event.Details.ContactData.ContactId},
date: { S: event.Details.ContactData.LastUpdateTimestamp},
surveyId: { S: event.Details.ContactData.Attributes.surveyId },
...surveyResults
}
}
try {
await ddb.putItem(params).promise(); // write the object to the DynamoDB table
} catch (err) {
console.log(err);
}
const response = {
statusCode: 200,
body: JSON.stringify('OK'),
};
return response;
};
Test Data:
{
"Name": "ContactFlowEvent",
"Details": {
"ContactData": {
"Attributes": {
"surveyId": "123456",
"survey_result_1": "4",
"survey_result_2": "5"
},
"Channel": "VOICE",
"ContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe",
"LastUpdateTimestamp": "2023-02-14T06:52:29Z",
"CustomerEndpoint": {
"Address": "+11234567890",
"Type": "TELEPHONE_NUMBER"
},
"InitialContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe",
"InitiationMethod": "API",
"InstanceARN": "arn:aws:connect:us-east-1:123456789012:instance/9308c2a1-9bc6-4cea-8290-6c0b4a6d38fa",
"MediaStreams": {
"Customer": {
"Audio": {
"StartFragmentNumber": "91343852333181432392682062622220590765191907586",
"StartTimestamp": "1565781909613",
"StreamARN": "arn:aws:kinesisvideo:us-east-1:123456789012:stream/connect-contact-a3d73b84-ce0e-479a-a9dc-5637c9d30ac9/1565272947806"
}
}
},
"PreviousContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe",
"Queue": null,
"SystemEndpoint": {
"Address": "+11234567890",
"Type": "TELEPHONE_NUMBER"
}
},
"Parameters": {}
}
}
Error:
2023-02-15T14:10:26.426Z 94fad25e-bcdb-443b-95c0-47640bfaba34 INFO ValidationException: Supplied AttributeValue is empty, must contain exactly one of the supported datatypes
at Request.extractError (/var/runtime/node_modules/aws-sdk/lib/protocol/json.js:52:27)
at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
at Request.emit (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
at Request.emit (/var/runtime/node_modules/aws-sdk/lib/request.js:686:14)
at Request.transition (/var/runtime/node_modules/aws-sdk/lib/request.js:22:10)
at AcceptorStateMachine.runTo (/var/runtime/node_modules/aws-sdk/lib/state_machine.js:14:12)
at /var/runtime/node_modules/aws-sdk/lib/state_machine.js:26:10
at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:38:9)
at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:688:12)
at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:116:18) {
code: 'ValidationException',
time: 2023-02-15T14:10:26.328Z,
requestId: 'F5JJOCF20D507JCFRO7FEMAH6VVV4KQNSO5AEMVJF66Q9ASUAAJG',
statusCode: 400,
retryable: false,
retryDelay: 15.458319111471575
}
You state that ...surveyResults contains the following:
"survey_result_1": "4",
"survey_result_2": "5"
This is not DynamoDB JSON. You need to modify the values to suit the correct format as you have done with the other values.
const AWS = require('aws-sdk');
const ddb = new AWS.DynamoDB();
exports.handler = async (event) => {
var surveyResults = {};
var data = event.Details.ContactData.Attributes; // extract the contact attributes from the Amazon Connect event
Object.keys(data).forEach(element => {
if (element.startsWith("survey_result_")) {
surveyResults[element] = { S: data[element] };
}
});
var contact_params = {
ContactId: event.Details.ContactData.InitialContactId, /* required */
InstanceId: 'place your instance id' /* required */
};
var connect = new AWS.Connect();
console.log('describeContact');
/*connect.describeContact(contact_params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});*/
const attributes = await connect.describeContact(contact_params).promise();
console.log(attributes)
console.log(typeof attributes.Contact.LastUpdateTimestamp)
var agent_params = {
InstanceId: 'place your instance id', /* required */
UserId: attributes.Contact.AgentInfo.Id /* required */
};
const agent = await connect.describeUser(agent_params).promise();
let agent_username = "";
if(agent_username != null){
agent_username = agent.User.Username;
}
console.log(agent)
var params = {
TableName: process.env.TABLE,
Item: {
contactId: { S: event.Details.ContactData.ContactId},
surveyId: { S: event.Details.ContactData.Attributes.surveyId },
date: { S: attributes.Contact.LastUpdateTimestamp.toLocaleDateString()},
time: { S: attributes.Contact.LastUpdateTimestamp.toLocaleTimeString()},
agentname: {S: agent_username},
...surveyResults
}
}
try {
await ddb.putItem(params).promise(); // write the object to the DynamoDB table
} catch (err) {
console.log(err);
}
const response = {
statusCode: 200,
body: JSON.stringify('OK'),
};
return response;
};

Add computed field to graphql results within Strapi 4

I'm using Strapi 4, and I try to add computed field to my custom resolver. (I'm not a graphql expert). I've followed this tutorial to do it.
https://www.theitsolutions.io/blog/how-to-add-custom-graphql-query-to-strapi-v4
I’m also using the “toEntityResponseCollection” methods to send the datas and display it in graphql playground.
But, when I send it back, I get a null result.
Here is my custom resolver
"use strict";
module.exports =
(strapi, toEntityResponseCollection, toEntityResponse) =>
({ nexus }) => ({
typeDefs: `
type PopularityResponse {
id: ImpressionEntityResponseCollection
startDate: String
endDate: String
branding: String
}
extend type Query {
popularity(id: ID!, startDate: String, endDate: String, branding: String): PopularityResponse
}
`,
resolvers: {
Query: {
popularity: {
resolve: async (parent, args, context) => ({
id: args.id,
startDate: args.startDate,
endDate: args.endDate,
branding: args.branding,
}),
},
},
PopularityResponse: {
id: {
resolve: async (parent, args) => {
let query = {
value: await strapi.entityService.findMany(
"api::impression.impression",
{
filters: {
googleid: {
id: {
$eq: parent.id,
},
},
date_debut: {
$gte: parent.startDate,
},
date_fin: {
$lte: parent.endDate,
},
},
},
args
),
};
console.log(query.value);
console.log(parent);
let aggregate = query.value.reduce(
(acc, key) => {
// vérifie si la campagne est dans la liste
if (
[parent.branding].some((elem) => {
let reg = new RegExp(elem);
return reg.test(key.campaignName);
})
) {
let brandingIndex = acc.branding.findIndex(
(el) => el.date_debut == key.date_debut
);
if (brandingIndex !== -1) {
// si on a un élément
acc.branding[brandingIndex].search_impression_share +=
parseInt(key.search_impression_share);
} else {
acc.branding.push({
search_impression_share: parseInt(
key.search_impression_share
),
date_debut: key.date_debut,
});
}
} else {
let nobrandingIndex = acc.nobranding.findIndex(
(el) => el.date_debut == key.date_debut
);
if (nobrandingIndex !== -1) {
// si on a un élément
acc.nobranding[nobrandingIndex].search_impression_share +=
parseInt(key.search_impression_share);
} else {
acc.nobranding.push({
search_impression_share: parseInt(
key.search_impression_share
),
date_debut: key.date_debut,
});
}
}
return acc;
},
{ branding: [], nobranding: [] }
);
console.log("==========>>>>",aggregate);
let y = [query.value[0]];
return toEntityResponseCollection([aggregate]);
},
},
},
},
resolversConfig: {
"Query.popularity": {
auth: {
scope: [
"api::impression.impression.findOne",
"api::impression.impression.find",
],
},
},
},
});
Here is my graphql query
query GetPopularity {
popularity(id: "37", startDate:"2022-06-13",endDate:"2022-07-15",branding:"brand") {
myData {
data {
attributes {
googleid {
data {
attributes {
g_customer_id
}
}
}
}
}
}
}
}
When I log the result ssr, I get my computed datas, but when I look at grapql Playground, I get null.
{
"data": {
"popularity": {
"id": {
"data": [
{
"attributes": {
"search_impression_share": null,
"search_top_impression_share": null
}
}
]
}
}
}
}
I don't know what to do to make it work.
I do it like this, because I need to fetch a huge amount of datas. I know that strapi has a 100 limit result from graphql. Even if I can manualy increase it in the config file, I understand it's not a good practice.
If you have any idea how to solve this, please let me know.
Thanks
Fabien
I found how to solve my issue.
I’ve created a specific type which aggregate the datas.
Now I’m able to fetch my computed elements.
"use strict";
module.exports =
(strapi, toEntityResponseCollection, toEntityResponse) =>
({ nexus }) => ({
typeDefs: `
type PopularityResponse {
id: ImpressionEntityResponseCollection
startDate: String
endDate: String
branding: String
aggregated: aggregateInput
}
type aggregateInput {
brand: [singleAggregate]
nobrand: [singleAggregate]
}
type singleAggregate {
date_debut: String
search_impression_share: Int
}
extend type Query {
popularity(id: ID!, startDate: String, endDate: String, branding: String): PopularityResponse
}
`,
resolvers: {
Query: {
popularity: {
resolve: async (parent, args, context) => ({
id: args.id,
startDate: args.startDate,
endDate: args.endDate,
branding: args.branding,
}),
},
},
PopularityResponse: {
aggregated: {
resolve: async (parent, args, ctx) => {
let compile = await strapi.entityService.findMany(
"api::impression.impression",
{
filters: {
googleid: {
id: {
$eq: parent.id,
},
},
date_debut: {
$gte: parent.startDate,
},
date_fin: {
$lte: parent.endDate,
},
},
},
args
);
// console.log(compile);
let aggregate = compile.reduce(
(acc, key) => {
// vérifie si la campagne est dans la liste
if (
[parent.branding].some((elem) => {
let reg = new RegExp(elem);
return reg.test(key.campaignName);
})
) {
let brandingIndex = acc.branding.findIndex(
(el) => el.date_debut == key.date_debut
);
if (brandingIndex !== -1) {
// si on a un élément
acc.branding[brandingIndex].search_impression_share +=
parseInt(key.search_impression_share);
} else {
acc.branding.push({
search_impression_share: parseInt(
key.search_impression_share
),
date_debut: key.date_debut,
});
}
} else {
let nobrandingIndex = acc.nobranding.findIndex(
(el) => el.date_debut == key.date_debut
);
if (nobrandingIndex !== -1) {
// si on a un élément
acc.nobranding[nobrandingIndex].search_impression_share +=
parseInt(key.search_impression_share);
} else {
acc.nobranding.push({
search_impression_share: parseInt(
key.search_impression_share
),
date_debut: key.date_debut,
});
}
}
return acc;
},
{ branding: [], nobranding: [] }
);
return {
brand: aggregate.branding,
nobrand: () => {
return aggregate.nobranding;
},
};
},
},
},
},
resolversConfig: {
"Query.popularity": {
auth: {
scope: [
"api::impression.impression.findOne",
"api::impression.impression.find",
],
},
},
},
});
````

How can I use "count" and "group by" in Prisma 2?

I have this function which works:
export const tagsByLabel = async (params) => {
const findManyParams = {
where: { userId: userIdFromSession },
orderBy: { title: "asc" },
};
if (params) {
const { searchTerm } = params;
findManyParams.where.title = { contains: searchTerm };
}
console.log("findManyParams", findManyParams);
const tagsByLabelResult = await db.tag.findMany(findManyParams);
console.log("tagsByLabelResult", tagsByLabelResult);
return tagsByLabelResult;
};
If I search for 'mex', I see:
findManyParams {
where: { userId: 1, title: { contains: 'mex' } },
orderBy: { title: 'asc' }
}
tagsByLabelResult [
{
id: 9,
title: 'mex',
description: 'Mexican food',
userId: 1,
createdAt: 2020-05-03T22:16:09.134Z,
modifiedAt: 2020-05-03T22:16:09.134Z
}
]
And for an empty query, tagsByLabelResult contains all tag records.
How can I adjust my tagsByLabel function to aggregate (using "group by") the records and output a "count" for each record of tagsByLabelResult in order by count descending?
tagsByLabelResult [
{
id: 9,
title: 'mex',
description: 'Mexican food',
count: 25,
userId: 1,
createdAt: 2020-05-03T22:16:09.134Z,
modifiedAt: 2020-05-03T22:16:09.134Z
}
]
I see the docs example of prisma.user.count(), but that seems to retrieve a simple count of the result of the whole query rather than a count as a field with a "group by".
I'm using RedwoodJs, Prisma 2, Apollo, GraphQL.
As of now groupBy support is still in spec here so currently you would only be able to use count with specific querying.
As a workaround, you would have to use prisma.raw for the timebeing.
In my tags.sdl.js I needed to add:
type TagCount {
id: Int!
title: String!
count: Int!
principles: [Principle]
description: String
createdAt: DateTime!
modifiedAt: DateTime!
}
And change query tagsByLabel(searchTerm: String): [Tag!]! to tagsByLabel(searchTerm: String): [TagCount!]!
In my TagsAutocomplete.js component, I now have:
export const TagsAutocomplete = ({ onChange, selectedOptions, closeMenuOnSelect }) => {
const state = {
isLoading: false,
};
const client = useApolloClient();
const promiseOptions = useCallback(
async (searchTerm) => {
try {
const { data } = await client.query({
query: QUERY_TAGS_BY_LABEL,
variables: { searchTerm },
});
console.log("promiseOptions data", data);
const tags = data.tags.map((tag) => {
if (!tag.label.includes("(")) {
//ONEDAY why does the count keep getting appended if this condition isn't checked here?
tag.label = tag.label + " (" + tag.count + ")";
}
return tag;
});
console.log("promiseOptions tags", tags);
return tags;
} catch (e) {
console.error("Error fetching tags", e);
}
},
[client]
);
};
And in my tags.js service, I now have:
export const tagsByLabel = async (params) => {
let query = `
SELECT t.*, COUNT(pt.B) as count FROM tag t LEFT JOIN _PrincipleToTag pt ON t.id = pt.B WHERE t.userId = ${userIdFromSession} `;
if (params) {
const { searchTerm } = params;
if (searchTerm) {
query += `AND t.title LIKE '%${searchTerm}%' `;
}
}
query += "GROUP BY t.id ORDER BY count DESC, t.title ASC;";
console.log("query", query);
const tagsByLabelResult = await db.raw(query);
//TODO get secure parameterization working
console.log("tagsByLabelResult", tagsByLabelResult);
return tagsByLabelResult;
};
But, as mentioned in the comment, I'm still trying to figure out how to get secure parameterization working.

How to merge the result of two observations (with different type)

Assume I have these two observations:
const thread = of({
thread: {
name: "Name",
author: null
}
})
const author = of({name:"Snoob"})
How can i get the merged result of these observations:
const threadWithAuthor = .....;
threadWithAuthor.subscribe(it=>console.log(it))
// {
// thread: {
// name: "Name",
// author: { name: "Snoob" }
// }
// }
Here's an example of how you can do it using combineLatest, pipe, and map:
var {of, combineLatest } = require('rxjs')
var { map } = require('rxjs/operators')
var mergeByAuthor = ([t, a]) => {
var x = Object.assign({}, t)
x.thread.author = a
return x
}
var thread = of({
thread: {
name: 'Name',
author: null
}
})
var author = of({name:'Snoob'})
var threadWithAuthor = combineLatest(thread, author).pipe(
map(mergeByAuthor)
)
threadWithAuthor.subscribe(x => console.log(JSON.stringify(x, null, 2)))
Output
{
"thread": {
"name": "Name",
"author": {
"name": "Snoob"
}
}
}

How should I create a Tree structure in Rally of defects with respect to user story

I am able to get tree structure for the user stories but want it same for defects also which are related to particular user story so that at a singe screen I can see both user stories and the related defects.
You may use features: [{ftype:'groupingsummary'}] of ExtJS to group defects by user stories and even summarize by some other field, in the code below by PlanEstimate. To group defects by user story Requirement attribute on defect is used, which points to the related story. In this example defects are filtered by Iteration.
Ext.define('CustomApp', {
extend: 'Rally.app.TimeboxScopedApp',
componentCls: 'app',
scopeType: 'iteration',
comboboxConfig: {
fieldLabel: 'Select Iteration:',
labelWidth: 100
},
onScopeChange: function() {
this.makeStore();
},
makeStore: function() {
var filter = Ext.create('Rally.data.wsapi.Filter', {
property: 'Requirement',
operator: '!=',
value: null
});
filter= filter.and(this.getContext().getTimeboxScope().getQueryFilter());
filter.toString();
Ext.create('Rally.data.wsapi.Store', {
model: 'Defect',
fetch: ['ObjectID', 'FormattedID', 'Name', 'State', 'Requirement', 'PlanEstimate'],
autoLoad: true,
filters: [filter],
listeners: {
load: this.onDataLoaded,
scope: this
}
});
},
onDataLoaded: function(store, records){
if (records.length === 0) {
this.notifyNoDefects();
}
else{
if (this.notifier) {
this.notifier.destroy();
}
var that = this;
var promises = [];
_.each(records, function(defect) {
promises.push(this.getStory(defect, this));
},this);
Deft.Promise.all(promises).then({
success: function(results) {
that.defects = results;
that.makeGrid();
}
});
}
},
getStory: function(defect, scope) {
var deferred = Ext.create('Deft.Deferred');
var that = scope;
var storyOid = defect.get('Requirement').ObjectID;
Rally.data.ModelFactory.getModel({
type: 'HierarchicalRequirement',
scope: this,
success: function(model, operation) {
fetch: ['FormattedID','ScheduleState'],
model.load(storyOid, {
scope: this,
success: function(record, operation) {
var storyScheduleState = record.get('ScheduleState');
var storyFid = record.get('FormattedID');
var defectRef = defect.get('_ref');
var defectOid = defect.get('ObjectID');
var defectFid = defect.get('FormattedID');
var defectPlanEstimate = defect.get('PlanEstimate');
var defectName = defect.get('Name');
var defectState = defect.get('State');
var story = defect.get('Requirement');
result = {
"_ref" : defectRef,
"ObjectID" : defectOid,
"FormattedID" : defectFid,
"Name" : defectName,
"PlanEstimate" : defectPlanEstimate,
"State" : defectState,
"Requirement" : story,
"StoryState" : storyScheduleState,
"StoryID" : storyFid
};
deferred.resolve(result);
}
});
}
});
return deferred;
},
makeGrid: function() {
var that = this;
if (this.grid) {
this.grid.destroy();
}
var gridStore = Ext.create('Rally.data.custom.Store', {
data: that.defects,
groupField: 'StoryID',
pageSize: 1000,
});
this.grid = Ext.create('Rally.ui.grid.Grid', {
itemId: 'defectGrid',
store: gridStore,
features: [{ftype:'groupingsummary'}],
minHeight: 500,
columnCfgs: [
{
text: 'Formatted ID', dataIndex: 'FormattedID', xtype: 'templatecolumn',
tpl: Ext.create('Rally.ui.renderer.template.FormattedIDTemplate')
},
{
text: 'Name', dataIndex: 'Name',
},
{
text: 'State', dataIndex: 'State',
summaryRenderer: function() {
return "PlanEstimate Total";
}
},
{
text: 'PlanEstimate', dataIndex: 'PlanEstimate',
summaryType: 'sum'
},
{
text: 'Story', dataIndex: 'Story',
renderer: function(val, meta, record) {
return '' + record.get('Requirement').FormattedID + '';
}
},
{
text: 'Story Schedule State', dataIndex: 'StoryState',
}
]
});
this.add(this.grid);
this.grid.reconfigure(gridStore);
},
notifyNoDefects: function() {
if (this.grid) {
this.grid.destroy();
}
if (this.notifier) {
this.notifier.destroy();
}
this.notifier = Ext.create('Ext.Container',{
xtype: 'container',
itemId: 'notifyContainer',
html: "No Defects found matching selection."
});
this.add( this.notifier);
}
});

Resources